HTML.fromHtml在文本末尾添加空格?

8
在我的应用中,我使用Html.fromHtml(string).toString方法来删除一些在解析JSON时接收到的< p>标签。如果我保留< p>标签,则文本完全符合背景(背景是相对布局,在高度和宽度方面都使用wrap_content)。但是,如果我使用fromHtml来移除< p>标签,那么突然之间文本下方会有一个巨大的空间,这可能是fromHtml方法在末尾添加了空格造成的?有什么想法吗?编辑:以下是屏幕截图:http://imgur.com/a/zIZNo。带< p>标记的截图显然没有使用fromHtml! :) 编辑2:已找到解决方案,请参见下面的答案。感谢Andro Selva通过告诉我被添加的隐藏/ n来帮助我!

任何与差异相关的屏幕截图都会很有帮助,我想。 - Andro Selva
5个回答

13

找到解决方案:

fromHtml 返回类型为 Spanned。因此,我将返回值分配给一个变量,将其转换为字符串,然后使用.trim()方法。

这会删除末尾的所有空格。


5
这将去除额外的填充,但也会删除可能存在于span中的各种格式(例如粗体、斜体等).. 那么,为什么还要使用html呢? - Jakob Harteg
@Jakob String.Trim()只会移除给定变量开头和结尾的空格,不应该移除其他任何内容。 - wh-dev
2
@CameronW。是的,它确实可以删除空格,但除非您像您建议的那样将其转换为字符串(这将删除格式),否则它不是空格(而是HTML换行符)。 - Jakob Harteg
3
请参考这个链接 https://dev59.com/qmkw5IYBdhLWcg3w4eYD#9974562,其中有一个很好的回答可以在不删除任何格式的情况下缩短文本。 - Giulio Piancastelli
Spanned 和 String 之间的区别完全在于格式。通过执行 Spanned.toString(),您完全删除了格式,因此不再使用 Spanned 没有任何意义。 - javaxian
1
无意义的解决方案。如果您失去所有格式,将Spanned转换为String的目的是什么? - Leo DroidCoder

5

是的,你想到的是正确的。它会在底部添加空格。但在此之前,让我解释一下它是如何工作的。

你需要查看HTML类来了解它是如何工作的。

简单来说,它是这样工作的:每当你的Html类查看一个<p>标签时,它只是简单地将两个"\n"字符附加到末尾。

在这种情况下,你在底部看到的空白实际上是由于两个\n被附加到段落末尾。

我已经添加了Html类的实际方法,负责执行此操作。

    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");

    }
}

如果您想覆盖此操作,您需要覆盖Html类本身,这有点棘手,无法在此完成。
编辑:
这里是Html类的链接, Html class

谢谢你让我知道被添加的/n。然而,我找到了自己的解决方案,将在下面写出答案。 - wh-dev

3
如果你想在对象中使用或者将内容放在特定位置,请尝试使用 <a> 标签而不是 <p><p> 会在末尾添加回车符,但是 a 不会,如果你使用 <b>,需要自己记得写上 \n,并且样式会保留。

0

@Andro Selva所提供的解释是正确的,对此并没有太多可以做的。 令人沮丧的是,随着调用中标志的包含,API 24及更高版本的情况会变得更好。

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

我怀疑FROM_HTML_SEPARATOR_LINE_BREAK_PARAGRAPH标志将把标准段落终止的双"\n\n"减少到换行符的单个"\n"

考虑到现有的Android版本历史记录,我不能只为Android API 24+编写软件!所以...我找到了一个包含2个额外自定义标签的权宜之计解决方案。

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

两者都通过这种方法调用RelativeSizeSpan类

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);
                    }
                }
            }

这是从自定义的TagHandler中调用的,该TagHandler被提供给Html.fromHtml方法,即:

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;
... 
            }

       }

为了使用,我将Textview对象的文本大小设置为1。也就是说,1像素!然后我将所需的真实文本大小设置为变量mDefaultTextSize。 我把所有的Html功能放在一个htmlTextView里,它扩展了TextView如下:
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

}

其中包括公共方法

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;
    }

一个简单的((htmlTextView)tv).setDefaultTextMetrics(null, 30);调用可以设置我的htmlTextView默认使用标准字体,文本大小为30。
然后当我在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>

我的自定义标签<box>可以让我突出文本的背景。请参见附图,其中一个结果使用<default>标签,将TextView文本大小设置为1,并且<default>标签通过30倍因子调用了RelevantSizeSpan,另一个结果如下:

<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>

不使用<default>标签,而是将TextView文本大小设置为30。在第一种情况下,额外的新行仍然存在,但它只有1像素高!

NB:没有真正的必要使用<scale factor="1.5">...</scale>标签。它们只是其他测试留下的剩余物。

结果:以下两个示例在段落之间都有2个换行符,但在左侧的示例中,其中一行只有1像素高。我将让读者自己想办法将其减少到零,但不要使用0的文本大小。


0

这个解决方案对我很有效。 创建一个帮助方法来替换所有段落的起始和结束标签,并将它们全部替换为空字符。

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

使用方法

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接