@Andro Selva's explanation is correct, and little can be done about it. Disappointing, everything is getting better for API 24 and later with flags included in the call
Spanned fromHtml (String source, int flags, Html.ImageGetter imageGetter, Html.TagHandler tagHandler);
and I suspect that the FROM_HTML_SEPARATOR_LINE_BREAK_PARAGRAPH flag will reduce the double "\ n \ n" of the standard terminal paragraph to a single "\ n" line break
Given the history of Android versions, I cannot afford to write software for the Android API 24+ exclusively! So ... I found a kludge solution with 2 additional custom tags included.
1. <scale factor="x.xx">... </scale> 2. <default>... </default>
both calls to the RelativeSizeSpan class using this method
private void ProcessRelativeSizeTag(float scalefactor, boolean opening, Editable output) { int len = output.length(); if (opening) { System.out.println("scalefactor open: " + scalefactor); output.setSpan(new RelativeSizeSpan(scalefactor), len, len, Spannable.SPAN_MARK_MARK); } else { Object obj = getLast(output, RelativeSizeSpan.class); int where = output.getSpanStart(obj); scalefactor = ((RelativeSizeSpan)obj).getSizeChange(); output.removeSpan(obj); System.out.println("scalefactor close: " + scalefactor); if (where != len) { output.setSpan(new RelativeSizeSpan(scalefactor), where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } } }
which is called from a custom TagHandler passed to the Html.fromHtml method, namely:
private static class CustomTagHandler implements Html.TagHandler { private void ProcessRelativeSizeTag(float scalefactor, boolean opening, Editable output) { int len = output.length(); if (opening) { //mSizeStack.push(scalefactor); System.out.println("scalefactor open: " + scalefactor); output.setSpan(new RelativeSizeSpan(scalefactor), len, len, Spannable.SPAN_MARK_MARK); } else { Object obj = getLast(output, RelativeSizeSpan.class); int where = output.getSpanStart(obj); scalefactor = ((RelativeSizeSpan)obj).getSizeChange(); output.removeSpan(obj); //scalefactor = (float)mSizeStack.pop(); System.out.println("scalefactor close: " + scalefactor); if (where != len) { output.setSpan(new RelativeSizeSpan(scalefactor), where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } } } ... final HashMap<String, String> mAttributes = new HashMap<>(); @Override public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) { String Attr; processAttributes(xmlReader); if ("default".equalsIgnoreCase(tag)) { ProcessRelativeSizeTag(mDefaultTextSize, opening, output); return; } if ("scale".equalsIgnoreCase(tag)) { Attr = mAttributes.get("factor"); if (Attr != null && !Attr.isEmpty()) { float factor = parseFloat(Attr); if (factor > 0) ProcessRelativeSizeTag(factor, opening, output); } return; ... } }
To use, I set the text size of the Textview object to 1. That is, 1 pixel! Then I set the required true text size required for the mDefaultTextSize variable. I have all the Html functions inside an htmlTextView that extends Textview as:
public class htmlTextView extends AppCompatTextView { static Typeface mLogo; static Typeface mGAMZ; static Typeface mBrush; static Typeface mStandard; int GS_PAINTFLAGS = FILTER_BITMAP_FLAG | ANTI_ALIAS_FLAG | SUBPIXEL_TEXT_FLAG | HINTING_ON; static float mDefaultTextSize; static Typeface mDefaultTypeface; etc
}
which includes a public method
public void setDefaultTextMetrics(String face, float defaultTextSize) { mDefaultTypeface = mStandard; if (face != null) { if ("gamz".equalsIgnoreCase(face)) { mDefaultTypeface = mGAMZ; } else { if ("brush".equalsIgnoreCase(face)) { mDefaultTypeface = mBrush; } } } setTypeface(mDefaultTypeface); setTextSize(1); mDefaultTextSize = defaultTextSize; }
A simple call ((htmlTextView)tv).setDefaultTextMetrics(null, 30); sets my htmlTextView to use my standard default font with a text size of 30.
Then, when I give it this example for use in fromHtml:
<string name="htmlqwert"> <![CDATA[ <p><default><scale factor="1.5"><box> qwertQWERT </box></scale></default></p> <p><default><scale factor="1.5"><box> qwertQWERT </box></scale></default></p> <p><default><scale factor="1.5"><box> qwertQWERT </box></scale></default></p> <p><default><scale factor="1.5"><box> qwertQWERT </box></scale></default></p> ]]> </string>
my custom <box> just allows me to highlight the background of the text. See the Attached Image showing one result with the <default> with the Textview text size set to 1 and the <default> tag calling a RelevantSizeSpan 30 times, and one with:
<string name="htmlqwert"> <![CDATA[ <p><scale factor="1.5"><box> qwertQWERT </box></scale></p> <p><scale factor="1.5"><box>qwertQWERT</box></scale></p> <p><scale factor="1.5"><box>qwertQWERT</box></scale></p> <p><scale factor="1.5"><box>qwertQWERT</box></scale></p> ]]> </string>
using the <default> , but instead the Textview text Textview is 30. In the first case, an additional new line still exists, but it is only 1 pixel!
NB There is no real point for the <scale factor="1.5">...</scale> tags. They just left artifacts from other tests.
Results: Both examples below have two new lines between paragraphs, but in one of them one of these lines has only 1 pixel. I will leave it to the reader to figure out how to reduce it to zero, but not use text size 0 