La etiqueta Html List no funciona en la vista de texto de Android. ¿que puedo hacer?

La etiqueta Html List no funciona en Android TextView. Este es mi contenido de cuerda:

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.
  • Trim, tailored fit for a bespoke feel
  • Medium spread collar, one-button mitered barrel cuffs
  • Applied placket with genuine mother-of-pearl buttons
  • ;Split back yoke, rear side pleats
  • Made in the USA of 100% imported cotton.
";

Lo cargué en una vista de texto como esta:

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

El resultado parece un párrafo. ¿Que puedo hacer? ¿Hay alguna solución para eso?

Editar:

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

Como puede ver en el código fuente de la clase Html , Html.fromHtml(String) no admite todas las tags HTML. En este caso,

    y

  • no son compatibles.

    Del código fuente he creado una lista de tags HTML permitidas:

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

    Entonces es mejor usar WebView y su método loadDataWithBaseURL . Pruebe algo como esto:

     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.
    • Trim, tailored fit for a bespoke feel
    • Medium spread collar, one-button mitered barrel cuffs
    • Applied placket with genuine mother-of-pearl buttons
    • ;Split back yoke, rear side pleats
    • Made in the USA of 100% imported cotton.
    "; webView.loadDataWithBaseURL(null, str, "text/html", "utf-8", null);

    Parece una vieja pregunta, tengo el mismo problema, lo que hice fue anular el TagHandler predeterminado, soy nuevo en StackOverflow y Android, y valoro cualquier corrección o mejor método 🙂 Este me funcionó.

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

    y para mostrar el texto …

     myTextView.setText(Html.fromHtml("
    • I am an Android developer
    • Another Item
    ", null, new MyTagHandler()));

    [Editar]

    Kuitsi también ha publicado una biblioteca realmente buena que hace lo mismo:

    Lo obtuve de este enlace SO .

    El proyecto de muestra completo se encuentra en https://bitbucket.org/Kuitsi/android-textview-html-list .
    La imagen de muestra está disponible en http://sofes.miximages.com/android/stackoverflow3150400_screen.png

    Esta solución está más cerca de la respuesta de Masha . Algunos códigos también se toman de la clase interna android.text.Html.HtmlToSpannedConverter . Admite listas anidadas ordenadas y desordenadas, pero los textos demasiado largos en listas ordenadas siguen alineados con el número de artículo en lugar de con el texto. Las listas mixtas (ol y ul) también necesitan algo de trabajo. El proyecto de ejemplo contiene la implementación de Html.TagHandler que se pasa a Html.fromHtml (String, ImageGetter, TagHandler) .

    Editar: para una compatibilidad más amplia con tags HTML, también puede valer la pena intentar https://github.com/NightWhistler/HtmlSpanner .

    Una pequeña solución al código de Aman Guatam. La función anterior tiene el problema de representar el carácter de línea nueva. Por ejemplo: si antes de la etiqueta

  • es una etiqueta

    , se representan 2 caracteres de nueva línea. Aquí está el código actualizado:

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

    ADVERTENCIA

    a partir del 10 de febrero de 2016, android.text.Html realmente admite tags li y ul y utiliza un new BulletSpan() básico, lo que significa que en las últimas versiones de Android se Html.TagHandler soluciones Html.TagHandler publicadas aquí.

    asegúrese de que su código maneje este cambio en caso de que esté esperando un BulletSpan con un espacio más grande que el predeterminado, tendrá que tener algún tipo de solución que haga un descubrimiento / reemplazo de los tramos

    Diferente solución usando LeadingMarginSpan. Maneja listas ordenadas y desordenadas, así como la anidación.

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

    Si solo necesita formatear una lista, manténgala simple y copie / pegue un carácter Unicode en su TextView para lograr el mismo resultado.

    • Carácter Unicode ‘BULLET’ (U + 2022)

    Vine aquí buscando implementaciones de TagHandler. Las respuestas de Truong Nguyen y Aman Guatam son muy buenas, pero necesitaba una versión mixta de ambas: necesitaba mi solución para no sobreformarla y poder resolver las tags

      , ya que estoy analizando algo así como el

      title

      1. item
      2. item
      3. item

      .

      Aquí está mi solución.

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

      Tenga en cuenta que, dado que estamos restableciendo el valor del índice cada vez que se inicia una nueva lista, NO funcionará si anida listas como en

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

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

      Con ese código, obtendría 1, 1, 2, 3 lugar de 1, 1, 2, 2 .

      Claro, hay una forma de mostrar viñetas en Android TextView. Puede reemplazar las tags

    1. con (que es código HTML para viñeta).

      Si desea probar otros íconos de lista, use el preferido de la tabla es este enlace;

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

      Simplemente puede reemplazar el “li” con unicodes

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

      La respuesta de Lord Voldermort es un buen punto de partida. Sin embargo, ol etiqueta ol para mostrar la lista ordenada 1. 2. 3. .... lugar de viñetas. Además, las tags anidadas necesitan un manejo especial para funcionar correctamente.

      En mi código, he mantenido stack (parentList) para realizar un seguimiento de las tags ul y ol abiertas y cerradas, y también para conocer la etiqueta abierta actual. Además, levelWiseCounter se usa para mantener conteos diferentes en caso de tags ol anidadas.

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

      . . .

       private static class CustomTagHandler implements TagHandler { int level = 0; private LinkedList parentList = new LinkedList(); private HashMap levelWiseCounter = new HashMap(); @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 } } 

      ¿Qué tal el próximo código (basado en este enlace ):

       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 lists =new Stack(); /** * Tracks indexes of ordered lists so that after a nested list ends * we can continue with correct index of outer list */ Stack olNextIndex =new Stack(); /** * 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 { } } 

      Tuve el problema, siempre tuve una línea vacía después de una lista con la solución @Kuitsis. Agregué algunas líneas en handleTag () y ahora las líneas vacías se han ido:

       @Override public void handleTag(final boolean opening, final String tag, final Editable output, final XMLReader xmlReader) { if (UL_TAG.equalsIgnoreCase(tag)) { if (opening) { // handle 
        lists.push(new Ul()); } else { // handle
      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
        lists.push(new Ol()); // use default start index of 1 } else { // handle
      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
    2. lists.peek().openItem(output); } else { // handle
    3. lists.peek().closeItem(output, lists.size()); } } else { Log.d("TagHandler", "Found an unsupported tag " + tag); } }

      esto es una confirmación de lo que ha declarado Kassim. hay fragmentación. Encontré cómo resolver esto. Tengo que cambiar el nombre de

    4. y ul a una etiqueta personalizada. asi que:

       myHTML.replaceAll("","").replaceAll("
        ",""); //likewise for li

      luego en mi controlador puedo buscar esa etiqueta personalizada (que no hace nada) y hacer que haga algo.

       //now my handler can handle the customtags. it was ignoring them after nougat. public class UlTagHandler implements Html.TagHandler { //for ul in nougat and up this tagHandler is completely ignored @Override public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) { if (tag.equals("customtag2") && opening) output.append("\n\t\u25CF\t"); if (tag.equals("customtag2") && !opening) output.append("\n"); } } 

      esto debería hacer que funcione para todas las versiones de Android.