1

我有一些类似的 HTML 内容

<html>
<h3>nested unordered lists</h3>
<ul>
  <li>
     first level
     <ul>
        <li>
           second level
           <ul>
              <li>
                 third level
                 <ul>
                    <li>
                       fourth level
                       <ul>
                          <li>fifth level</li>
                       </ul>
                    </li>
                    <li>fourth level</li>
                 </ul>
              </li>
              <li>third level</li>
           </ul>
        </li>
        <li>second level</li>
        <li>second level: this should be a long enough text that will be wrapped into multiple lines</li>
     </ul>
  </li>
  <li>first level</li>
</ul> 

<hr>
<h3>nested ordered lists</h3>
<ol>
  <li>
     first level
     <ol>
        <li>
           second level
           <ol>
              <li>
                 third level
                 <ol>
                    <li>
                       fourth level
                       <ol>
                          <li>fifth level</li>
                       </ol>
                    </li>
                    <li>fourth level</li>
                 </ol>
              </li>
              <li>third level</li>
           </ol>
        </li>
        <li>second level</li>
        <li>second level: this should be a long enough text that will be wrapped into multiple lines</li>
     </ol>
  </li>
  <li>first level</li>
</ol>

<hr>
<h3>Mixed (ol and ul) nested lists:</h3>
<ul>
  <li>
     first unordered
     <ol>
        <li>first ordered</li>
        <li>
           second ordered
           <ul>
              <li>
                 unordered in second ordered
                 <ol>
                    <li>ordered in "unordered in second ordered"</li>
                    <li>another ordered in ""unordered in second ordered"</li>
                 </ol>
              </li>
           </ul>
        </li>
        <li>third ordered with some other formatting: <b>bold</b> and <i>italics</i></li>
     </ol>
  </li>
  <li>second unordered</li>
</ul>
</html>

现在我正在尝试在单个 Textview 上显示 HTML 内容。代码片段如下

textView.setText(Html.fromHtml(htmlContent, null, htmlTagHandler));

这里

  • htmlTagHandler是 HtmlTagHandler 引用,用于支持<ul>, <ol>,<li>标签。

HtmlTagHandler.java:

    public class HtmlTagHandler implements Html.TagHandler {
    /**
    * Keeps track of lists (ol, ul). On bottom of Stack is the outermost list
    * and on top of Stack is the most nested list
    */
    Stack<String> lists = new Stack<String>();
    /**
    * Tracks indexes of ordered lists so that after a nested list ends
    * we can continue with correct index of outer list
    */
    Stack<Integer> olNextIndex = new Stack<Integer>();
    /**
    * List indentation in pixels. Nested lists use multiple of this.
    */
    private static final int indent = 10;
    private static final int listItemIndent = indent * 2;
    private static final BulletSpan bullet = new BulletSpan(indent);

    @Override
    public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) {
    if (tag.equalsIgnoreCase("ul")) {
        if (opening) {
            lists.push(tag);
        } else {
            lists.pop();
        }
    } else if (tag.equalsIgnoreCase("ol")) {
        if (opening) {
            lists.push(tag);
            olNextIndex.push(Integer.valueOf(1)).toString();//TODO: add support for lists starting other index than 1
        } else {
            lists.pop();
            olNextIndex.pop().toString();
        }
    } else if (tag.equalsIgnoreCase("li")) {
        if (opening) {
            if (output.length() > 0 && output.charAt(output.length() - 1) != '\n') {
                output.append("\n");
            }
            String parentList = lists.peek();
            if (parentList.equalsIgnoreCase("ol")) {
                start(output, new Ol());
                output.append(olNextIndex.peek().toString() + ". ");
                olNextIndex.push(Integer.valueOf(olNextIndex.pop().intValue() + 1));
            } else if (parentList.equalsIgnoreCase("ul")) {
                start(output, new Ul());
            }
        } else {
            if (lists.peek().equalsIgnoreCase("ul")) {
                if ( output.length() > 0 && output.charAt(output.length() - 1) != '\n' ) {
                    output.append("\n");
                }
                // Nested BulletSpans increases distance between bullet and text, so we must prevent it.
                int bulletMargin = indent;
                if (lists.size() > 1) {
                    bulletMargin = indent-bullet.getLeadingMargin(true);
                    if (lists.size() > 2) {
                        // This get's more complicated when we add a LeadingMarginSpan into the same line:
                        // we have also counter it's effect to BulletSpan
                        bulletMargin -= (lists.size() - 2) * listItemIndent;
                    }
                }
                BulletSpan newBullet = new BulletSpan(bulletMargin);
                end(output,
                    Ul.class,
                    new LeadingMarginSpan.Standard(listItemIndent * (lists.size() - 1)),
                    newBullet);
            } else if (lists.peek().equalsIgnoreCase("ol")) {
                if ( output.length() > 0 && output.charAt(output.length() - 1) != '\n' ) {
                    output.append("\n");
                }
                int numberMargin = listItemIndent * (lists.size() - 1);
                if (lists.size() > 2) {
                    // Same as in ordered lists: counter the effect of nested Spans
                    numberMargin -= (lists.size() - 2) * listItemIndent;
                }
                end(output,
                    Ol.class,
                    new LeadingMarginSpan.Standard(numberMargin));
            }
        }
    } else {
        if (opening) Log.d("TagHandler", "Found an unsupported tag " + tag);
      }
    }


    private static void start(Editable text, Object mark) {
    int len = text.length();
    text.setSpan(mark, len, len, Spanned.SPAN_MARK_MARK);
    }

    private static void end(Editable text, Class<?> kind, Object... replaces) {
    int len = text.length();
    Object obj = getLast(text, kind);
    int where = text.getSpanStart(obj);
    text.removeSpan(obj);
    if (where != len) {
        for (Object replace: replaces) {
            text.setSpan(replace, where, len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
         }
       }
      return;
     }


    private static Object getLast(Spanned text, Class<?> kind) {
    /*
     * This knows that the last returned object from getSpans()
     * will be the most recently added.
     */
       Object[] objs = text.getSpans(0, text.length(), kind);
       if (objs.length == 0) {
               return null;
        }
       return objs[objs.length - 1];
      }

     private static class Ul { }
     private static class Ol { }
    }

在这里,我可以使用Html.fromHtml(htmlContent, htmlImageGet, htmlTagHandler)来显示项目符号和数字,但不能使用水平线。谁能指导我如何通过 HtmlTagHandler 或任何其他方法<hr>在方法中显示水平线或支持标签。Html.fromHtml()

预期输出如下屏幕截图。

在此处输入图像描述

一切正常,期望水平线。任何人都可以帮助解决这个问题。

4

1 回答 1

4

这就是我解决你的问题的方法。这不是一个非常优雅的解决方案,但它确实有效。

Html.TagHandler:

import org.xml.sax.XMLReader;
import android.text.Editable;
import android.text.Html;
import android.text.Spannable;
import android.text.Spanned;
import android.util.Log;

public class CustomTagHandler implements Html.TagHandler {

    @Override
    public void handleTag(final boolean opening, final String tag,
                          Editable output, final XMLReader xmlReader) {
        if(tag.equals("hr")) {
            handleHRTag(opening,output);
        }
    }

    private void handleHRTag(boolean opening, Editable output) {
        if(!opening) return;
        int start = output.length();
        // The space is necessary, the block requires some content:
        output.append(" \n"); 
        output.setSpan(new CustomHRSpan(0xff000000,5.0f,2.0f),
                       start, output.length(), 0);
    }
}

然后创建一个 CustomHRSpan 类:

import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.text.style.LineBackgroundSpan;
import android.text.style.LineHeightSpan;


public class CustomHRSpan implements LineBackgroundSpan, LineHeightSpan {
    private final int color;          // Color of line
    private final float height;       // Height of HR element
    private final float line;         // Line size
    private final float marginLeft;   // Margin of line, left side
    private final float marginRight;  // Margin of line, right side

    public CustomHRSpan(int color, float height, float line) {
        this.color        = color;
        this.height       = height;
        this.line         = line;
        this.marginLeft   = 5.0f;
        this.marginRight  = 5.0f;
    }

    @Override
    public void drawBackground(Canvas c, Paint p, int left, int right, int top, int baseline, int bottom,
                               CharSequence text, int start, int end, int lnum) {
        int paintColor = p.getColor();
        float y = (float)(top+(bottom-top)/2) - line*0.5f;
        RectF r = new RectF((float)left+marginLeft, y,
                            (float)(right-left)-marginRight, y+line);
        p.setColor(color);
        c.drawRect(r, p);
        p.setColor(paintColor);
    }

    @Override
    public void chooseHeight(CharSequence text, int start, int end, int spanstartv, int v, Paint.FontMetricsInt fm) {
        fm.descent  = (int)height / 2;
        fm.ascent   = (int)height - fm.descent;
        fm.leading  = 0;
        fm.top      = fm.ascent;
        fm.bottom   = fm.descent;
    }
}
于 2016-10-05T02:54:54.483 回答