webview 和 textview 解析html 之间进行比较

其中使用最多的两种方式分别为:

  • 使用 Android 提供的 WebView 控件。
  • 通过将 HTML 内容转化为 Spanned 格式在 TextView 中进行显示。

虽然这两种方式都可以显示 HTML 内容,但是其对用户交互的响应方式却有较大的不同。本文通过两个实例来说明:1) 这两种方式的基本使用方式。2) 这两种方式的交互如何实现。3) 通过对比阐述这两种方式各适合于哪些应用场景。

背景

对于有显示 HTML 内容的 Android 应用来说,使用 Android SDK WebView 来显示是最简单的方式,但是并不是所有的场景下都适合使用 WebView 来显示 HTML 内容,例如,如果应用要显示的内容只是一部分 HTML 片段,就可以利用 TextView 来进行显示,并且效率较高。

另外,使用 WebView 或者 TextView 来显示 HTML 内容,其交互的实现方式有较大的区别,以在 HTML 内容中的图片点击事件为例,在 WebView 模式下,开发人员需要通过注入 JavaScript 代码来进行点击事件的响应,Android 系统提供了 WebView 中 JavaScript 网页脚本调用 Java 类方法的机制;而在 TextView 中,图片会被解析为 ImageSpan,通过在 ImageSpan 上注册点击事件来响应。 本文后两节分别通过实例来讲述这两种方式的使用方法,以及其用户交互方式。

通过 WebView 显示 HTML 内容及用户交互

Android 中的 WebView 组件经常用来加载类似 HTML 这样的格式化文本,它强大的注入功能能轻易实现 JavaScript 代码与 Java 代码之间的交互。本文通过一个详细的例子一步一步教你学会使用并理解 WebView。

在 WebView 中显示 HTML 内容

Android 的 WebView 组件使用非常简单,可以使用 loadUrl()加载一个 URL 地址,也可以使用 loadData()或 loadDataWithBaseURL()加载一段 HTML 代码片段。loadData()不能加载图片内容,所以在本例中选择使用可以加载图片内容,并获得更强大 Web 支持的 loadDataWithBaseURL()来显示文字与图片内容,如清单 1中所示。

清单 1. loadDataWithBaseURL()方法定义

public void loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, String historyUrl) {
  checkThread();
  if (DebugFlags.TRACE_API) Log.d(LOGTAG, "loadDataWithBaseURL=" + baseUrl);
 mProvider.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl);
  }
  • baseUrl,相对路径,在本例中 HTML 文本内用到的所有资源文件,不论是图片还是其它的 JavaScript 文件或者 CSS 文件,其路径都是相对于这个参数的。
  • data, 以 encoding 格式编码的 HTML 内容字符串。
  • mimeType,数据的 MIME 类型,null 时默认为"text/html"。
  • encoding, 数据编码格式。
  • historyUrl,作为历史条目的 URL,可为 null。

用户交互事件的添加

对 loadDataWithBaseURL()方法传入参数前,对参数 data 字符串做处理:

String data = webContentHandle(DataUtil.getFromAssets(this, "htmlsample/sample.html"));
webview.loadDataWithBaseURL(BASE_PATH + "htmlsample/", data,"text/html", "utf-8", null);

在 webContentHandle()方法中为 HTML 内容增加 JavaScript 代码以实现两张图片分别添加 HTML 的 onclick 事件,并使图片能够全屏显示,还支持按键退出全屏显示。

本例在实现图片点击后全屏显示时,使用了两种方法,分别作用于两张图片。

第一张图片响应点击,会创建新的 Activity 来全屏显示图片(跳出 WebView,JavaScript 调用 Java),按退出键则退出全屏显示(Java 方法)。

第二张图片响应点击,利用纯 HTML 技术实现全屏显示(未跳出 WebView),按退出键退出全屏显示(Java 调用 JavaScript)。

JavaScript 调用 Java

清单 2. JavaScriptInterface 类定义

public class JavaScriptInterface {
	......
	@JavascriptInterface
	public void closeApp() {
	activity.finish();
	}	
	@JavascriptInterface
	public void showImage(final String imagePath) {
	Intent i = new Intent(activity, ImageActivity.class);
	Bundle bundle = new Bundle();
	bundle.putString(IMAGE_PATH_KEY, imagePath);
	i.putExtras(bundle);
	activity.startActivity(i);
	}
}

所有被 JavaScript 代码调用的 Java 方法都必须被预定义在一个类里如清单 2 所示,本例有两个被调用的方法即 closeApp()和 showImage(),被调用的方法可以带参也可以不带参,值得注意的是参数类型单一。

webview.getSettings().setJavaScriptEnabled(true);
webview.addJavascriptInterface(new JavaScriptInterface(this),"JSInterface");

要实现 WebView 与 JavaScript 的交互必须先打开 WebView 对 JavaScript 的支持功能。JavaScript 调用 Java 是通过 addJavascriptInterface()方法来实现的,该方法的第一个参数是一个包含被 JavaScript 代码调用的 Java 方法的对象,本例中就是类 JavaScriptInterface 的实例,第二个参数是该对象在 JavaScript 代码中的变量名,注意该变量是位于全局即 window 下的,即可以在 JavaScript 代码中用"window.变量名.方法名"来调用第一个参数中的方法。对于不带参的 Java 方法调用很简单如 closeApp(),但是对于带参的方法如上面的 showImage(),要注意的是参数的拼接,易拼错,且不易检查,详见代码中的 webContentHandle()方法。

Java 调用 JavaScript

清单 3. 对按返回键的处理

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
	webview.loadUrl("javascript: window.backAction();");
	return true;
}
return super.onKeyDown(keyCode, event);
}

Java 调用 JavaScript 用的是 WebView 的 loadUrl()方法,该方法不仅能以最常见的 HTTP 地址作为参数,还可以是 JavaScript 代码。清单 3中方法使得用户一旦按下退出键,就会通知 WebView 容器去触发 JavaScript 代码 window.backAction()的执行。

原始 HTML 文本仅仅是文字加两张图片,没有任何点击事件,经过增加 JavaScript 交互处理后图片有了点击事件,并且还能响应设备上的退出键。

在本例中第一张图片用户交互式体验效果如下图 1。

在本例中第二张图片用户交互式体验效果如下图 2。

图 1

通过WebView或者TextView显示HTML内容的最佳实践和对比

图 2

通过WebView或者TextView显示HTML内容的最佳实践和对比

通过 TextView 显示 HTML 内容及用户交互

Android 中的 TextView 组件常用于显示文本内容,其实它也可以显示 HTML 的内容。简单来讲,这就需要先把 HTML 的内容以字符串的形式获取后,经过 android.text.Html.fromHtml()转化成 Spanned 的格式,然后将其传递到 TextView 的 setText()方法中,这样就可以在 TextView 中显示 HTML 页面的内容了。需要注意的是,并不是所有的 HTML 标签在 TextView 中都是支持的,且官方文档并没有明确的说明支持 HTML 标签列表,通过查看 Android 源代码,可以得到简单的支持列表。

{<br>,< p>,< div align=>,< strong>, <b>, <em>, <cite>, <dfn>, <i>, <big>, <small>, <font size=>,  <font color=>, <blockquote>, <tt>, <a href=>, <u>, <sup>, <sub>, <h1>,<h2>,<h3>,<h4>,<h5>,<h6>, <img src=>, <strike>}

下面的示例来介绍如何在 TextView 中显示一段 HTML 内容,要显示的这段 HTML 内容即包含超链接内容,也包含有图片,示例中也会演示如何在图片上注册点击事件来完成用户交互,工程代码可以再附件中直接下载(附链接)。

在 TextView 中显示 HTML 内容

显示的过程中最主要的过程就是调用 Android.text.Html 类提供的 fromHtml()方法,将一段 HTML 内容转化为 Spanned 对象。

Android.text.Html 类提供的 fromHtml()方法使用如下清单 4

清单 4. fromHtml()方法定义

public static Spanned fromHtml(String source, ImageGetter imageGetter,
	TagHandler tagHandler) {
        ……
HtmlToSpannedConverter converter =
new HtmlToSpannedConverter(source, imageGetter, tagHandler,  parser);
return converter.convert();
}
  • source,就是包含 HTML 内容的字符串。而 Html.ImageGetter 和 Html.TagHandler 是两个接口,提供给开发者继承使用。
  • imageGetter, 如果要显示图片是需要被继承的,重写 getDrawable(String source)方法,用于获取 HTML 里面的图片来显示在 TextView 中。
  • tagHandler,其作用是把 HTML 带标记的文本内容字符串转化成可以显示效果的的 Spanned 字符串 。由于并非所有的 HTML 标签都可以转化,所以在使用时,用户需要自己添加一些必要的标签和处理方法时才会继承使用的。

    在本例中使用 fromHtml()方法之前,要准备好该方法要用的三个参数内容,首先将 HTML 字符串内容准备好,在项目中需要创建两个类 MImageGetter 和 MTagHandler 分别继承于 ImageGetter 和 TagHandler,分别用户图片的获取,和特殊标签的支持。

  • MImageGetter

    继承于 ImageGetter,重写 getDrawable (String source) 方法中从 assets 路径下取出的图片流(这里当然也可以通过网络操作来完成图片流的获取),最后获得可供显示的图片对象,例如 Drawable 对像。由于 Android 设备的异构性,为了有更好的显示效果,通常需要获取屏幕大小,然后调用 drawable.setBounds () 还可以重新设置图片的大小, 最后返回合适大小的图片 Drawable 对象。 由此 Spanned 中的 ImageSpan 就获得了图像被显示在 TextView 中对应位置了。

    TypedValue typedValue = new TypedValue();
    typedValue.density = TypedValue.DENSITY_DEFAULT;
    drawable = Drawable.createFromResourceStream(null, typedValue, is, "src");
    DisplayMetrics dm = c.getResources().getDisplayMetrics();  
    int dwidth = dm.widthPixels-10;//padding left + padding right
    float dheight = (float)drawable.getIntrinsicHeight()*(float)dwidth/(float)drawable.getIntrinsicWidth();
    int dh = (int)(dheight+0.5);
    int wid = dwidth;
    int hei = dh;
    drawable.setBounds(0, 0, wid, hei);DisplayMetrics dm = c.getResources().getDisplayMetrics();
  • MTagHandler

    继承于 TagHandler,重写了 handleTag()方法,为的是支持<ul><ol><dd>和<li>标签,这四个标签是在 formHtml()方法中本身是不支持。如果开发者认为安卓 TagHandler 提供的默认标签解析已经够用,直接在 fromHtml()方法中第三个参数的地方填写 null 既可。

    清单 5. 重写 handleTag()方法

    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) {
    	mListParents.add(tag);
    	} else mListParents.remove(tag);
    } else if (tag.equals("li") && !opening) {
    	 handleListTag(output);
    }
    }
    private void handleListTag(Editable output) {
    ……
     }

    最后,在完成了 MImageGetter、MTagHandler 以后,就可以通过 formHtml()方法将 HTML 内容转化为可供显示的 SpannableString,将 SpannableString 通过 setText 方法放入 TextView 中,就可以显示图文并茂的内容了。

    progressBar.setVisibility(View.GONE);
    text.setText(Html.fromHtml(htmlCont, new MImageGetter(text,MainActivity.this), new MTagHandler()));
    text.setVisibility(View.VISIBLE);

    在本例中用 TextView 展现 HTML 内容效果如下图3。

    图 3

    通过WebView或者TextView显示HTML内容的最佳实践和对比

用户交互事件的添加

在上一步骤,formHtml()方法已经将 HTML 内容中的超链接和图片转义成为 UrlSpan 和 ImageSpan,进而在 TextView 中完成显示。但是此时是没有任何用户交互的,用户只能看到 HTML 的内容,下面介绍如何添加用户交互功能。

要完成用户交互,这里我们需要在 TextView 中还需要调用 textView.setMovementMethod()方法,Android 提供了 LinkMovementMethod 类以实现了对于文本内容中超链接的遍历,并且支持对于超链接的点击事件。所以只要在添加一句 setMovementMethod(LinkMovementMethod.getInstance())就可以使点击 UrlSpan 能够触发打开链接的功能。

但是本例仅使用 setMovementMethod ()方法还不够,为了更好地用户体验,不仅 UrlSpan 能够被触发,ImageSpan 也要求可以跳转到只看到图片的全屏显示的功能。

特此创建一个 LinkMovementMethodExt 类继承于 LinkMovementMethod,在能够触发 UrlSpan 的基础上,也使 IamgeSpan 可以被触发。在 LinkMovementMethodExt 中重写方法 onTouchEvent, 将 ImageSpan 的类型传入,并使点击图片才能向 Handler 传递 Message,滑动图片并且不影响 TextView 内容的展示,这样就不会影响有图片文章的向下滑动阅读。

清单 6. 创建 LinkMovementMethodExt 类

public class LinkMovementMethodExt extends LinkMovementMethod {
……
@Override
 public boolean onTouchEvent(TextView widget, Spannable buffer,   MotionEvent event) {
……
	Object [] spans = buffer.getSpans(off, off, spanClass);
	if (spans.length != 0) {
	 if (action == MotionEvent.ACTION_DOWN) {
		Selection.setSelection(buffer, buffer.getSpanStart(spans[0]),  buffer.getSpanEnd(spans[0]));
		MessageSpan obj = new MessageSpan();
		obj.setObj(spans);
		obj.setView(widget);
		Message message = handler.obtainMessage();
		message.obj = obj;
		message.sendToTarget();
		 ……
	  }
	} ……

其中 Handler 用来处理从 HTML 中获得的 Span,把 LinkMovementMethodExt 中点击的内容传输到 MainActivity 之中,并对于 Span 类型做以判断,如果是 ImageSpan 就可以打开一个 ShowPicActivity,将图片以 ImageView 显示在其中。最终产生的效果为:可以在点击超链接时请求打开浏览器看超链接的内容,同时也可以在 TextView 显示的 HTML 内容中点击图片就可以跳转到 ShowPicActivity,先调用 reSizePicture()显示合适横屏或纵屏全屏大小的图片,将图片以 ImageView 的格式展示。

清单 7.点击 ImageSpan 后的处理

if (span instanceof ImageSpan) {
Intent intent = new Intent(c, ShowPicActivity.class);
        Bundle bundle = new Bundle();
bundle.putString("picUrl",((ImageSpan) span).getSource());
intent.putExtras(bundle);      		
startActivity(intent);
}

由于图片被以 ImageView 的格式展示出来,可以对于图片做出各种处理,比如使用对图片增加手势缩放,旋转的,这些就是对于 ImageView 的操作,在这里就不一一赘述了。不过这些效果都会提供有很好的用户体验。

在本例中用户交互式体验效果如下图4。

图 4

通过WebView或者TextView显示HTML内容的最佳实践和对比

总结

通过以上两部分的描述以及示例代码,相信读者已经对在 Android Native App 中如果显示 HTML 内容有了基本的了解,这里笔者通过在自己实际项目中的经验对这两种方法进行比较。在笔者所参与的项目中,也有显示 HTML 页面内容的需求,经过实际比较,

  • 如果 HTML 内容比较复杂,还是建议使用 WebView 作为显示方式,1. TextView 里面不是所有的 HTML 标签都支持,需要开发者额外增加对于标签的支持,2. Android 也提供了 WebView 和 App 之间的交互方式,使用它也可以满足基本需求。
  • 而对于显示格式化文本这样的需求,使用 TextView 就再合适不过了,可以再服务器端进行格式化文本的按照 HTML 格式的组装,在应用端直接显示,这样显示的效果在服务端完全控制,且相比使用 WebView 更轻量级。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值