# Remove useless whitespace from styled string

I have a big string resource (basically the "about" text of the application) which contains styles (such as <b>, <a> etc.). The string resource is written on multiple lines, like this:

<string name="about_text"><big><b>About</b></big>\n
Lorem ipsum dolor sit amet and all that stuff...\n
\n
More stuff.</string>


Now, just like in HTML, Android treats actual new lines (not the special \n) as spaces, so the text ends up looking something like this:

About
Lorem ipsum dolor sit
amet and all that
stuff...

More stuff.


Which looks pretty stupid. Now, I have two options:

1. Write the whole thing on one line. I did not go with this because:
1. It would be a pretty big line.
2. The text needs to be translated, and putting it all on one line would give the translators a great headache.
2. Remove the unneeded whitespace programatically.

I went with the second solution, but I'm not sure my implementation is optimal:

CharSequence aboutText = getText(R.string.about_text);

for (int i=0; i < ssb.length()-1; i++) {
if (ssb.charAt(i) == '\n' && ssb.charAt(i+1) == ' ') {
ssb.replace(i+1, i+2, "");
}
}



It seems very hackish, but I could not find a better way. Is there a better way?

• How about assigning a SingleLineTransformationMethod to the TextView? Jun 24, 2011 at 14:17
• That just makes the entire text single line. I still want new lines, I just don't want spaces and beginning of lines caused by TextView interpreting actual new lines as spaces (like in HTML). I basically want the equivalent of white-space: pre. Jun 24, 2011 at 15:54

The answer is to put the newlines at the start of each line, directly before the text.

<string name="about_text"><big><b>About</b></big>
\nLorem ipsum dolor sit amet and all that stuff...
\n\nMore stuff.
</string>


Therefore all the redundant whitespace appears at the end of each line (e.g. after "About") where it doesn't affect the appearance of your text.

• Man, you're a genius Apr 15, 2019 at 7:24

Since this is XML, you can use the method of ignoring whitespace using comments:

<string name="about_text"><big><b>About</b></big>\n<!--
-->Lorem ipsum dolor sit amet and all that stuff...\n<!--
-->\n<!--
-->More stuff.</string>


It may be ugly, but this technique has the advantage that the actual text content of your XML is the exact string you wanted it to be; there are no workarounds in code and you are not depending on any quirks.

This should work:

String aboutText = getText(R.string.about_text).toString();



toString() is there to convert to a String object.

• A CharSequence is not just a String in disguise. The CharSequence returned by getText() is actually an instance of SpannableString. Thus, when calling toString() all the formatting is lost, which is not the desired effect. Jun 24, 2011 at 14:03
• I successfully tested it on my emulator, and I also checked the class and it was a String. I wonder if we have different API versions? Jun 26, 2011 at 1:49
• Did it preserve formatting? Seems kind of strange. What's your target API version? Jun 27, 2011 at 9:52
• The Resources are clever enough not to construct a SpannableString if it's really just plain text. For more follow getText()->getResourceText()->StringBlock.get() and notice res = str and res = applyStyles(str, ... where applyStyles shortcuts to str as well if there are no styles. Note that the links are Gingerbread. May 25, 2015 at 15:57

I'm not sure string.xml is such a good choice or long strings.

For the about dialog, I chose to have HTML page in assets and use a WebView (use strings.xml to store the name of the html file for the locale)

This is more flexible and feature rich.

• or use res/raw so translation is automatic May 25, 2015 at 0:41

I think your use of SpannableStringBuilder makes sense. You could adopt Character.isSpaceChar() or Character.isWhitespace() if you want to easily trim additional whitespace characters.

I realize it doesn't directly address your question, but I wrote a similar function to remove trailing whitespace from a CharSequence this afternoon:

public static CharSequence trimTrailingWhitespace(CharSequence source) {

if(source == null)
return "";

int i = source.length();

// loop back to the first non-whitespace character
while(--i >= 0 && Character.isWhitespace(source.charAt(i))) {
}

return source.subSequence(0, i+1);
}


Edit: I ran into a similar problem this morning where I needed to remove internal blank lines, so I took a stab at writing a method that:

• minimizes the number of calls to replace(), and
• handles other forms of whitespace such as linefeeds, tabs, etc.

Again, this doesn't exactly solve your problem (and as the other answers have pointed out, there are ways to work around your issue in the source), but an approach similar to this would be suitable for strings you didn't author, e.g. downloaded strings.

public static CharSequence removeExcessBlankLines(CharSequence source) {

if(source == null)
return "";

int newlineStart = -1;
int nbspStart = -1;
int consecutiveNewlines = 0;
SpannableStringBuilder ssb = new SpannableStringBuilder(source);
for(int i = 0; i < ssb.length(); ++i) {
final char c = ssb.charAt(i);
if(c == '\n') {
if(consecutiveNewlines == 0)
newlineStart = i;

++consecutiveNewlines;
nbspStart = -1;
}
else if(c == '\u00A0') {
if(nbspStart == -1)
nbspStart = i;
}
else if(consecutiveNewlines > 0) {

// note: also removes lines containing only whitespace,
//       or nbsp; except at the beginning of a line
if( !Character.isWhitespace(c) && c != '\u00A0') {

// we've reached the end
if(consecutiveNewlines > 2) {
// replace the many with the two
ssb.replace(newlineStart, nbspStart > newlineStart ? nbspStart : i, "\n\n");
i -= i - newlineStart;
}

consecutiveNewlines = 0;
nbspStart = -1;
}
}
}

return ssb;
}