简体   繁体   中英

HTML.fromHtml adds space at end of text?

In my app I use the Html.fromHtml(string).toString method to remove some <p> tags that are received when I parse some JSON.

If I leave the <p> tags on, the text fits the background perfectly (the background is a relative layout with wrap_content in both height and width.) However, if I use fromHtml to remove the <p> tags, suddenely there is a huge space below the text, which I believe is the fromHtml method adding in space at the end?

Any ideas?

EDIT:

Here are screenshots: http://imgur.com/a/zIZNo

The one with <p> tags is the one that doesnt use fromHtml, obviously! :)

EDIT 2: Solution has been found, see my answer below. Thank you to Andro Selva for helping me by telling me about the hidden /n that was being added!

Solution was found:

fromHtml returns the type Spanned. So I assigned what was being returned to a variable, converted it to a string and then used the .trim() method on it.

It removed all white space at the end.

Yes what you thought about is really correct. It adds space to the bottom. But before that let me explain how this works.

You have to look at HTML class to see how it works.

To be simple, this is how it works: whenever your Html class looks at a <p> tag, what it does is simply append two "\\n" chars to the end.

In this case the empty space you see at the bottom is actually because of the two \\n appended to the end of the paragaraph.

And I have added the actual method of the Html class which is responsible for this action,

    private static void handleP(SpannableStringBuilder text) {
    int len = text.length();

    if (len >= 1 && text.charAt(len - 1) == '\n') {
        if (len >= 2 && text.charAt(len - 2) == '\n') {
            return;
        }
        text.append("\n");
        return;
    }

    if (len != 0) {

       text.append("\n\n");

    }
}

If you want to override this action, you have to override the Html class itself which is a bit tricky and can't be completed here.

EDIT

here is the link to the Html class,

Html class

如果您尝试在对象中使用它或尝试将其放置在特定位置,请尝试使用<a>标记而不是<p><p>在末尾添加回车符, a 不写入,但您有记得用<b>自己写\\n ,你就可以保持风格

The explanation by @Andro Selva is correct and there is not much to be done about it. Frustratingly, things get better for API 24 and later with the inclusion of flags in the call

Spanned fromHtml (String source, 
                int flags, 
                Html.ImageGetter imageGetter, 
                Html.TagHandler tagHandler);

and I suspect the FROM_HTML_SEPARATOR_LINE_BREAK_PARAGRAPH flag will reduce the double "\\n\\n" of the standard paragraph termination to that of the single "\\n" of a line break

Given the history of Android versions out there ~I can't afford to write software for Android API 24+ exclusively! So... I found a kludge solution with the inclusion of 2 extra custom tags.

1. <scale factor="x.xx">... </scale>
2. <default>... </default>

both invoke the RelativeSizeSpan class through 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 the custom TagHandler supplied to the Html.fromHtml method, viz:

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! I then set the required true text size required in the variable mDefaultTextSize . I have all the Html functionality inside an htmlTextView which 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 the 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 ((htmlTextView)tv).setDefaultTextMetrics(null, 30); call sets my htmlTextView to use my standard typeface as default with a text size of 30.

Then when I give it this example to 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 tag <box> just lets me highlight the background of the text. See the attached picture, showing one result using the <default> tag with the TextView text size set to 1 and the <default> tag invoking a RelevantSizeSpan by a factor of 30, 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 no <default> tag but setting the TextView text size to 30 instead. In the first case the extra new line is still there but it is only 1 pixel high!

NB There is no real point to the <scale factor="1.5">...</scale> tags. They are just left over artefacts from other tests.

Results: Both examples below have 2 newlines between paragraphs but, in the one on the left, one of those lines is only 1 pixel high. I will leave it to the reader to figure out how to reduce it to zero, but do not use a text size of 0

This solution works for me Create a helper method to replace all paragraph starting and ending tags and replace all with empty characters.

@Nullable
public static String removeParagraphTags(@Nullable String input) {
    if (input == null) {
        return null;
    }
    return input.replaceAll("<p>", "").replaceAll("</p>", "");
}

And Usage

  String input = "<p>This is some text in a paragraph.</p>";
HtmlCompat.fromHtml(StringUtils.removeParagraphTags(input),HtmlCompat.FROM_HTML_MODE_COMPACT)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM