2016-02-26 4 views
1

У меня есть некоторое содержимое HTML с чем-то вродеКак отображать горизонтальные линии между текстом содержимого 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> 

Теперь я пытаюсь для отображения содержимого HTML на одном TextView. Фрагмент кода приведен ниже

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) но не горизонтальные линии. Может ли кто-нибудь вести меня, как отображать горизонтальную линию или поддерживать тег <hr> в методе Html.fromHtml() с помощью HtmlTagHandler, а также любой другой подход.

Ожидаемый результат, например, снимок экрана.

enter image description here

Все работает отлично ожидать, что горизонтальные линии. Может кто-нибудь помочь в этом вопросе.

+1

Не используйте TextView. Если у вас есть HTML этот комплекс, используйте WebView. –

+0

Здесь у нас есть функции редактирования контента, такие как выделение текста, прокрутка, подчеркивание, рисование и т. Д. Вот почему вы выбираете Textview вместо webview. – sandeepmaaram

+0

TextView на самом деле не предназначен для этого. Вы должны использовать пользовательский вид, вы подталкиваете его и за пределы его предназначения, и если ваша модель поддержки ваших данных - это строка, которую вы html-ified, вы ужасно неэффективны. Но если вам абсолютно необходимо попытаться это сделать, ваш лучший выбор - это обычная возможность. –

ответ

2

Вот как я решил ваш вопрос. Это не очень элегантное решение, но оно работает.

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; 
    } 
} 
Смежные вопросы