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

在日常开发过程中,有时候会遇到需要在app中嵌入网页,此时使用WebView实现效果,但在默认情况下是无法点击图片查看大图的,更无法保存图片。本文将就这一系列问题的实现进行说明。

图示:

项目的知识点:

加载网页后如何捕捉网页中的图片点击事件;

获取点击的图片资源后进行图片显示,获取整个页面所有的图片;

支持查看上下一张的图片以及对图片缩放显示;

对图片进行保存;

其他:图片缓存的处理(不用每次都重新加载已查看过的图片)

项目代码结构:

前期准备(添加权限、依赖和混淆设置):

添加权限:

 <uses-permission android:name="android.permission.INTERNET" />
 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
 <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>

添加依赖:

 compile 'com.bm.photoview:library:1.4.1'
 compile 'com.github.bumptech.glide:glide:3.7.0'
 compile 'com.android.support:support-v4:25.0.0'

混淆文件设置:

-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
 **[] $VALUES;
 public *;
} 

代码解析:

MainActivity很简单,代码如下:

@Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  contentWebView = (WebView) findViewById(R.id.webView);
  contentWebView.getSettings().setJavaScriptEnabled(true);
  contentWebView.loadUrl("http://a.mp.uc.cn/article.html?uc_param_str=frdnsnpfvecpntnwprdssskt&client=ucweb&wm_aid=c51bcf6c1553481885da371a16e33dbe&wm_id=482efebe15ed4922a1f24dc42ab654e6&pagetype=share&btifl=100");
  contentWebView.addJavascriptInterface(new MJavascriptInterface(this,imageUrls), "imagelistener");
  contentWebView.setWebViewClient(new MyWebViewClient());
 } 

很显然,就是WebView的基本初始化操作。其中1.自定义了MJavascriptInterface的类用来实现js调用本地的方法;2.自定义MyWebViewClient来实现对WebView的监听管理。

MyWebViewClient代码如下:

public class MyWebViewClient extends WebViewClient {
 @Override
 public void onPageFinished(WebView view, String url) {
  view.getSettings().setJavaScriptEnabled(true);
  super.onPageFinished(view, url);
  addImageClickListener(view);//待网页加载完全后设置图片点击的监听方法
 }
 @Override
 public void onPageStarted(WebView view, String url, Bitmap favicon) {
  view.getSettings().setJavaScriptEnabled(true);
  super.onPageStarted(view, url, favicon);
 }
 private void addImageClickListener(WebView webView) {
  webView.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); " +//通过js代码找到标签为img的代码块,设置点击的监听方法与本地的openImage方法进行连接
    " } " +
    "}" +
    "})()");
 }
} 

该类继承自WebViewClient,在onPageFinished方法中设置addImageClickListener的监听方法——>当整个WebView页面加载完毕后,为每张图片设置监听事件——>这意味着,整个页面未加载完毕时,点击是无效的。
addImageClickListener的代码实现也很简单,通过js找到相应的img标签,这样就知道是图片了,然后为这些图片设置点击监听事件——>每当点击时调用自定义的openImage(url)方法。这个openImage(url)方法与MJavascriptInterface中对应的方法交相辉映,这样就形成了js调用本地的方法。

MJavascriptInterface代码(主要为与js对应的本地方法的实现):

public class MJavascriptInterface {
 private Context context;
 private String [] imageUrls;
 public MJavascriptInterface(Context context,String[] imageUrls) {
  this.context = context;
  this.imageUrls = imageUrls;
 }
 @android.webkit.JavascriptInterface
 public void openImage(String img) {
  Intent intent = new Intent();
  intent.putExtra("imageUrls", imageUrls);
  intent.putExtra("curImageUrl", img);
  intent.setClass(context, PhotoBrowserActivity.class);
  context.startActivity(intent);
 }
} 

可以看到,openImage(url)方法实现的逻辑是:通过传递当前图片的url与该WebView整个页面的图片列表(imageUrls)进行跳转至PhotoBrowserActivity中。PhotoBrowserActivity就是用来显示大图的图片列表的页面。

此处的疑问:imageUrls怎么获得呢?

方式:1.服务器端直接将WebView中所有的图片按照顺序组合成String数组传递过来;2.或者直接将所有含img标签的html代码传递过来,从而让客户端自己解析出所有图片地址组合成的String数组。(此处是采用的第二种,具体如何解析,可以下载源码查看。)

OK,到了这里算是完成了项目知识点的第1点:1.加载网页后如何捕捉网页中的图片点击事件;

接下来就说明后面的几点:

2.获取点击的图片资源后进行图片显示,获取整个页面所有的图片;

3.支持查看上下一张的图片以及对图片缩放显示;

4.对图片进行保存;

其他所有的几点实现均在PhotoBrowserActivity中,代码如下:主要就是将图片放进ViewPager中进行显示:

mPager = (ViewPager) findViewById(R.id.pager);
  mPager.setPageMargin((int) (getResources().getDisplayMetrics().density * 15));
  mPager.setAdapter(new PagerAdapter() {
   @Override
   public int getCount() {
    return imageUrls.length;
   }
   @Override
   public boolean isViewFromObject(View view, Object object) {
    return view == object;
   }
   @Override
   public Object instantiateItem(ViewGroup container, final int position) {
    if (imageUrls[position] != null && !"".equals(imageUrls[position])) {
     final PhotoView view = new PhotoView(PhotoBrowserActivity.this);
     view.enable();
     view.setScaleType(ImageView.ScaleType.FIT_CENTER);
     Glide.with(PhotoBrowserActivity.this).load(imageUrls[position]).override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL).fitCenter().crossFade().listener(new RequestListener<String, GlideDrawable>() {
      @Override
      public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {
       if (position == curPosition) {
        hideLoadingAnimation();
       }
       showErrorLoading();
       return false;
      }
      @Override
      public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
       occupyOnePosition(position);
       if (position == curPosition) {
        hideLoadingAnimation();
       }
       return false;
      }
     }).into(view);
     container.addView(view);
     return view;
    }
    return null;
   }
   @Override
   public void destroyItem(ViewGroup container, int position, Object object) {
    releaseOnePosition(position);
    container.removeView((View) object);
   }
  });
  curPosition = returnClickedPosition() == -1 ? 0 : returnClickedPosition();
  mPager.setCurrentItem(curPosition);
  mPager.setTag(curPosition);
  if (initialedPositions[curPosition] != curPosition) {//如果当前页面未加载完毕,则显示加载动画,反之相反;
   showLoadingAnimation();
  }
  photoOrderTv.setText((curPosition + 1) + "/" + imageUrls.length);//设置页面的编号
  mPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
   @Override
   public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
   }
   @Override
   public void onPageSelected(int position) {
    if (initialedPositions[position] != position) {//如果当前页面未加载完毕,则显示加载动画,反之相反;
     showLoadingAnimation();
    } else {
     hideLoadingAnimation();
    }
    curPosition = position;
    photoOrderTv.setText((position + 1) + "/" + imageUrls.length);//设置页面的编号
    mPager.setTag(position);//为当前view设置tag
   }
   @Override
   public void onPageScrollStateChanged(int state) {
   }
  });
 }
 private int returnClickedPosition() {
  if (imageUrls == null || curImageUrl == null) {
   return -1;
  }
  for (int i = 0; i < imageUrls.length; i++) {
   if (curImageUrl.equals(imageUrls[i])) {
    return i;
   }
  }
  return -1;
 } 

1.首先通过returnClickedPosition方法来获得用户点击的是哪一张图片的位置并设置当前是哪一个page——>通过遍历当前url与所有url来匹配获取;

2.通过addOnPageChangeListener来实现对页面滑动事件的监听——>此处主要用来处理设置当前页面的position、动画、页面序号显示的逻辑;

3.PagerAdapter的实现——>每一页内容的初始化,主要为instantiateItem,核心代码再次拖出来如下;

if (imageUrls[position] != null && !"".equals(imageUrls[position])) {
     final PhotoView view = new PhotoView(PhotoBrowserActivity.this);
     view.enable();
     view.setScaleType(ImageView.ScaleType.FIT_CENTER);
     Glide.with(PhotoBrowserActivity.this).load(imageUrls[position]).override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL).fitCenter().crossFade().listener(new RequestListener<String, GlideDrawable>() {
      @Override
      public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {
       if (position == curPosition) {
        hideLoadingAnimation();
       }
       showErrorLoading();
       return false;
      }
      @Override
      public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
       occupyOnePosition(position);
       if (position == curPosition) {
        hideLoadingAnimation();
       }
       return false;
      }
     }).into(view);
     container.addView(view);
     return view;
    } 

大体思路:

1.通过PhotoView来实现图片的伸缩显示;2.通过Glide来加载图片等处理;

PhotoView是什么——>就是图片组件,对图片的伸缩、动效、缓存等方面进行了处理,点击地址查看GitHub介绍>>:

Gilde是什么——>Google推荐的图片加载库,此处用它的理由是好用、简单,点击地址查看GitHub介绍>>:

Glide的简化形式——>Glide.with(...).load(图片地址).override(加载图片的大小).listener(设置监听方法).into(某个一个组件,此处是PhotoView),此处使用的是原图加载,监听方法中有两个回调方法:

onException和onResourceReady,此处在onResourceReady做的处理是:当资源加载完毕时调用——>此时取消加载动画的显示。

页面中的“页面编号”和“保存”的组件显示是通过写在整个Activity的布局文件中实现的,而不是通过在每一页中写入这些组件。以下为获取图片资源对象的代码:

private void savePhotoToLocal() {
  ViewGroup containerTemp = (ViewGroup) mPager.findViewWithTag(mPager.getCurrentItem());
  if (containerTemp == null) {
   return;
  }
  PhotoView photoViewTemp = (PhotoView) containerTemp.getChildAt(0);
  if (photoViewTemp != null) {
   GlideBitmapDrawable glideBitmapDrawable = (GlideBitmapDrawable) photoViewTemp.getDrawable();
   if (glideBitmapDrawable == null) {
    return;
   }
   Bitmap bitmap = glideBitmapDrawable.getBitmap();
   if (bitmap == null) {
    return;
   }
   FileUtils.savePhoto(this, bitmap, new FileUtils.SaveResultCallback() {
    @Override
    public void onSavedSuccess() {
     runOnUiThread(new Runnable() {
      @Override
      public void run() {
       Toast.makeText(PhotoBrowserActivity.this, "保存成功", Toast.LENGTH_SHORT).show();
      }
     });
    }
    @Override
    public void onSavedFailed() {
     runOnUiThread(new Runnable() {
      @Override
      public void run() {
       Toast.makeText(PhotoBrowserActivity.this, "保存失败", Toast.LENGTH_SHORT).show();
      }
     });
    }
   });
  }
 } 

因为下载图片需要知道当前处于哪一页,所以在ViewPager初始化显示和滑动时都给每一页设置了tag,此时就派上了用场——>mPager.findViewWithTag获取当前page中的布局对象,然后获得对应的PhotoView对象,从而经过处理最终获取到Bitmap对象。这样已经很简单了,接下来只要将Bitmap对象保存至本地即可,代码如下:

public class FileUtils {
 public static void savePhoto(final Context context, final Bitmap bmp , final SaveResultCallback saveResultCallback) {
  new Thread(new Runnable() {
   @Override
   public void run() {
    File appDir = new File(Environment.getExternalStorageDirectory(), "out_photo");
    if (!appDir.exists()) {
     appDir.mkdir();
    }
    SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");//设置以当前时间格式为图片名称
    String fileName = df.format(new Date()) + ".png";
    File file = new File(appDir, fileName);
    try {
     FileOutputStream fos = new FileOutputStream(file);
     bmp.compress(Bitmap.CompressFormat.PNG, 100, fos);
     fos.flush();
     fos.close();
     saveResultCallback.onSavedSuccess();
    } catch (FileNotFoundException e) {
     saveResultCallback.onSavedFailed();
     e.printStackTrace();
    } catch (IOException e) {
     saveResultCallback.onSavedFailed();
     e.printStackTrace();
    }
    //保存图片后发送广播通知更新数据库
    Uri uri = Uri.fromFile(file);
    context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri));
   }
  }).start();
 }
 public interface SaveResultCallback{
  void onSavedSuccess();
  void onSavedFailed();
 }
} 

图片如何保存已经如代码所示,但要注意的是需要将已经保存的图片进行广播通知数据库更新——>这样立马进入微信或者扣扣点击发送图片,就可以看到刚刚保存的图片。

缓存的处理:

使用Glide其中的一个好处是会将图片默认缓存,在需要清除缓存时,只需要执行下面的代码(此处是放在MainActivity中,退出页面即清除缓存):

@Override
 protected void onDestroy() {
  new Thread(new Runnable() {
   @Override
   public void run() {
    Glide.get(MainActivity.this).clearDiskCache();//清理磁盘缓存需要在子线程中执行
   }
  }).start();
  Glide.get(this).clearMemory();//清理内存缓存可以在UI主线程中进行
  super.onDestroy();
 } 

特别注意:

1.若项目配置中将targetSdkVersion 指定为22以上,则要加入动态权限申请的模块,否则在进行保存操作时则会提示失败!

2.项目中暴露的js接口类:MJavascriptInterface不能混淆,其调用的方法的声明也不能混淆,所以还要添加如下混淆设置代码(代码因包名而变化):

-keepclassmembers class com.example.administrator.webviewpagescannerapp.other.MJavascriptInterface{
 public *;
}
-keepattributes *Annotation*
-keepattributes *JavascriptInterface* 

源码已经上传至GitHub,点击此处查看>>

以上所述是小编给大家介绍的Android 实现WebView点击图片查看大图列表及图片保存功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • 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播放flash的方法

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

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

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

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

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

  • 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中WebView图片实现自适应的方法

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

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

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

  • 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界面中图片滑动浏览与保存图片功能

    一.实现需求 最近在公司的项目中遇到需求如下: 1.点击 WebView 页面的图片实现开启查看图片模式,即可以显示点击的图片,然后滑动显示下一张图片. 3.长按 WebView 页面图片弹出对话框可以选择保存长按的图片到本地相册. 拿到这个需求笔者第一反应是没做过 WebView 相关的交互,甚至分不清这个需求是否需要服务端配合完成 Java 与 JavaScript 的互相调用,一脸茫然. 遇到这种情况笔者的解决思路一般分两个方向: 1.找一个比较出名的客户端有类似功能的,然后 Google

  • 解析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自适应全屏的方法.分享给大家供大家参考,具体如下: 第一种: settings.setUseWideViewPort(true); settings.setLoadWithOverviewMode(true); 第二种: WebSetting settings = webView.getSettings(); settings.setLayoutAlgorithm(LayoutAlgorithm.SINGLE_COLUMN); 把所有内容放在we

随机推荐