现在网络的繁盛时代,光文字是不能满足人们的胃口的,图片,flash,音频,视频就成为浏览网页的主流显示,在手机上也一样。在手机上显示从网络端获取的数据显示,大家很自然的想起两种方式,一种就是webview,一种就是TextView。当然webView直接显示html页面就行了,我主要说的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.
class,
new StyleSpan(Typeface.BOLD));
}
else
if (tag.equalsIgnoreCase("b")) {
end(mSpannableStringBuilder, Bold.
class,
new StyleSpan(Typeface.BOLD));
}
else
if (tag.equalsIgnoreCase("strong")) {
end(mSpannableStringBuilder, Italic.
class,
new StyleSpan(Typeface.ITALIC));
}
else
if (tag.equalsIgnoreCase("cite")) {
end(mSpannableStringBuilder, Italic.
class,
new StyleSpan(Typeface.ITALIC));
}
else
if (tag.equalsIgnoreCase("dfn")) {
end(mSpannableStringBuilder, Italic.
class,
new StyleSpan(Typeface.ITALIC));
}
else
if (tag.equalsIgnoreCase("i")) {
end(mSpannableStringBuilder, Italic.
class,
new StyleSpan(Typeface.ITALIC));
}
else
if (tag.equalsIgnoreCase("big")) {
end(mSpannableStringBuilder, Big.
class,
new RelativeSizeSpan(1.25f));
}
else
if (tag.equalsIgnoreCase("small")) {
end(mSpannableStringBuilder, Small.
class,
new RelativeSizeSpan(0.8f));
}
else
if (tag.equalsIgnoreCase("font")) {
endFont(mSpannableStringBuilder);
}
else
if (tag.equalsIgnoreCase("blockquote")) {
handleP(mSpannableStringBuilder);
end(mSpannableStringBuilder, Blockquote.
class,
new 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.
class,
new UnderlineSpan());
}
else
if (tag.equalsIgnoreCase("sup")) {
end(mSpannableStringBuilder, Super.
class,
new SuperscriptSpan());
}
else
if (tag.equalsIgnoreCase("sub")) {
end(mSpannableStringBuilder, Sub.
class,
new 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,
null,
new 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