studio fromhtml example html android textview

fromhtml - Android: ¿Cómo usar el Html.TagHandler?



html.fromhtml android example (5)

Estoy tratando de construir una aplicación de Android para un tablero de mensajes. Para mostrar el html formateado para los contenidos de la publicación, he elegido el método TextView y el Html.fromHtml (). Eso, lamentablemente, cubre solo unas pocas etiquetas html. Las etiquetas desconocidas son manejadas por una clase que implementa TagHandler y tiene que ser generada por mí mismo.

Ahora, busqué en Google y no puedo encontrar un ejemplo de cómo debería funcionar esta clase. Consideremos que tengo una etiqueta u para subrayar algunos textos (sé que esto está obsoleto, pero lo que sea). ¿Cómo se ve mi TagHandler?

Se llama de la siguiente manera:

public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) {

Los primeros dos argumentos están bien. Supongo que tengo que modificar la salida usando output.append (). ¿Pero cómo adjunto algo subrayado allí?


Esta solución se encuentra en el sdk de Android

En android.text.html . Líneas 596 - 626. Copiar / pegar

private static <T> Object getLast(Spanned text, Class<T> 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; } else { return objs[objs.length - 1]; } } private static void start(SpannableStringBuilder text, Object mark) { int len = text.length(); text.setSpan(mark, len, len, Spannable.SPAN_MARK_MARK); } private static <T> void end(SpannableStringBuilder text, Class<T> kind, Object repl) { int len = text.length(); Object obj = getLast(text, kind); int where = text.getSpanStart(obj); text.removeSpan(obj); if (where != len) { text.setSpan(repl, where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } }

Para usar, anule TagHandler como sigue:

public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) { if(tag.equalsIgnoreCase("strike") || tag.equals("s")) { if(opening){ start((SpannableStringBuilder) output, new Strike(); } else { end((SpannableStringBuilder) output, Strike.class, new StrikethroughSpan()); } } } /* * Notice this class. It doesn''t really do anything when it spans over the text. * The reason is we just need to distinguish what needs to be spanned, then on our closing * tag, we will apply the spannable. For each of your different spannables you implement, just * create a class here. */ private static class Strike{}


Aunque puedo verlo en la API de Html.java, el estilo y la alineación de texto se pueden usar con las etiquetas <p> , <div> etc. No estoy logrando que funcione con <p align="center"> o <p style="text-align: center"> y muchas otras variantes. Al no poder hacer esta alineación central de texto y otros estilos como el tamaño de fuente, múltiples caras de fuente desde mis archivos ttf, color de fondo, hice mi propia htmlTextView basada en TextView pero con mi propia clase tagHandler. Dadas una o dos irritaciones menores, la mayoría de las etiquetas están bien, pero mis etiquetas de alineación personalizadas, izquierda , centro , derecha funcionan solo en condiciones especiales (que yo no entiendo), de lo contrario. ¡no funcionan ni bloquean la aplicación! Este es mi identificador de etiqueta de alineación. Tiene la misma estructura que todos los otros manejadores de etiquetas personalizadas pero realmente se comporta de forma extraña. ¡La forma básica de mis controladores de etiquetas es la misma y no la he concebido ! Encontré la plantilla del manejador de etiquetas después de muchas horas buscando en la web. Estoy agradecido con quien sea que lo haya publicado, pero mi memoria y capacidad organizativa son tales que realmente no puedo recordar quién o dónde, así que si reconoces este código como tuyo, por favor avísame. El único enlace (que está aquí) que tengo está en mi código: : Android: ¿Cómo usar el Html.TagHandler?

private void ProcessAlignment(Layout.Alignment align, boolean opening, Editable output) { int len = output.length(); if (opening) { output.setSpan(new AlignmentSpan.Standard(align), len, len, Spannable.SPAN_MARK_MARK); } else { Object obj = getLast(output, AlignmentSpan.Standard.class); int where = output.getSpanStart(obj); output.removeSpan(obj); if (where != len) { output.setSpan(new AlignmentSpan.Standard(align), where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } } }

Creo que el problema es que la etiqueta final no se está conectando con la etiqueta de inicio correcta.

private Object getLast(Editable text, Class kind) { Object[] objs = text.getSpans(0, text.length(), kind); if (objs.length == 0) { return null; } else { for (int i = objs.length - 1; i >= 0; --i) { if (text.getSpanFlags(objs[i]) == Spannable.SPAN_MARK_MARK) { return objs[i]; } } return null; } }

Esta es la clase total y algo no está bien. ¡El componente más grande es mi comprensión! Quizás alguien pueda ayudarme a entender mejor ...

public class htmlTextView extends AppCompatTextView { static Typeface mLogo; static Typeface mGAMZ; static Typeface mChalk; static Typeface mSouvenir; int GS_PAINTFLAGS = FILTER_BITMAP_FLAG | ANTI_ALIAS_FLAG | SUBPIXEL_TEXT_FLAG | HINTING_ON; public htmlTextView(Context context) { super(context); initialise(); } public htmlTextView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); initialise(); } public htmlTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initialise(); } private void initialise() { mLogo = Typeface.createFromAsset(theAssetManager, "fonts/logo.ttf"); mGAMZ = Typeface.createFromAsset(theAssetManager, "fonts/GAMZ One.ttf"); mChalk = Typeface.createFromAsset(theAssetManager, "fonts/swapfix.ttf"); mSouvenir = Typeface.createFromAsset(theAssetManager, "fonts/Souvenir Regular.ttf"); setPaintFlags(GS_PAINTFLAGS); } public void setDefaultTypefaceSouvenir() { setTypeface(mSouvenir); } public void setDefaultTypefaceGAMZ() { setTypeface(mGAMZ); } public void setDefaultTypefaceChalk() { setTypeface(mChalk); } /*public myTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); }*/ public void setHTML(String htmltext) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // Nougat API 24 setText(Html.fromHtml(htmltext, Html.FROM_HTML_MODE_LEGACY, null, new TypefaceTagHandler())); } else { setText(Html.fromHtml(htmltext, null, new TypefaceTagHandler())); } } @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); } @Override public Bitmap getDrawingCache(boolean autoScale) { return super.getDrawingCache(autoScale); } @Override public void draw(Canvas canvas) { super.draw(canvas); } // http://.com/questions/4044509/android-how-to-use-the-html-taghandler private static class TypefaceTagHandler implements Html.TagHandler { private void ProcessAlignment(Layout.Alignment align, boolean opening, Editable output) { int len = output.length(); if (opening) { output.setSpan(new AlignmentSpan.Standard(align), len, len, Spannable.SPAN_MARK_MARK); } else { Object obj = getLast(output, AlignmentSpan.Standard.class); int where = output.getSpanStart(obj); output.removeSpan(obj); if (where != len) { output.setSpan(new AlignmentSpan.Standard(align), where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } } } private void ProcessTypefaceTag(Typeface tf, boolean opening, Editable output) { int len = output.length(); if (opening) { output.setSpan(new CustomTypefaceSpan("", tf), len, len, Spannable.SPAN_MARK_MARK); } else { Object obj = getLast(output, CustomTypefaceSpan.class); int where = output.getSpanStart(obj); output.removeSpan(obj); if (where != len) { output.setSpan(new CustomTypefaceSpan("", tf), where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } } } private void ProcessScaleTag(float scalefactor, boolean opening, Editable output) { int len = output.length(); if (opening) { output.setSpan(new RelativeSizeSpan(scalefactor), len, len, Spannable.SPAN_MARK_MARK); } else { Object obj = getLast(output, RelativeSizeSpan.class); int where = output.getSpanStart(obj); output.removeSpan(obj); if (where != len) { output.setSpan(new RelativeSizeSpan(scalefactor), where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } } } private void ProcessBox(int colour, boolean opening, Editable output) { int len = output.length(); if (opening) { output.setSpan(new BackgroundColorSpan(colour), len, len, Spannable.SPAN_MARK_MARK); } else { Object obj = getLast(output, BackgroundColorSpan.class); int where = output.getSpanStart(obj); output.removeSpan(obj); if (where != len) { output.setSpan(new BackgroundColorSpan(colour), where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } } } private void ProcessTextColour(int colour, boolean opening, Editable output) { int len = output.length(); if (opening) { output.setSpan(new ForegroundColorSpan(colour), len, len, Spannable.SPAN_MARK_MARK); } else { Object obj = getLast(output, ForegroundColorSpan.class); int where = output.getSpanStart(obj); output.removeSpan(obj); if (where != len) { output.setSpan(new ForegroundColorSpan(colour), where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } } } final HashMap<String, String> attributes = new HashMap<>(); @Override public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) { String Attr = ""; //if (!opening) attributes.clear(); processAttributes(xmlReader); if ("txt".equalsIgnoreCase(tag)) { Attr = attributes.get("clr"); System.out.println("clr Attr: " + Attr + ", opening: " + opening); if (Attr == null || Attr.isEmpty() || "black".equalsIgnoreCase(Attr) || Attr.charAt(0) == ''k'') { System.out.println("did black, opening: " + opening); ProcessTextColour(parseColor("#000000"), opening, output); } else { if (Attr.equalsIgnoreCase("g")) { ProcessTextColour(parseColor("#b2b3b3"), opening, output); } else { System.out.println("did colour, opening: " + opening); ProcessTextColour(parseColor(Attr), opening, output); } } return; } if ("box".equalsIgnoreCase(tag)) { ProcessBox(parseColor("#d7d6d5"), opening, output); return; } if ("scl".equalsIgnoreCase(tag)) { Attr = attributes.get("fac"); System.out.println("scl Attr: " + Attr); if (Attr != null && !Attr.isEmpty()) { ProcessScaleTag(parseFloat(Attr), opening, output); } return; } if ("left".equalsIgnoreCase(tag)) { ProcessAlignment(Layout.Alignment.ALIGN_NORMAL, opening, output); return; } if ("centre".equalsIgnoreCase(tag)) { ProcessAlignment(Layout.Alignment.ALIGN_CENTER, opening, output); return; } if ("right".equalsIgnoreCase(tag)) { ProcessAlignment(Layout.Alignment.ALIGN_OPPOSITE, opening, output); return; } if ("logo".equalsIgnoreCase(tag)) { ProcessTypefaceTag(mLogo, opening, output); return; } if ("gamz".equalsIgnoreCase(tag)) { ProcessTypefaceTag(mGAMZ, opening, output); return; } if ("chalk".equalsIgnoreCase(tag)) { System.out.println("chalk " + (opening ? "opening" : "closing")); ProcessTypefaceTag(mChalk, opening, output); return; } } private Object getLast(Editable text, Class kind) { Object[] objs = text.getSpans(0, text.length(), kind); if (objs.length == 0) { return null; } else { for (int i = objs.length - 1; i >= 0; --i) { if (text.getSpanFlags(objs[i]) == Spannable.SPAN_MARK_MARK) { return objs[i]; } } return null; } } private void processAttributes(final XMLReader xmlReader) { try { Field elementField = xmlReader.getClass().getDeclaredField("theNewElement"); elementField.setAccessible(true); Object element = elementField.get(xmlReader); Field attsField = element.getClass().getDeclaredField("theAtts"); attsField.setAccessible(true); Object atts = attsField.get(element); Field dataField = atts.getClass().getDeclaredField("data"); dataField.setAccessible(true); String[] data = (String[])dataField.get(atts); Field lengthField = atts.getClass().getDeclaredField("length"); lengthField.setAccessible(true); int len = (Integer)lengthField.get(atts); /** * MSH: Look for supported attributes and add to hash map. * This is as tight as things can get :) * The data index is "just" where the keys and values are stored. */ for(int i = 0; i < len; i++) attributes.put(data[i * 5 + 1], data[i * 5 + 4]); } catch (Exception e) { Log.d(TAG, "Exception: " + e); } } } private static class CustomTypefaceSpan extends TypefaceSpan { private final Typeface newType; public CustomTypefaceSpan(String family, Typeface type) { super(family); newType = type; } @Override public void updateDrawState(TextPaint ds) { applyCustomTypeFace(ds, newType); } @Override public void updateMeasureState(TextPaint paint) { applyCustomTypeFace(paint, newType); } private void applyCustomTypeFace(Paint paint, Typeface tf) { int oldStyle; Typeface old = paint.getTypeface(); if (old == null) { oldStyle = 0; } else { oldStyle = old.getStyle(); } int fake = oldStyle & ~tf.getStyle(); if ((fake & Typeface.BOLD) != 0) { paint.setFakeBoldText(true); } if ((fake & Typeface.ITALIC) != 0) { paint.setTextSkewX(-0.25f); } paint.setTypeface(tf); } }

}

El htmlTextView se crea a partir de la actividad con:

protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); theAssetManager = getAssets(); htmlTextView tv = new htmlTextView(this); tv.setDefaultTypefaceSouvenir(); tv.setTextColor(BLACK); tv.setBackgroundColor(0xfff0f0f0); tv.setPadding(4, 4, 4, 4); tv.setTextSize(30); tv.setMovementMethod(new ScrollingMovementMethod()); tv.setHTML(getString(R.string.htmljumblies)); //tv.setHTML(getString(R.string.htmltest)); RelativeLayout rl = (RelativeLayout) findViewById(R.id.rl); rl.addView(tv); }

y htmljumblies se define en strings.xml como a continuación. Esta versión particular bloqueará la aplicación, pero si las primeras etiquetas <centre> , </centre> se eliminan de las líneas 7 y 9, ¿aparecerán los Jumblies centralizados? Confuso y frustrante! Guárdalos y quita las etiquetas <centre> , </centre> rodean a los Jumblies y no pasa nada. ¡La línea en el encabezado no está alineada centralmente!

<string name="htmljumblies"> <![CDATA[&DoubleLongRightArrow;<logo><scl fac="1.1"><font color="#e5053a">GAMZ</font></scl></logo> <chalk><scl fac="1.8"> SWAP </scl></chalk> <scl fac="1.00">Set <b>1</b>, Game <b>1</b></scl> <br> <centre> <gamz><font color="#e5053a"><scl fac="1.50">a</scl></font><scl fac="0.90">(9)</scl></gamz>, <gamz><font color="#00a3dd"><scl fac="1.50">e</scl></font><scl fac="0.90">(8)</scl></gamz>, <gamz><font color="#fba311"><scl fac="1.50">i</scl></font><scl fac="0.90">(8)</scl></gamz>, <gamz><font color="#bc5e1e"><scl fac="1.50">o</scl></font><scl fac="0.90">(8)</scl></gamz>, <gamz><font color="#bf30b5"><scl fac="1.50">u</scl></font><scl fac="0.90">(9)</scl></gamz> </centre> <br> This is an example of my custom <b>htmlTextView</b> drawn from HTML format text with custom tags to use custom fonts, colouring typeface sizing and highlight boxes. The default font is <b><i>Souvenir</i></b>, but 3 other fonts are used:<br> The <font color="#e5053a"><b><logo><scl fac="1.1">GAMZ</scl></logo></b></font> <font color="#000080"><gamz><scl fac="0.8"><box>letter</box> <box>fonts</box><sc></gamz></font> and <chalk><scl fac="1.8">swapfix</scl></chalk>, essentially <chalk><scl fac="0.9">Staccato 555</scl></chalk>, as used in the words <chalk><scl fac="1.2">SWAP</scl></chalk> and <chalk><scl fac="1.2">FIX</scl></chalk> on the <font color="#e5053a"><b><logo><scl fac="1.1">GAMZ</scl></logo></b></font> boxes. <br> <centre> <scl fac="2"><box><b> <u>The Jumblies</u> </b></box></scl><br> <font color="#0000ff"> They went to sea in a Sieve, they did,<br> In a Sieve they went to sea:<br> In spite of all their friends could say,<br> On a winter/'s morn, on a stormy day,<br> In a Sieve they went to sea!<br> And when the Sieve turned round and round,<br> And every one cried, /'You/'ll all be drowned!/'<br> They called aloud, /'Our Sieve ain/'t big,<br> But we don/'t care a button! we don/'t care a fig!<br> In a Sieve we/'ll go to sea!/'<br> Far and few, far and few,<br> Are the lands where the Jumblies live;<br> Their heads are green, and their hands are blue,<br> And they went to sea in a Sieve.<br> <br> They sailed away in a Sieve, they did,<br> In a Sieve they sailed so fast,<br> With only a beautiful pea-green veil<br> Tied with a riband by way of a sail,<br> To a small tobacco-pipe mast;<br> And every one said, who saw them go,<br> /'O won/'t they be soon upset, you know!<br> For the sky is dark, and the voyage is long,<br> And happen what may, it/'s extremely wrong<br> In a Sieve to sail so fast!/'<br> Far and few, far and few,<br> Are the lands where the Jumblies live;<br> Their heads are green, and their hands are blue,<br> And they went to sea in a Sieve.<br> <br> The water it soon came in, it did,<br> The water it soon came in;<br> So to keep them dry, they wrapped their feet<br> In a pinky paper all folded neat,<br> And they fastened it down with a pin.<br> And they passed the night in a crockery-jar,<br> And each of them said, /'How wise we are!<br> Though the sky be dark, and the voyage be long,<br> Yet we never can think we were rash or wrong,<br> While round in our Sieve we spin!/'<br> Far and few, far and few,<br> Are the lands where the Jumblies live;<br> Their heads are green, and their hands are blue,<br> And they went to sea in a Sieve.<br> <br> And all night long they sailed away;<br> And when the sun went down,<br> They whistled and warbled a moony song<br> To the echoing sound of a coppery gong,<br> In the shade of the mountains brown.<br> /'O Timballo! How happy we are,<br> When we live in a Sieve and a crockery-jar,<br> And all night long in the moonlight pale,<br> We sail away with a pea-green sail,<br> In the shade of the mountains brown!/'<br> Far and few, far and few,<br> Are the lands where the Jumblies live;<br> Their heads are green, and their hands are blue,<br> And they went to sea in a Sieve.<br> <br> They sailed to the Western Sea, they did,<br> To a land all covered with trees,<br> And they bought an Owl, and a useful Cart,<br> And a pound of Rice, and a Cranberry Tart,<br> And a hive of silvery Bees.<br> And they bought a Pig, and some green Jack-daws,<br> And a lovely Monkey with lollipop paws,<br> And forty bottles of Ring-Bo-Ree,<br> And no end of Stilton Cheese.<br> Far and few, far and few,<br> Are the lands where the Jumblies live;<br> Their heads are green, and their hands are blue,<br> And they went to sea in a Sieve.<br> <br> And in twenty years they all came back,<br> In twenty years or more,<br> And every one said, /'How tall they/'ve grown!<br> For they/'ve been to the Lakes, and the Torrible Zone,<br> And the hills of the Chankly Bore!/'<br> And they drank their health, and gave them a feast<br> Of dumplings made of beautiful yeast;<br> And every one said, /'If we only live,<br> We too will go to sea in a Sieve,---<br> To the hills of the Chankly Bore!/'<br> Far and few, far and few,<br> Are the lands where the Jumblies live;<br> Their heads are green, and their hands are blue,<br> And they went to sea in a Sieve.</centre></font> ]]> </string>


Entonces, finalmente lo descubrí por mi cuenta.

public class MyHtmlTagHandler implements TagHandler { public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) { if(tag.equalsIgnoreCase("strike") || tag.equals("s")) { processStrike(opening, output); } } private void processStrike(boolean opening, Editable output) { int len = output.length(); if(opening) { output.setSpan(new StrikethroughSpan(), len, len, Spannable.SPAN_MARK_MARK); } else { Object obj = getLast(output, StrikethroughSpan.class); int where = output.getSpanStart(obj); output.removeSpan(obj); if (where != len) { output.setSpan(new StrikethroughSpan(), where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } } } private Object getLast(Editable text, Class kind) { Object[] objs = text.getSpans(0, text.length(), kind); if (objs.length == 0) { return null; } else { for(int i = objs.length;i>0;i--) { if(text.getSpanFlags(objs[i-1]) == Spannable.SPAN_MARK_MARK) { return objs[i-1]; } } return null; } } }

Y para su TextView puede llamarlo así:

myTextView.setText (Html.fromHtml(text.toString(), null, new MyHtmlTagHandler()));

si alguien lo necesita

Aclamaciones


Hemos estado desarrollando internamente esta biblioteca https://github.com/square1-io/rich-text-android desde hace un tiempo y la usamos en una serie de aplicaciones de noticias de contenido intensivo.

La biblioteca puede analizar la mayoría de las etiquetas html comunes, incluyendo video e img con descarga remota de imágenes. Una vista personalizada, RichTextView se puede utilizar como un reemplazo de TextView para mostrar el contenido analizado.

Recientemente lo hemos lanzado públicamente, por lo que el documento aún está incompleto; sin embargo, el ejemplo proporcionado debería ser fácil de seguir para ver si se ajusta a sus necesidades.


Tomé la respuesta de janoliver y presenté mi versión que intenta dar soporte a más opciones

String text = ""; // HTML text to convert // Preprocessing phase to set up for HTML.fromHtml(...) text = text.replaceAll("<span style=/"(?:color: (#[a-fA-F//d]{6})?; )?(?:font-family: (.*?); )?(?:font-size: (.*?);)? ?/">(.*?)</span>", "<font color=/"$1/" face=/"$2/" size=/"$3/">$4</font>"); text = text.replaceAll("(?<=<font color=/"#[a-fA-F0-9]{6}/" )face=/"''(.*?)'', .*?/"", "face=/"$1/""); text = text.replaceAll("(?<=<font color=/"#[a-fA-F0-9]{6}/" )(face=/".*?/" )size=/"xx-small/"", "$1size=/"1/""); text = text.replaceAll("(?<=<font color=/"#[a-fA-F0-9]{6}/" )(face=/".*?/" )size=/"x-small/"", "$1size=/"2/""); text = text.replaceAll("(?<=<font color=/"#[a-fA-F0-9]{6}/" )(face=/".*?/" )size=/"small/"", "$1size=/"3/""); text = text.replaceAll("(?<=<font color=/"#[a-fA-F0-9]{6}/" )(face=/".*?/" )size=/"medium/"", "$1size=/"4/""); text = text.replaceAll("(?<=<font color=/"#[a-fA-F0-9]{6}/" )(face=/".*?/" )size=/"large/"", "$1size=/"5/""); text = text.replaceAll("(?<=<font color=/"#[a-fA-F0-9]{6}/" )(face=/".*?/" )size=/"x-large/"", "$1size=/"6/""); text = text.replaceAll("(?<=<font color=/"#[a-fA-F0-9]{6}/" )(face=/".*?/" )size=/"xx-large/"", "$1size=/"7/""); text = text.replaceAll("<strong>(.*?)</strong>", "<_em>$1</_em>"); // we use strong for bold-face text = text.replaceAll("<em>(.*?)</em>", "<strong>$1</strong>"); // and em for italics text = text.replaceAll("<_em>(.*?)</_em>", "<em>$1</em>"); // but Android uses em for bold-face text = text.replaceAll("<span style=/"background-color: #([a-fA-F0-9]{6}).*?>(.*?)</span>", "<_$1>$2</_$1>"); text_view.setText(Html.fromHtml(text, null, new Html.TagHandler() { private List<Object> _format_stack = new LinkedList<Object>(); @Override public void handleTag(boolean open_tag, String tag, Editable output, XMLReader _) { if (tag.startsWith("ul")) processBullet(open_tag, output); else if (tag.matches(".[a-fA-F0-9]{6}")) processBackgroundColor(open_tag, output, tag.substring(1)); } private void processBullet(boolean open_tag, Editable output) { final int length = output.length(); if (open_tag) { final Object format = new BulletSpan(BulletSpan.STANDARD_GAP_WIDTH); _format_stack.add(format); output.setSpan(format, length, length, Spanned.SPAN_MARK_MARK); } else { applySpan(output, length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } } private void processBackgroundColor(boolean open_tag, Editable output, String color) { final int length = output.length(); if (open_tag) { final Object format = new BackgroundColorSpan(Color.parseColor(''#'' + color)); _format_stack.add(format); output.setSpan(format, length, length, Spanned.SPAN_MARK_MARK); } else { applySpan(output, length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } } private Object getLast(Editable text, Class kind) { @SuppressWarnings("unchecked") final Object[] spans = text.getSpans(0, text.length(), kind); if (spans.length != 0) for (int i = spans.length; i > 0; i--) if (text.getSpanFlags(spans[i-1]) == Spannable.SPAN_MARK_MARK) return spans[i-1]; return null; } private void applySpan(Editable output, int length, int flags) { if (_format_stack.isEmpty()) return; final Object format = _format_stack.remove(0); final Object span = getLast(output, format.getClass()); final int where = output.getSpanStart(span); output.removeSpan(span); if (where != length) output.setSpan(format, where, length, flags); } }));

Esto parece obtener las viñetas, el color de primer plano y el color de fondo. Puede funcionar para la fuente de la fuente, pero es posible que deba proporcionar las fuentes, ya que no parece que Android admita fuentes que no sean Droid / Roboto.

Esto es más una prueba de concepto, es posible que desee convertir la expresión regular en procesamiento de String , ya que Regex no admite la combinación del preprocesamiento de ninguna manera, lo que significa que esto requiere muchos pases sobre la String . Esto tampoco parece cambiar el tamaño de la fuente, he intentado definirlo como "16sp", "medium" o "4" sin ver los cambios. Si alguien ha conseguido tamaños para trabajar, compartir la mente?

En este momento me gustaría poder agregar compatibilidad de lista numerada / ordenada a esto, es decir,

  1. ít
  2. ít
  3. ít

NOTA: Para las personas que comienzan con algo de esto, parece que la "etiqueta" que se da a handleTag(...) es solo el nombre de la etiqueta (como "span"), y no contiene ninguno de los atributos asignado en la etiqueta (como si tuviera "), puede ver mi escapatoria alrededor de este para el color de fondo.