Android中Textview显示带html文本-------【HTML标签】

现在网络的繁盛时代,光文字是不能满足人们的胃口的,图片,flash,音频,视频就成为浏览网页的主流显示,在手机上也一样。在手机上显示从网络端获取的数据显示,大家很自然的想起两种方式,一种就是webview,一种就是TextView。当然webView直接显示html页面就行了,我主要说的TextView显示html内容。

首先,说下TextView到底支持那些标签呢,通过对源码的查看,发现Textview可以解析一部分html标签,如:



























大家想究其根本可以查看android.text.Html源码,其中有一段这样写:

 


private  void handleStartTag(String tag, Attributes attributes) {
         if (tag.equalsIgnoreCase("br")) {
             //  We don't need to handle this. TagSoup will ensure that there's a 
 for each 

            
//  so we can safely emite the linebreaks when we handle the close tag.
        }  else  if (tag.equalsIgnoreCase("p")) {
            handleP(mSpannableStringBuilder);
        }  else  if (tag.equalsIgnoreCase("div")) {
            handleP(mSpannableStringBuilder);
        }  else  if (tag.equalsIgnoreCase("em")) {
            start(mSpannableStringBuilder,  new Bold());
        }  else  if (tag.equalsIgnoreCase("b")) {
            start(mSpannableStringBuilder,  new Bold());
        }  else  if (tag.equalsIgnoreCase("strong")) {
            start(mSpannableStringBuilder,  new Italic());
        }  else  if (tag.equalsIgnoreCase("cite")) {
            start(mSpannableStringBuilder,  new Italic());
        }  else  if (tag.equalsIgnoreCase("dfn")) {
            start(mSpannableStringBuilder,  new Italic());
        }  else  if (tag.equalsIgnoreCase("i")) {
            start(mSpannableStringBuilder,  new Italic());
        }  else  if (tag.equalsIgnoreCase("big")) {
            start(mSpannableStringBuilder,  new Big());
        }  else  if (tag.equalsIgnoreCase("small")) {
            start(mSpannableStringBuilder,  new Small());
        }  else  if (tag.equalsIgnoreCase("font")) {
            startFont(mSpannableStringBuilder, attributes);
        }  else  if (tag.equalsIgnoreCase("blockquote")) {
            handleP(mSpannableStringBuilder);
            start(mSpannableStringBuilder,  new Blockquote());
        }  else  if (tag.equalsIgnoreCase("tt")) {
            start(mSpannableStringBuilder,  new Monospace());
        }  else  if (tag.equalsIgnoreCase("a")) {
            startA(mSpannableStringBuilder, attributes);
        }  else  if (tag.equalsIgnoreCase("u")) {
            start(mSpannableStringBuilder,  new Underline());
        }  else  if (tag.equalsIgnoreCase("sup")) {
            start(mSpannableStringBuilder,  new Super());
        }  else  if (tag.equalsIgnoreCase("sub")) {
            start(mSpannableStringBuilder,  new Sub());
        }  else  if (tag.length() == 2 &&
                   Character.toLowerCase(tag.charAt(0)) == 'h' &&
                   tag.charAt(1) >= '1' && tag.charAt(1) <= '6') {
            handleP(mSpannableStringBuilder);
            start(mSpannableStringBuilder,  new Header(tag.charAt(1) - '1'));
        }  else  if (tag.equalsIgnoreCase("img")) {
            startImg(mSpannableStringBuilder, attributes, mImageGetter);
        }  else  if (mTagHandler !=  null) {
            mTagHandler.handleTag( true, tag, mSpannableStringBuilder, mReader);
        }
    }

     private  void handleEndTag(String tag) {
         if (tag.equalsIgnoreCase("br")) {
            handleBr(mSpannableStringBuilder);
        }  else  if (tag.equalsIgnoreCase("p")) {
            handleP(mSpannableStringBuilder);
        }  else  if (tag.equalsIgnoreCase("div")) {
            handleP(mSpannableStringBuilder);
        }  else  if (tag.equalsIgnoreCase("em")) {
            end(mSpannableStringBuilder, Bold. classnew StyleSpan(Typeface.BOLD));
        }  else  if (tag.equalsIgnoreCase("b")) {
            end(mSpannableStringBuilder, Bold. classnew StyleSpan(Typeface.BOLD));
        }  else  if (tag.equalsIgnoreCase("strong")) {
            end(mSpannableStringBuilder, Italic. classnew StyleSpan(Typeface.ITALIC));
        }  else  if (tag.equalsIgnoreCase("cite")) {
            end(mSpannableStringBuilder, Italic. classnew StyleSpan(Typeface.ITALIC));
        }  else  if (tag.equalsIgnoreCase("dfn")) {
            end(mSpannableStringBuilder, Italic. classnew StyleSpan(Typeface.ITALIC));
        }  else  if (tag.equalsIgnoreCase("i")) {
            end(mSpannableStringBuilder, Italic. classnew StyleSpan(Typeface.ITALIC));
        }  else  if (tag.equalsIgnoreCase("big")) {
            end(mSpannableStringBuilder, Big. classnew RelativeSizeSpan(1.25f));
        }  else  if (tag.equalsIgnoreCase("small")) {
            end(mSpannableStringBuilder, Small. classnew RelativeSizeSpan(0.8f));
        }  else  if (tag.equalsIgnoreCase("font")) {
            endFont(mSpannableStringBuilder);
        }  else  if (tag.equalsIgnoreCase("blockquote")) {
            handleP(mSpannableStringBuilder);
            end(mSpannableStringBuilder, Blockquote. classnew QuoteSpan());
        }  else  if (tag.equalsIgnoreCase("tt")) {
            end(mSpannableStringBuilder, Monospace. class,
                     new TypefaceSpan("monospace"));
        }  else  if (tag.equalsIgnoreCase("a")) {
            endA(mSpannableStringBuilder);
        }  else  if (tag.equalsIgnoreCase("u")) {
            end(mSpannableStringBuilder, Underline. classnew UnderlineSpan());
        }  else  if (tag.equalsIgnoreCase("sup")) {
            end(mSpannableStringBuilder, Super. classnew SuperscriptSpan());
        }  else  if (tag.equalsIgnoreCase("sub")) {
            end(mSpannableStringBuilder, Sub. classnew SubscriptSpan());
        }  else  if (tag.length() == 2 &&
                Character.toLowerCase(tag.charAt(0)) == 'h' &&
                tag.charAt(1) >= '1' && tag.charAt(1) <= '6') {
            handleP(mSpannableStringBuilder);
            endHeader(mSpannableStringBuilder);
        }  else  if (mTagHandler !=  null) {
            mTagHandler.handleTag( false, tag, mSpannableStringBuilder, mReader);
        }
    }

通过源码可以看到,除了默认的一些标签,其还支持自定义标签;看下面代码:

else  if (mTagHandler !=  null) {
            mTagHandler.handleTag( false, tag, mSpannableStringBuilder, mReader);
        }

系统会调用mTagHandler的handleTag方法。所以,我们可以实现此接口,来解析自己定义的标签类型。

具体的,自己可以看一下下面实例:

 


package com.mxgsa.tvimg;

import org.xml.sax.XMLReader;

import android.content.Context;
import android.content.Intent;
import android.text.Editable;
import android.text.Html.TagHandler;
import android.text.Spanned;
import android.text.style.ClickableSpan;
import android.view.View;
import android.view.View.OnClickListener;

public  class MxgsaTagHandler  implements TagHandler{
     private  int sIndex = 0;  
     private   int eIndex=0;
     private  final Context mContext;
    
     public MxgsaTagHandler(Context context){
        mContext=context;
    }
    
     public  void handleTag( boolean opening, String tag, Editable output, XMLReader xmlReader) {
         //  TODO Auto-generated method stub
         if (tag.toLowerCase().equals("mxgsa")) {
             if (opening) {
                sIndex=output.length();
            } else {
                eIndex=output.length();
                output.setSpan( new MxgsaSpan(), sIndex, eIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            }
        }
    }
     private  class MxgsaSpan  extends ClickableSpan  implements OnClickListener{
        @Override
         public  void onClick(View widget) {
             //  TODO Auto-generated method stub
            
// 具体代码,可以是跳转页面,可以是弹出对话框,下面是跳转页面
            mContext.startActivity( new Intent(mContext,MainActivity. class));
        }
    }

}

调用页面:

 

package com.mxgsa.tvimg;

import android.app.Activity;
import android.os.Bundle;
import android.text.Html;
import android.text.method.LinkMovementMethod;
import android.widget.TextView;

public  class MxgsaActivity  extends Activity{

     private TextView tView;

    @Override
     public  void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
        setContentView(R.layout.mxgsa_activity);
        findControl();
        setData();
    }

     private  void findControl() {
        tView = (TextView) findViewById(R.id.tvImage);
    }

     private  void setData() {
         //  TODO Auto-generated method stub
         final String sText = "测试自定义标签:

测试自定义标签

";
        tView.setText(Html.fromHtml(sText,  nullnew MxgsaTagHandler( this)));
        tView.setClickable( true);
        tView.setMovementMethod(LinkMovementMethod.getInstance());
    }

    

}

下篇将会讲带图片的html文本显示!


Textview可以显示基本的HTML标签,如果不知道那些标签,可以查看Android中Textview显示带html文本一-------【HTML标签】!

下面着重说一下Textview显示“img”标签,也许看到这里,大家都会想到就是构建ImageGetter,重载一下其 public Drawable getDrawable(String source)方法,获取该路径的图片。

例如:

final Html.ImageGetter imageGetter = new Html.ImageGetter() {
        public Drawable getDrawable(String source) {
            return drawable;
        };

    };

下面来说下public Drawable getDrawable(String source)这个方法,source就是图片路径!

例如:

final String sText = "测试图片信息:
"; tView.setText(Html.fromHtml(sText, imageGetter, null));

则source就是img的src的值,既是:http://pic004.cnblogs.com/news/201211/20121108_091749_1.jpg这个图片路径

当然这个 这个路径既可以是网络图片,也可以本地图片,项目资源图片

例如:本地图片   项目资源图片

但是不同的路径,ImageGetter的重载处理方法都不一样,下面来一一介绍各种的处理方式.

第一种:本地图片

复制代码
final String sText2 = "测试图片信息:";
tView.setText(Html.fromHtml(sText2, imageGetter, null));

final Html.ImageGetter imageGetter = new Html.ImageGetter() {

    public Drawable getDrawable(String source) {
        Drawable drawable=null;
    drawable=Drawable.createFromPath(source);
        drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
    return drawable;  };
}
复制代码

第二种:项目资源图片

复制代码
final String sText1 = "测试图片信息:";tView.setText(Html.fromHtml(sText1, imageGetter, null));

final Html.ImageGetter imageGetter = new Html.ImageGetter() {

    public Drawable getDrawable(String source) {
        Drawable drawable=null;
    int rId=Integer.parseInt(source);
    drawable=getResources().getDrawable(rId);
    drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
    return drawable;    };
}
复制代码

第三种:网络图片

复制代码
final String sText = "测试图片信息:
"; tView.setText(Html.fromHtml(sText, imageGetter, null)); final Html.ImageGetter imageGetter = new Html.ImageGetter() { public Drawable getDrawable(String source) { Drawable drawable=null; URL url; try { url = new URL(source); drawable = Drawable.createFromStream(url.openStream(), ""); } catch (Exception e) { e.printStackTrace(); return null; } drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); return drawable;     }; }
复制代码

 

通过这三个方式,可以看出,不同的图片路径,得到图片的处理方式不同,大家也能一目了然的看出来ImageGetter是干什么的了,就是得到img中src所需的图片!

提醒一点:获取图片以后,一定要设置图片的边界,界线,即:drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());,不然获取图片后,Textview不能显示图片。

通过以上三种方式,是能可以显示出来图片,但是我发现了一个问题,就是第三种,显示网络图片,我用android2.3的系统,可以显示图片出来,并且如果图片比较大,应用会卡的现象,肯定是因为使用主线程去获取网络图片造成的,但如果我用android4.0以上的系统运行,则不能显示图片,只显示小方框。

究其原因,是在4.0的系统上执行的时候报错了,异常是:android.os.NetworkOnMainThreadException 经过查文档,原来是4.0系统不允许主线程(UI线程)访问网络,因此导致了其异常。说白了就是在主线程上访问网络,会造成主线程挂起,系统不允许使用了。

具体处理方式看下篇:Android中Textview显示带html文本三-------【Textview显示网络图片】


上篇遗留下来一个问题就是:显示网络图片,我用android2.3的系统,可以显示图片出来,并且如果图片比较大,应用会卡的现象,肯定是因为使用主线程去获取网络图片造成的,但如果我用android4.0以上的系统运行,则不能显示图片,只显示小方框。

究其原因,是在4.0的系统上执行的时候报错了,异常是:android.os.NetworkOnMainThreadException 经过查文档,原来是4.0系统不允许主线程(UI线程)访问网络,因此导致了其异常。说白了就是在主线程上访问网络,会造成主线程挂起,系统不允许使用了。

看到Android4.0不允许主线程(UI线程)访问网络,立马脑子就想起来 ,不能用主线程访问,可以开另外一个线程,把图片下到本地sd卡中,之后在赋值到TextView里面。不急着来代码,我和大家在把这个逻辑在理一下:获取图片路径——异步下载图片——完成下载后重新赋值Textview

想到这里,我就准备自己亲自实践下......于是,我就简单的写了文件下载类DownLoadUtils,有四个事件就是开始下载,下载中(返回进度),完成下载后,下载出错!具体代码就不贴出来了。大家可以自己去写一个,下载文件的代码搜下都有!下载类里面用到了线程和Handler的的使用,下篇我具体讲下这个。

下面是Activity页面处理代码:

View Code

下面来简单的介绍下上面的代码,最重要的就是有两点,就是第一次把sText赋值Textview,在Html.ImageGetter的重载方法里面去判断该图片文件是否已经下载,如果已经下载,就直接读取SD卡里面的图片文件,如上篇所讲的Textview显示本地图片,

//获取本地文件返回Drawable
                drawable=Drawable.createFromPath(fileString);
                //设置图片边界
                drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());

如果没有下载就开启一个下载线程

//启动新线程下载
downLoadUtils.download(source, path+String.valueOf(source.hashCode()));

第二个重点就是监听下载完成事件,完成下载以后,重新给Textview赋值

View Code

这样做了之后,网络图片就可以显示在Textview里面。在网络正常的情况下,如果是相同图片只需要下载一次,这样可以节省了手机的流量。

我还有一种解决方案就是不需要给Textview赋两次值,就是首先解析出来图片路径,然后下载图片,最后赋值给Textview,其实道理是一样的,之前的做法是通过重载方法解析出来图片的路径然后下载图片。只不过是多了一个赋值,没有任何影响。大家有好的思路,也可以介绍下。

转 ---http://www.cnblogs.com/mxgsa/archive/2012/12/20/2823666.html

你可能感兴趣的:(android)