【问题标题】:Extra padding on TextView with HTML contents使用 HTML 内容在 TextView 上进行额外填充
【发布时间】:2013-05-16 10:54:32
【问题描述】:

我有这个TextView

<TextView
    android:id="@+id/issue_journal_item_notes"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignLeft="@+id/issue_journal_item_details"
    android:layout_below="@+id/issue_journal_item_details"
    android:background="@drawable/journal_item_notes_background"
    android:padding="8dp"
    android:text="issue_journal_item_notes"
    android:textIsSelectable="true" />

我正在填写:

String html = "<p>Hi,<br/>Do you think you could get a logcat during the crash? That seems really strange, especially the fact that it makes Android reboot.<br/>You can get the SDK here: http://developer.android.com/sdk/index.html<br/>(needed for logcat)</p>";
theTextView.setText(Html.fromHtml(html));

这会导致:

“Assignee ...”是另一个TextView
我的TextView 是灰色背景的。非常浅的灰色可以清楚地看到它的边界。左边的深灰色条是背景的一部分,所以也是TextView

我们可以清楚地看到 8dp 方形内边距。但是,底部的空白空间是什么?这是某种填充,但我没有在 XML 或代码中设置它!

如果有人问,我需要 HTML 支持,因为与上面显示的屏幕截图不同,内容可能包含一些 HTML 内容(&lt;pre&gt;&lt;i&gt;&lt;b&gt; 等) .

【问题讨论】:

标签: android html textview


【解决方案1】:

您看到的额外“填充”实际上只是一个换行符,然后是另一个换行符:

当您深入了解Html.fromHtml(...) 实现时,您会遇到以下处理段落标签的方法:

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

上面的 sn-p 取自Android 4.2.2 source。逻辑非常简单,基本上确保每个段落标签都以\n\n 结尾,以在两个元素块之间提供视觉间隙。这意味着框架不会考虑整个 Html 文本是否仅包含单个段落(您的情况)或多个连续的段落 - 在转换的末尾会有两个换行符总是段。

话虽如此,如果您知道自己总是在处理单个段落,最简单的解决方案是在将其提供给Html.fromHtml(...) 之前删除该环绕段落。这几乎是其他答案之一中提出的内容。

现在,既然您提到这不是一个真正的选项,那么替代方法是“修剪”Html.fromHtml(...) 的结果,删除任何尾随空格。 Android 返回一个Spanned(通常这是一个SpannableStringBuilder 对象),不幸的是,它没有内置trim() 方法。不过,想出自己的实现方式或借用现有的几种实现方式中的一种并不太难。

trim() 方法的基本实现有点像这样:

public static CharSequence trim(CharSequence s, int start, int end) {
    while (start < end && Character.isWhitespace(s.charAt(start))) {
        start++;
    }

    while (end > start && Character.isWhitespace(s.charAt(end - 1))) {
        end--;
    }

    return s.subSequence(start, end);
}

要使用它,请将您的原始代码更改为:

String html = "<p>Hi,<br/>Do you think you could get a logcat during the crash? That seems really strange, especially the fact that it makes Android reboot.<br/>You can get the SDK here: http://developer.android.com/sdk/index.html<br/>(needed for logcat)</p>";
CharSequence trimmed = trim(Html.fromHtml(html));
theTextView.setText(trimmed);

然后瞧,之前和之后:

【讨论】:

  • Html.fromHtml 返回一个 Spanned,它不能像 CharSequence 那样修剪。此外,我的文本已经被修剪:&lt;p&gt;text&lt;/p&gt; 不包含任何空白字符,但它在底部有额外的填充。
  • 听起来你没有尝试就拒绝了我的建议——考虑到我投入的时间和我提供的所有即用型代码,这有点忘恩负义。请注意Spanned implements CharSequence(就像Java 中所有代表文本内容的类一样),所以任何适用于后者的东西也适用于前者。此外,修剪样式/标记CharSequence(或Spanned,如果你喜欢)与修剪原始输入String不同。我建议你先试试上面,应该不到 1 分钟...
  • 我的措辞不好,对此我深表歉意。我非常感谢您为此付出了很多努力。但是,我的意思是,如果我将我的 HTML 代码转换为愚蠢的 CharSequence 并简单地删除 Android 添加的额外 \n,我将丢失所有格式。您的解决方案确实有效,但以 HTML 格式为代价,实际上只是将文本放在 TextView 中。我同意你确实发现了多余的双 \n 的故障,这是不必要的。但是你提出的,除非我做错了,否则不会做我想做的。
  • 听起来你明白我要指出的内容:修剪Html.fromHtml() 的结果应该保持实际内容(及其所有格式)完好无损,同时删除底部的额外间距同时。很高兴听到它成功了 - 没有造成任何伤害。 :)
  • @Boy:是的,不应该有所作为。哎呀,您甚至可以将其分配给 Object 实例...(但请不要)。
【解决方案2】:

你也可以使用下面的代码

 myTextView.setText(noTrailingwhiteLines(html));

private CharSequence noTrailingwhiteLines(CharSequence text) {

    while (text.charAt(text.length() - 1) == '\n') {
        text = text.subSequence(0, text.length() - 1);
    }
    return text;
}

【讨论】:

  • 这是帮助我解决这个问题的答案。我认为这也是一个 css 问题......这对于 android 来说是一个非常讨厌的技巧来添加那些额外的 \n's
【解决方案3】:

Html.fromHtml(html) 返回Spannable,因此您可以通过调用toString() 然后将trim 字符串转换为字符串,然后将其设置为textview

String html = "<p>Hi,<br/>Do you think you could get a logcat during the crash? That seems really strange, especially the fact that it makes Android reboot.<br/>You can get the SDK here: http://developer.android.com/sdk/index.html<br/>(needed for logcat)</p>";
theTextView.setText(Html.fromHtml(html).toString().trim());

【讨论】:

  • 最简单的解决方案
  • 它首先会杀死您使用 Html 的所有格式。
  • 不是最好的解决方案。因为Html.fromHtml(html: String) 的目的是充分利用格式化。
【解决方案4】:

试试这个,去掉&lt;p&gt;标签。

String html = "Hi,<br/>Do you think you could get a logcat during the crash? That seems really strange, especially the fact that it makes Android reboot.<br/>You can get the SDK here: http://developer.android.com/sdk/index.html<br/>(needed for logcat)";
theTextView.setText(Html.fromHtml(html));

希望它有效。

【讨论】:

  • 我想过,但实际上我的文本来自 Textile 标记文本,它将任何文本行转换为 &lt;p&gt; 标记内的任何文本行或空行。所以我不想删除段落标签,因为 1)在某些情况下它是必需的(例如,一个 &lt;pre&gt; 后跟一个 &lt;p&gt;)和 2)因为它应该可以工作。
【解决方案5】:

处理 HTML 标记会导致添加换行符 \n,有时会导致多个 \n 相互跟随。结果将是一个文本之间有多个换行符的字符串(特别是如果 HTML 字符串包含&lt;br/&gt; 标记)。

以下方法删除由 &lt;br/&gt; tag. 产生的前导和尾随换行符

此外,减少文本之间的多个新行 (API > 24)。

/**
 *
 * @param htmlString
 * @return Spanned represents the html string removed leading and trailing newlines. Also, reduced newlines resulted from processing HTML tags (for API >24)
 */
public static Spanned processHtmlString(String htmlString){

    // remove leading <br/>
    while (htmlString.startsWith("<br/>")){

        htmlString = htmlString.replaceFirst("<br/>", "");
    }

    // remove trailing <br/>
    while (htmlString.endsWith("<br/>")){

        htmlString =  htmlString.replaceAll("<br/>$", "");
    }

    // reduce multiple \n in the processed HTML string 
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {

        return Html.fromHtml(htmlString,  FROM_HTML_MODE_COMPACT);
    }else{

        return Html.fromHtml(htmlString);
    }
}

【讨论】:

    【解决方案6】:

    根据 Android 文档,Html.fromHtml() 使用 FROM_HTML_MODE_LEGACY 将 HTML 转换为文本。但 FROM_HTML_MODE_LEGACY 在其间添加了两个换行符。

    从 Android N 开始,FROM_HTML_MODE_COMPACT 默认只使用一个换行符。

    /**
         * Flags for {@link #fromHtml(String, int, ImageGetter, TagHandler)}: Separate block-level
         * elements with blank lines (two newline characters) in between. This is the legacy behavior
         * prior to N.
         */
        public static final int FROM_HTML_MODE_LEGACY = 0x00000000;
    
        /**
         * Flags for {@link #fromHtml(String, int, ImageGetter, TagHandler)}: Separate block-level
         * elements with line breaks (single newline character) in between. This inverts the
         * {@link Spanned} to HTML string conversion done with the option
         * {@link #TO_HTML_PARAGRAPH_LINES_INDIVIDUAL}.
         */
        public static final int FROM_HTML_MODE_COMPACT =
                FROM_HTML_SEPARATOR_LINE_BREAK_PARAGRAPH
                | FROM_HTML_SEPARATOR_LINE_BREAK_HEADING
                | FROM_HTML_SEPARATOR_LINE_BREAK_LIST_ITEM
                | FROM_HTML_SEPARATOR_LINE_BREAK_LIST
                | FROM_HTML_SEPARATOR_LINE_BREAK_DIV
                | FROM_HTML_SEPARATOR_LINE_BREAK_BLOCKQUOTE;
    

    因此,我们可以使用将 HTML 转换为纯文本,

    HtmlCompat.fromHtml(htmlString, HtmlCompat.FROM_HTML_MODE_COMPACT);
    

    【讨论】:

      【解决方案7】:

      TextView 周围的布局是什么?我记得有些布局忽略了组件的高度偏好,只是让它适合。

      【讨论】:

      • 这是一个RelativeLayout,具有布局高度/宽度、背景和另一个 8dp 内边距(屏幕截图两边最灰)
      【解决方案8】:

      https://stackoverflow.com/a/16745540/2761728 表明 Android 将在您的 html 标记末尾添加额外的 \n。答案显示了一种在内容末尾修剪不需要的\n 的方法,但它不会删除Android 插入的每个标记之间的\n

      对于我来说,我有这个 html 内容:

      <p>This is the first paragraph.</p><p>And this is second paragraph.</p>
      

      Html.fromHtml之后,文字会变成

      This is the first paragraph. 
      
      
      And this is second paragraph. 
      
      
      

      我尝试了一些方法,发现\n 可以通过提供标志Html 来避免。我将代码与trim 结合起来,得出了这个解决方案:

      trimLastBreak(HtmlCompat.fromHtml(htmlContent, FROM_HTML_SEPARATOR_LINE_BREAK_PARAGRAPH))
      

      结果很完美:

      This is the first paragraph.
      And this is second paragraph.
      

      【讨论】:

        猜你喜欢
        • 2017-06-17
        • 2015-10-24
        • 1970-01-01
        • 1970-01-01
        • 2015-04-02
        • 2016-07-24
        • 2018-12-17
        • 2018-03-30
        • 2013-01-01
        相关资源
        最近更新 更多