Android实现点击WebView界面中图片滑动浏览与保存图片功能

一、实现需求

最近在公司的项目中遇到需求如下:

1、点击 WebView 页面的图片实现开启查看图片模式,即可以显示点击的图片,然后滑动显示下一张图片。

3、长按 WebView 页面图片弹出对话框可以选择保存长按的图片到本地相册。

拿到这个需求笔者第一反应是没做过 WebView 相关的交互,甚至分不清这个需求是否需要服务端配合完成 Java 与 JavaScript 的互相调用,一脸茫然。

遇到这种情况笔者的解决思路一般分两个方向:

1、找一个比较出名的客户端有类似功能的,然后 Google 搜索,仿 XXXX,先粗略看一下有没有现成的 Demo 可以参考,比如我这个需要,先去搜索一下 ”Android 仿微信朋友圈浏览图片效果“ (这个搜索关键字很关键啊),可是笔者没找到符合该需要的 Demo。

2、在第一个方案不好使的情况下,我们没有了参考,那么咱们就得自己思考这个大概实现思路,然后把这个需求进行拆分,逐一击破。所以思考大概如下:

(1)要想展示图片那么就得先拿到图片,要拿到图片只有两种可能,第一种可能是 WebView 本身缓存了图片,我们去缓存中读取图片进行显示,可是想一下,咱们浏览微博看图的时候如果没有网,这时候去点击图片那么图片是加载不出来的,所以这种可能否定了;所以只有第二种可能就是点击图片的时候拿到该图片对应的 URL 网址,然后咱们自己去网络加载图片进行显示,所以这个点我们 Get 到了。

(2)要滑动图片进行显示下一张,那么就需要我们能拿到所有要显示的图片的 URL ,然后放到一个数组里面,每次滑动就进行加载一张图片,那么也就是我们一次性拿到所有 WebView 包含图片的 URL,这个就不是在点击图片的时候去获取,而是在 WebView 加载完成后获取到,这怎么能拿到?再想一下,WebView 进行加载显示的时候其实是加载 HTML(比如 Assets 目录中的文件)文本的字符串,然后进行渲染处理显示出来,所以 HTML文本文件里面包含了我们想要的图片网址,大家看一下下面这张截图就是一个带图片的 WebView 对应加载的 HTML文本文件部分截图,

其中标签 src 对象的内容就是我们想要的图片 URL,所以到这里我们就有了思路,我们先拿到 WebView 加载的 HTML 内容,然后在从 HTML 里面提取我要想要的 URL。

(3)现在我们能拿到所有图片对应的 URL,那么滑动图片显示下一张就简单了,我们直接用一个 ViewPager 来实现滑动加载图片即可。

总结要实现这个需要我们需要做的工作有:

  • 拿到 WebView 加载的 HTML 文本。
  • 从 HTML 文本中提取所有图片对应的 URL。
  • 处理 WebView 中图片的点击和长按响应事件。
  • 用 ViewPager 来实现滑动加载下一张图片。

下面我们就按照以上几个步骤来实现我们想要的功能。

二、主要内容

2.1 获取 WebView 页面所有图片对应地址

2.1.1 解析 WebView 页面加载的 HTML文本文件

定义供 JavaScript 调用的交互接口

 /**
 *这个接口就是给 JavaScript 调用的,调用结果就是返回 HTML 文本,
 *然后 getAllImageUrlFromHtml(HTML)
 *从 HTML文件中提取页面所有图片对应的地址对象
 **/
private class InJavaScriptLocalObj {
 /**
 * 获取 WebView 加载对应的 HTML 文本
 * @param HTML WebView 加载对应的 HTML 文本
 */
 @android.webkit.JavascriptInterface
 public void showSource(String html) {
  //从 HTML 文件中提取页面所有图片对应的地址对象
  getAllImageUrlFromHtml(html);
 }
 }

WebView 开启 JavaScript 脚本执行,调用 JavaScript 代码

 mWebView.getSettings().setJavaScriptEnabled(true);
 mWebView.addJavascriptInterface(new InJavaScriptLocalObj(), "local_obj");
 mWebView.setWebViewClient(new WebViewClient() {
  // 网页跳转
  @Override
  public boolean shouldOverrideUrlLoading(WebView view, String url) {
  view.loadUrl(url);
  return true;
  }
  // 网页加载结束
  @Override
  public void onPageFinished(WebView view, String url) {
  super.onPageFinished(view, url);
  //解析 HTML
  parseHTML(view);
  }
 /**
 * Java 调取 js 代码,
 * @param view WebView
 */
private void parseHTML(WebView view) {
 //这段 js 代码是解析获取到了 HTML 文本文件,然后调用本地定义的 Java 代码返回
 //解析出来的 HTML 文本文件
 view.loadUrl("javascript:window.local_obj.showSource('<head>'+"
  + "document.getElementsByTagName('html')[0].innerHTML+'</head>');");
}

2.1.2 从获取到的 HTML文本文件中提取页面所有图片对应的地址对象

 // 获取 img 标签正则
 private static final String IMAGE_URL_TAG = "<img.*src=(.*?)[^>]*?>";
 // 获取 src 路径的正则
 private static final String IMAGE_URL_CONTENT = "http:\"?(.*?)(\"|>|\\s+)";
/***
 * 获取页面所有图片对应的地址对象,
 * 例如 <img src="http://sc1.hao123img.com/data/f44d0aab7bc35b8767de3c48706d429e" />
 * @param HTML WebView 加载的 HTML 文本
 * @return
 */
private List<String> getAllImageUrlFromHtml(String html) {
 Matcher matcher = Pattern.compile(IMAGE_URL_TAG).matcher(html);
 List<String> listImgUrl = new ArrayList<String>();
 while (matcher.find()) {
 listImgUrl.add(matcher.group());
 }
 //从图片对应的地址对象中解析出 src 标签对应的内容
 getAllImageUrlFormSrcObject(listImgUrl);
 return listImgUrl;
}
 /***
 * 从图片对应的地址对象中解析出 src 标签对应的内容,即 url
 * 例如 "http://sc1.hao123img.com/data/f44d0aab7bc35b8767de3c48706d429e"
 * @param listImageUrl 图片地址对象例如 :
 *<img src="http://sc1.hao123img.com/data/f44daab" />
 */
 private List<String> getAllImageUrlFormSrcObject(List<String> listImageUrl) {
 for (String image : listImageUrl) {
  Matcher matcher = Pattern.compile(IMAGE_URL_CONTENT).matcher(image);
  while (matcher.find()) {
  listImgSrc.add(matcher.group().substring(0, matcher.group().length() - 1));
  }
 }
 return listImgSrc;
 }

到这里我们获取到了 WebView 页面中所有图片对象对应的 URL 地址,下面就还差一步,就是在点击 WebView 界面的图片时候去响应点击事件,然后把相应的 URL 地址传递给 ViewPager 进行显示就齐活了。

2.2 响应 WebView 界面图片的点击事件

2.2.1定义供 JavaScript 调用的交互接口

// js 通信接口,定义供 JavaScript 调用的交互接口
private class MyJavascriptInterface {
 private Context context;
 public MyJavascriptInterface(Context context) {
 this.context = context;
 }
 /**
 * 点击图片启动新的 ShowImageFromWebActivity,并传入点击图片对应的 url
 * 和页面所有图片对应的 url
 * @param url 点击图片对应的 url
 */
 @android.webkit.JavascriptInterface
 public void openImage(String url) {
 Intent intent = new Intent();
 intent.putExtra("image", url);
 //listImgSrc 该参数为页面所有图片对应的 url
 intent.putStringArrayListExtra(URL_ALL, (ArrayList<String>) listImgSrc);
 intent.setClass(context, ShowImageFromWebActivity.class);
 context.startActivity(intent);
 }
}

2.2.2 WebView 开启 JavaScript 脚本执行,调用 JavaScript 代码

 mWebView.getSettings().setJavaScriptEnabled(true);
//载入 js
mWebView.addJavascriptInterface(new MyJavascriptInterface(this), "imageListener");
mWebView.setWebViewClient(new WebViewClient() {
 // 网页跳转
 @Override
 public boolean shouldOverrideUrlLoading(WebView view, String url) {
 view.loadUrl(url);
 return true;
 }
 // 网页加载结束
 @Override
 public void onPageFinished(WebView view, String url) {
 super.onPageFinished(view, url);
 // web 页面加载完成,添加监听图片的点击 js 函数
 addImageClickListener();
 }

 /**
 * 注入 js 函数监听,这段 js 函数的功能就是,遍历所有的图片,并添加 onclick 函数,
 * 实现点击事件,
 * 函数的功能是在图片点击的时候调用本地 java 接口并传递点击图片对应的 url 过去
 */
 private void addImageClickListener() {
 mWebView.loadUrl("javascript:(function(){" +
  "var objs = document.getElementsByTagName(\"img\"); " +
  "for(var i=0;i<objs.length;i++) " +
  "{"
  + " objs[i].onclick=function() " +
  " { "
  + " window.imageListener.openImage(this.src); " +
  " } " +
  "}" +
  "})()");
 }

到这里我们完成了前两步,拿去到 WebView 界面图片对应的所有 URL 地址和响应 WebView 界面图片的点击事件,下面的事情就简单了,用 ViewPager 滑动显示每一张图片,再我们进行最后一步之前,我们再来实现一个功能就是长按 WebView 界面图片,弹出对话框来,然后可以选择保存图片功能,代码如下:

WebView 中图片长按点击事件处理

//长按点击事件
mWebView.setOnLongClickListener(new View.OnLongClickListener() {
 @Override
 public boolean onLongClick(View v) {
 //响应长按事件
 responseWebLongClick(v);
 return false;
 }
});
 /**
 * 响应 WebView 长按图片的点击事件
 * @param v
 */
 private void responseWebLongClick(View v) {
 if (v instanceof WebView) {
  WebView.HitTestResult result = ((WebView) v).getHitTestResult();
  if (result != null) {
  int type = result.getType();
  //判断点击类型如果是图片
  if (type == WebView.HitTestResult.IMAGE_TYPE || type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) {
   longClickUrl = result.getExtra();
   //弹出对话框
   showDialog(longClickUrl);
  }
  }
 }
 }
 /**
 * 长按 WebView 中图片弹出对话框,可以选择保存图片
 * @param url 点击图片对应的 url
 */
 private void showDialog(final String url) {
 new ActionSheetDialog(this)
  .builder()
  .setCancelable(true)
  .setCanceledOnTouchOutside(true)
  .addSheetItem(
   "保存到相册",
   ActionSheetDialog.SheetItemColor.Blue,
   new ActionSheetDialog.OnSheetItemClickListener() {
    @Override
    public void onClick(int which) {
    //下载图片
    downloadImage(url);
    }
   }).show();
 }

2.3 ViewPager 滑动显示每一张图片,PhotoView 实现自由缩放功能

由于这部分代码比较简单,这里就直接贴出部分代码,文章中所用的 Demo 代码最终会上传到 GitHub上,有兴趣可以去瞧一瞧完整的代码,这里简单介绍几个类,ShowImageFromWebActivity.java 这个类内部就包含一个 ViewPager 和两个按钮, ViewPager 用来滑动显示每一张图片,按钮用来显示滑动的页数和实现点击保存图片功能,代码如下:

public class ShowImageFromWebActivity extends Activity implements View.OnClickListener {
 private ViewPager vpImageBrowser;
 private TextView tvImageIndex;//显示滑动页数
 private Button btnSave;//保存图片按钮
 private ImageBrowserAdapter adapter;
 private ArrayList<String> imgUrls;//WebView 页面所有图片 URL
 private String url;//WebView 页面所有图片中被点击图片对应 URL
 private int currentIndex;//标记被滑动图片在所有图片中的位置
 private Handler mHandler;//异步发送消息
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_show_image_from_web);
 initView();
 initListener();
 initData();
 }
 private void initView(){
 vpImageBrowser = (ViewPager) findViewById(R.id.vp_image_browser);
 tvImageIndex = (TextView) findViewById(R.id.tv_image_index);
 btnSave = (Button) findViewById(R.id.btn_save);
 }
 private void initData(){
 mHandler = new Handler();
 imgUrls=getIntent().getStringArrayListExtra(MainActivity.URL_ALL);
 url=getIntent().getStringExtra("image");
 //获取被点击图片在所有图片中的位置
 int position=imgUrls.indexOf(url);
 adapter=new ImageBrowserAdapter(this,imgUrls);
 vpImageBrowser.setAdapter(adapter);
 final int size=imgUrls.size();
 if(size > 1) {
  tvImageIndex.setVisibility(View.VISIBLE);
  tvImageIndex.setText((position+1) + "/" + size);
 } else {
  tvImageIndex.setVisibility(View.GONE);
 }
 vpImageBrowser.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
  @Override
  public void onPageSelected(int arg0) {
  currentIndex=arg0;
  int index = arg0 % size;
  tvImageIndex.setText((index+1) + "/" + size);
  }
  @Override
  public void onPageScrolled(int arg0, float arg1, int arg2) {
  // TODO Auto-generated method stub
  }
  @Override
  public void onPageScrollStateChanged(int arg0) {
  // TODO Auto-generated method stub
  }
 });
 vpImageBrowser.setCurrentItem(position);
 }
 private void initListener(){
 btnSave.setOnClickListener(this);
 }
 @Override
 public void onClick(View v) {
 switch (v.getId()){
  case R.id.btn_save :
  Toast.makeText(getApplicationContext(), "开始下载图片", Toast.LENGTH_SHORT).show();
  downloadImage();
  break;
 }
 /**
 * 开始下载图片
 */
 private void downloadImage() {
 downloadAsync(imgUrls.get(currentIndex), Environment.getExternalStorageDirectory().getAbsolutePath() + "/ImagesFromWebView");
 }

 }

ShowImageFromWebActivity.java 对应 xml 文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:background="@android:color/black"
 tools:context="activity.ShowImageFromWebActivity">
 <view.PhotoViewViewPager
 android:id="@+id/vp_image_browser"
 android:layout_width="match_parent"
 android:layout_height="match_parent" >
 </view.PhotoViewViewPager>
 <LinearLayout
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:layout_alignParentBottom="true"
 android:orientation="horizontal"
 android:padding="16dp" >
 <TextView
  android:textSize="18sp"
  android:id="@+id/tv_image_index"
  android:layout_width="56dp"
  android:layout_height="36dp"
  android:background="@drawable/shape_corner_rect_gray"
  android:gravity="center"
  android:paddingTop="3dp"
  android:paddingBottom="3dp"
  android:paddingRight="10dp"
  android:paddingLeft="10dp"
  android:text="1/9"
  android:textColor="@android:color/white" />
 <View
  android:layout_width="0dp"
  android:layout_height="1dp"
  android:layout_weight="1" />
 <Button
  android:id="@+id/btn_save"
  android:textSize="@dimen/_16sp"
  android:layout_width="56dp"
  android:layout_height="36dp"
  android:background="@drawable/shape_corner_rect_gray"
  android:gravity="center"
  android:padding="4dp"
  android:text="保存"
  android:textColor="@color/white" />
 </LinearLayout>
</RelativeLayout>

ImageBrowserAdapter.java 类代码如下:

public class ImageBrowserAdapter extends PagerAdapter {
 private Activity context;
 private List<String> picUrls;
 public ImageBrowserAdapter(Activity context, ArrayList<String> picUrls) {
 this.context = context;
 this.picUrls = picUrls;
 }
 @Override
 public int getCount() {
 return picUrls.size();
 }

 @Override
 public boolean isViewFromObject(View view, Object object) {
 return view == object;
 }
 @Override
 public View instantiateItem(ViewGroup container, int position) {
 View view = View.inflate(context, R.layout.item_image_browser, null);
 ImageView iv_image_browser = (ImageView) view.findViewById(R.id.show_webimage_imageview);
 String picUrl = picUrls.get(position);
 final PhotoViewAttacher photoViewAttacher=new PhotoViewAttacher(iv_image_browser);
 photoViewAttacher.setScaleType(ImageView.ScaleType.FIT_CENTER);
 //显示图片
 Glide.with(context).
  load(picUrl)
  .crossFade()
  .placeholder(R.drawable.avatar_default)
  .error(R.drawable.image_default_rect)
  .into(new GlideDrawableImageViewTarget(iv_image_browser){
  @Override
  public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> animation) {
   super.onResourceReady(resource, animation);
   photoViewAttacher.update();
  }
  });
 container.addView(view);
 return view;
 }
 @Override
 public void destroyItem(ViewGroup container, int position, Object object) {
 container.removeView((View) object);
 }

上面代码也很简单,就是根据 URL 来加载显示图片,然后利用 PhotoView 进行缩放。

//ImageBrowserAdapter Item 布局文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:gravity="center">
 <RelativeLayout
  android:orientation="horizontal"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:gravity="center_vertical">
  <uk.co.senab.photoview.PhotoView
  android:id="@+id/pv_show_image"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:background="@android:color/white"
  android:src="@drawable/image_default_rect" />
 </RelativeLayout>
</RelativeLayout>

以上为本次学习内容,如有错误还望指正,谢谢!

文章中 Demo 已经上传在 GitHub上,地址为ShowImageFromWebView,大家也可以通过本地下载

总结

以上就是这篇文章的全部内容了,希望本文的内容对各位Android开发者们能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • Android开发之WebView组件的使用解析

    在 Android 手机中内置了一款高性能 webkit 内核浏览器, SDK 中封装为一个叫做 WebView 组件. WebView 类是 WebKit 模块 Java 层的视图类,( 所有需要使用 Web 浏览功能的Android应用程序都要创建该视图对象显示和处理请求的网络资源.目前,WebKit 模块支持 HTTP.HTTPS.FTP 以及 javascript 请求. WebView 作为应用程序的 UI 接口,为用户提供了一系 列的网页浏览.用户交互接口,客户程序通过这些接口访问

  • Android编程实现WebView自适应全屏方法小结

    本文实例讲述了Android编程实现WebView自适应全屏的方法.分享给大家供大家参考,具体如下: 第一种: settings.setUseWideViewPort(true); settings.setLoadWithOverviewMode(true); 第二种: WebSetting settings = webView.getSettings(); settings.setLayoutAlgorithm(LayoutAlgorithm.SINGLE_COLUMN); 把所有内容放在we

  • 解析Android中webview和js之间的交互

    1.android中利用webview调用网页上的js代码.Android 中可以通过webview来实现和js的交互,在程序中调用js代码,只需要将webview控件的支持js的属性设置为true,,然后通过loadUrl就可以直接进行调用,如下所示:mWebView.getSettings().setJavaScriptEnabled(true);mWebView.loadUrl("javascript:test()"); 2. 网页上调用android中java代码的方法在网页中

  • Android中WebView图片实现自适应的方法

    本文实例讲述了Android中WebView图片实现自适应的方法.分享给大家供大家参考.具体实现方法如下: 复制代码 代码如下: WebSettings ws = tv.getSettings(); 加上这个属性后,html的图片就会以单列显示就不会变形占了别的位置 ws.setLayoutAlgorithm(LayoutAlgorithm.SINGLE_COLUMN); //让缩放显示的最小值为起始 webView.setInitialScale(5); // 设置支持缩放 webSettin

  • Android编程实现读取本地SD卡图片的方法

    本文实例讲述了Android编程实现读取本地SD卡图片的方法.分享给大家供大家参考,具体如下: private Bitmap getDiskBitmap(String pathString) { Bitmap bitmap = null; try { File file = new File(pathString); if(file.exists()) { bitmap = BitmapFactory.decodeFile(pathString); } } catch (Exception e)

  • android异步加载图片并缓存到本地实现方法

    在android项目中访问网络图片是非常普遍性的事情,如果我们每次请求都要访问网络来获取图片,会非常耗费流量,而且图片占用内存空间也比较大,图片过多且不释放的话很容易造成内存溢出.针对上面遇到的两个问题,首先耗费流量我们可以将图片第一次加载上面缓存到本地,以后如果本地有就直接从本地加载.图片过多造成内存溢出,这个是最不容易解决的,要想一些好的缓存策略,比如大图片使用LRU缓存策略或懒加载缓存策略.今天首先介绍一下本地缓存图片. 首先看一下异步加载缓存本地代码: 复制代码 代码如下: public

  • Android使用WebView播放flash的方法

    本文实例讲述了Android使用WebView播放flash及判断是否安装flash插件的方法.分享给大家供大家参考.具体实现方法如下: 一.问题: 最近帮一个同学做一个项目,断断续续的一些知识点记录一下.一个页面中有一个WebView,用来播放swf,如果系统中未安装flash插件,必须提示用户到market中安装. 二.解决方法: 下面做一个demo,效果图如下: 图1: 图2: 图3: 首先布局文件,很简单: 复制代码 代码如下: <RelativeLayout xmlns:android

  • android中webview控件和javascript交互实例

    当我们要实现丰富的图文混排效果的时候,我们一般会使用webview,这是一个功能十分强大的的控件,来看看官方的解释: 复制代码 代码如下: A View that displays web pages. This class is the basis upon which you can roll your own web browser or simply display some online content within your Activity. It uses the WebKit

  • Android webview打开本地图片上传实现代码

    Webview打开本地图片选择器十分之麻烦,其在安卓系统3x 4x 5x上的行为都不同,处理也不同,所以之前差点崩溃.经过测试和完善,最终其在各个版本上都能完美工作. 直接上代码 package com.testandroid.webview; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.support.v7.app.AlertDialog; imp

  • Android开发实现webview中img标签加载本地图片的方法

    本文实例讲述了Android开发实现webview中img标签加载本地图片的方法.分享给大家供大家参考,具体如下: 在网上查了很多教程,感觉很麻烦,各种方法,最后实践很简单,主要是两步: WebSettings webSettings=webView.getSettings(); //允许webview对文件的操作 webSettings.setAllowUniversalAccessFromFileURLs(true); webSettings.setAllowFileAccess(true)

  • Android 实现WebView点击图片查看大图列表及图片保存功能

    在日常开发过程中,有时候会遇到需要在app中嵌入网页,此时使用WebView实现效果,但在默认情况下是无法点击图片查看大图的,更无法保存图片.本文将就这一系列问题的实现进行说明. 图示: 项目的知识点: 加载网页后如何捕捉网页中的图片点击事件: 获取点击的图片资源后进行图片显示,获取整个页面所有的图片: 支持查看上下一张的图片以及对图片缩放显示: 对图片进行保存: 其他:图片缓存的处理(不用每次都重新加载已查看过的图片) 项目代码结构: 前期准备(添加权限.依赖和混淆设置): 添加权限: <us

随机推荐