HTML列表标记不能在android textview中工作。 我能做什么?

HTML列表标记不能在Android TextView中使用。 这是我的string内容:

String str="A dressy take on classic gingham in a soft, textured weave of stripes that resembles twill. Take a closer look at this one.<ul><li>Trim, tailored fit for a bespoke feel</li><li>Medium spread collar, one-button mitered barrel cuffs</li><li>Applied placket with genuine mother-of-pearl buttons</li><li>;Split back yoke, rear side pleats</li><li>Made in the USA of 100% imported cotton.</li></ul>"; 

我用这样的文本视图加载它:

 textview.setText(Html.fromHtml(str)); 

输出看起来像一个段落。 我能做什么? 有没有解决办法?

编辑:

 webview.loadData(str,"text/html","utf-8"); 

正如您在Html类源代码中看到的, Html.fromHtml(String)不支持所有HTML标记。 在这种情况下,不支持<ul><li>

从源代码我已经build立了一个允许的HTML标签列表:

  • br
  • p
  • div
  • em
  • b
  • strong
  • cite
  • dfn
  • i
  • big
  • small
  • font
  • blockquote
  • tt
  • monospace
  • a
  • u
  • sup
  • sub

所以你最好使用WebView和它的loadDataWithBaseURL方法。 尝试这样的事情:

 String str="<html><body>A dressy take on classic gingham in a soft, textured weave of stripes that resembles twill. Take a closer look at this one.<ul><li>Trim, tailored fit for a bespoke feel</li><li>Medium spread collar, one-button mitered barrel cuffs</li><li>Applied placket with genuine mother-of-pearl buttons</li><li>;Split back yoke, rear side pleats</li><li>Made in the USA of 100% imported cotton.</li></ul></body></html>"; webView.loadDataWithBaseURL(null, str, "text/html", "utf-8", null); 

看起来像一个老问题,我有同样的问题,我所做的是重写默认的TagHandler ,我是新的StackOverflow和Android,并感谢任何更正或更好的方法:)这一个为我工作。

 package com.tumblr.amangautam.cars; import org.xml.sax.XMLReader; import android.app.Activity; import android.os.Bundle; import android.text.Editable; import android.text.Html; import android.text.Html.TagHandler; import android.util.Log; public class MyTagHandler implements TagHandler{ boolean first= true; String parent=null; int index=1; @Override public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) { if(tag.equals("ul")) parent="ul"; else if(tag.equals("ol")) parent="ol"; if(tag.equals("li")){ if(parent.equals("ul")){ if(first){ output.append("\n\t•"); first= false; }else{ first = true; } } else{ if(first){ output.append("\n\t"+index+". "); first= false; index++; }else{ first = true; } } } } } 

并显示文字…

 myTextView.setText(Html.fromHtml("<ul><li>I am an Android developer</li><li>Another Item</li></ul>", null, new MyTagHandler())); 

[编辑]

Kuitsi也发布了一个非常好的图书馆 ,

从这个链接得到它。

完整示例项目位于https://bitbucket.org/Kuitsi/android-textview-html-list
示例图片可从https://kuitsi.bitbucket.io/stackoverflow3150400_screen.png获取;

这个解决scheme最接近masha的答案 。 一些代码也取自内部类android.text.Html.HtmlToSpannedConverter 。 它支持嵌套的有序列表和无序列表,但是过长的有序列表文本仍然与项目编号而不是文本alignment。 混合列表(ol和ul)也需要一些工作。 示例项目包含传递给Html.fromHtml(String,ImageGetter,TagHandler)的Html.TagHandler的实现 。

编辑:为了更广泛的HTML标签支持, https://github.com/NightWhistler/HtmlSpanner也许值得一试。;

阿曼Guatam代码的一个小修复。 上面的函数有渲染换行符的问题。 例如:如果<li>标签之前是<p>标签,则会呈现2个换行符。 这里是升级代码:

 import org.xml.sax.XMLReader; import android.text.Editable; import android.text.Html.TagHandler; public class ListTagHandler implements TagHandler { boolean first = true; @Override public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) { // TODO Auto-generated method stub if (tag.equals("li")) { char lastChar = 0; if (output.length() > 0) lastChar = output.charAt(output.length() - 1); if (first) { if (lastChar == '\n') output.append("\t• "); else output.append("\n\t• "); first = false; } else { first = true; } } } } 

使用LeadingMarginSpan的不同解决scheme。 处理有序和无序列表以及嵌套。

 public class ListTagHandler implements TagHandler { private int m_index = 0; private List< String > m_parents = new ArrayList< String >( ); @Override public void handleTag( final boolean opening, final String tag, Editable output, final XMLReader xmlReader ) { if( tag.equals( "ul" ) || tag.equals( "ol" ) || tag.equals( "dd" ) ) { if( opening ) { m_parents.add( tag ); } else m_parents.remove( tag ); m_index = 0; } else if( tag.equals( "li" ) && !opening ) handleListTag( output ); } private void handleListTag( Editable output ) { if( m_parents.get(m_parents.size()-1 ).equals( "ul" ) ) { output.append( "\n" ); String[ ] split = output.toString( ).split( "\n" ); int lastIndex = split.length - 1; int start = output.length( ) - split[ lastIndex ].length( ) - 1; output.setSpan( new BulletSpan( 15 * m_parents.size( ) ), start, output.length( ), 0 ); } else if( m_parents.get(m_parents.size()-1).equals( "ol" ) ) { m_index++ ; output.append( "\n" ); String[ ] split = output.toString( ).split( "\n" ); int lastIndex = split.length - 1; int start = output.length( ) - split[ lastIndex ].length( ) - 1; output.insert( start, m_index + ". " ); output.setSpan( new LeadingMarginSpan.Standard( 15 * m_parents.size( ) ), start, output.length( ), 0 ); } } } 

如果你只需要格式化一个列表,保持简单,在你的TextView中复制/粘贴一个unicode字符来获得相同的结果。

•Unicode字符“BULLET”(U + 2022)

警告

截至2016年2月10日, android.text.Html实际上支持liul标签,并使用基本的new BulletSpan() ,这意味着在Android的最新版本中,这里发布的Html.TagHandler解决scheme将被忽略

确保你的代码处理这个变化,如果你期望BulletSpan的缺口大于默认值,那么你必须有一些解决scheme来查找/replace跨度

我来这里寻找TagHandler实现。 Truong Nguyen和Aman Guatam的答案都非常好,但我需要两者的混合版本:我需要我的解决scheme不要过度格式化,并且能够重新parsing<ol>标签,因为我正在parsing<h3>title</h3><ol><li>item</li><li>item</li><li>item</li></ol>

这是我的解决scheme。

 import org.xml.sax.XMLReader; import android.text.Editable; import android.text.Html.TagHandler; public class MyTagHandler implements TagHandler { boolean first = true; String parent = null; int index = 1; public void handleTag(final boolean opening, final String tag, final Editable output, final XMLReader xmlReader) { if (tag.equals("ul")) { parent = "ul"; index = 1; } else if (tag.equals("ol")) { parent = "ol"; index = 1; } if (tag.equals("li")) { char lastChar = 0; if (output.length() > 0) { lastChar = output.charAt(output.length() - 1); } if (parent.equals("ul")) { if (first) { if (lastChar == '\n') { output.append("\t• "); } else { output.append("\n\t• "); } first = false; } else { first = true; } } else { if (first) { if (lastChar == '\n') { output.append("\t" + index + ". "); } else { output.append("\n\t" + index + ". "); } first = false; index++; } else { first = true; } } } } } 

请注意,由于我们正在重新设置索引值,每当一个新的列表开始时,如果您在<ol><li>1<ol><li>1.1</li><li>1.2</li></ol><li>2</li></ol>

  1. 1
    1. 1.1
    2. 1.2
  2. 2

用这个代码,你会得到1, 1, 2, 3而不是1,1,2,2。

当然,在Android TextView中有一种显示子弹的方法。 您可以用&#149;replace<li>标签 (这是子弹的HTML代码)。

如果你想尝试其他列表图标,使用表中的首选是这个链接;

http://www.ascii-code.com/

下一个代码如何(基于这个链接 ):

 public class TextViewHtmlTagHandler implements 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(final boolean opening,final String tag,final Editable output,final 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"); final 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.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; } final 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.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(final Editable text,final Object mark) { final int len=text.length(); text.setSpan(mark,len,len,Spanned.SPAN_MARK_MARK); } private static void end(final Editable text,final Class<?> kind,final Object... replaces) { final int len=text.length(); final Object obj=getLast(text,kind); final int where=text.getSpanStart(obj); text.removeSpan(obj); if(where!=len) for(final Object replace : replaces) text.setSpan(replace,where,len,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); return; } private static Object getLast(final Spanned text,final Class<?> kind) { /* * This knows that the last returned object from getSpans() * will be the most recently added. */ final 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 { } } 

您可以简单地用unicodesreplace“li”

  @Override public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) { if (tag.equalsIgnoreCase("li")) { if (opening) { output.append("\u2022 "); } else { output.append("\n"); } } } 

我遇到了这个问题,在用@Kuitsis解决scheme列表后,我总是得到一个空行。 我在handleTag()中添加了几行,现在空行消失了:

 @Override public void handleTag(final boolean opening, final String tag, final Editable output, final XMLReader xmlReader) { if (UL_TAG.equalsIgnoreCase(tag)) { if (opening) { // handle <ul> lists.push(new Ul()); } else { // handle </ul> lists.pop(); if (output.length() > 0 && output.charAt(output.length() - 1) == '\n') { output.delete(output.length() - 1, output.length()); } } } else if (OL_TAG.equalsIgnoreCase(tag)) { if (opening) { // handle <ol> lists.push(new Ol()); // use default start index of 1 } else { // handle </ol> lists.pop(); if (output.length() > 0 && output.charAt(output.length() - 1) == '\n') { output.delete(output.length() - 1, output.length()); } } } else if (LI_TAG.equalsIgnoreCase(tag)) { if (opening) { // handle <li> lists.peek().openItem(output); } else { // handle </li> lists.peek().closeItem(output, lists.size()); } } else { Log.d("TagHandler", "Found an unsupported tag " + tag); } } 

沃尔德莫特勋爵的答案是一个很好的起点。 不过,我需要ol标签来显示有序列表1. 2. 3. ....而不是子弹。 另外,嵌套标签需要特殊处理才能正常工作。

在我的代码中,我维护了堆栈(parentList)来跟踪打开和closures的ulol标签,并了解当前打开的标签。 而且,在嵌套ol标签的情况下,使用levelWiseCounter来维护不同的计数。

 myTextView.setText(Html.fromHtml("your string", null, new CustomTagHandler())); 

。 。 。

 private static class CustomTagHandler implements TagHandler { int level = 0; private LinkedList<Tag> parentList = new LinkedList<DetailFragment.CustomTagHandler.Tag>(); private HashMap<Integer, Integer> levelWiseCounter = new HashMap<Integer, Integer>(); @Override public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) { if (tag.equalsIgnoreCase("ul") || tag.equalsIgnoreCase("ol")) { if (opening) { if (tag.equalsIgnoreCase("ul")) { parentList.push(Tag.UL); } else { parentList.push(Tag.OL); } level++; } else { if (!parentList.isEmpty()) { parentList.pop(); //remove counter at that level, in any present. levelWiseCounter.remove(level); } level--; if (level < 0) { level = 0; } } } else if (tag.equalsIgnoreCase("li")) { if (opening && level > 0) { //new line check int length = output.toString().length(); if (length > 0 && (output.toString().charAt(length - 1) == '\n')) { } else { output.append("\n"); } //add tabs as per current level of li for (int i = 0; i < level; i++) { output.append("\t"); } // append dot or numbers based on parent tag if (Tag.UL == parentList.peek()) { output.append("•"); } else { //parent is OL. Check current level and retreive counter from levelWiseCounter int counter = 1; if (levelWiseCounter.get(level) == null) { levelWiseCounter.put(level, 1); } else { counter = levelWiseCounter.get(level) + 1; levelWiseCounter.put(level, counter); } output.append(padInt(counter) + "."); } //trailing tab output.append("\t"); } } } /** * Add padding so that all numbers are aligned properly. Currently supports padding from 1-99. * * @param num * @return */ private static String padInt(int num) { if (num < 10) { return " " + num; } return "" + num; } private enum Tag { UL, OL } }