全网整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:400-708-3566

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点击图片查看大图列表及图片保存功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!


# webview  # 点击图片查看大图  # Android 自定义View手写签名并保存图片功能  # Android使用webView长按保存下载网络图片  # Android WebView实现长按保存图片及长按识别二维码功能  # Android 保存WebView中的图片示例  # Android长按imageview把图片保存到本地的实例代码  # Android实现点击WebView界面中图片滑动浏览与保存图片功能  # Android实现将View转化为图片并保存到本地  # 加载  # 的是  # 自定义  # 来实现  # 很简单  # 几点  # 小编  # 这一  # 放在  # 在此  # 上了  # 以及对  # 遍历  # 中有  # 但在  # 等方面  # 交相辉映  # 形成了  # 也很  # 给大家 


相关文章: 青岛网站设计制作公司,查询青岛招聘信息的网站有哪些?  怎么将XML数据可视化 D3.js加载XML  IOS倒计时设置UIButton标题title的抖动问题  如何选择PHP开源工具快速搭建网站?  广东专业制作网站有哪些,广东省能源集团有限公司官网?  如何通过宝塔面板实现本地网站访问?  阿里云网站制作公司,阿里云快速搭建网站好用吗?  如何零基础开发自助建站系统?完整教程解析  建站之星与建站宝盒如何选择最佳方案?  制作网站的过程怎么写,用凡科建站如何制作自己的网站?  海南网站制作公司有哪些,海口网是哪家的?  制作销售网站教学视频,销售网站有哪些?  南宁网站建设制作定制,南宁网站建设可以定制吗?  如何在服务器上配置二级域名建站?  香港服务器网站生成指南:免费资源整合与高速稳定配置方案  如何在Windows 2008云服务器安全搭建网站?  如何通过商城免费建站系统源码自定义网站主题?  文字头像制作网站推荐软件,醒图能自动配文字吗?  电脑免费海报制作网站推荐,招聘海报哪个网站多?  山东云建站价格为何差异显著?  建站之星2.7模板快速切换与批量管理功能操作指南  TestNG的testng.xml配置文件怎么写  如何快速打造个性化非模板自助建站?  小说建站VPS选用指南:性能对比、配置优化与建站方案解析  小自动建站系统:AI智能生成+拖拽模板,多端适配一键搭建  实例解析angularjs的filter过滤器  如何通过网站建站时间优化SEO与用户体验?  如何用低价快速搭建高质量网站?  如何在Golang中使用replace替换模块_指定本地或远程路径  建站之星导航如何优化提升用户体验?  高端云建站费用究竟需要多少预算?  小程序网站制作需要准备什么资料,如何制作小程序?  建站之星后台管理系统如何操作?  如何通过wdcp面板快速创建网站?  建站之星代理如何获取技术支持?  家具网站制作软件,家具厂怎么跑业务?  英语简历制作免费网站推荐,如何将简历翻译成英文?  如何基于PHP生成高效IDC网络公司建站源码?  成都响应式网站开发,dw怎么把手机适应页面变成网页?  C#如何在一个XML文件中查找并替换文本内容  微网站制作教程,不会写代码,不会编程,怎么样建自己的网站?  如何选择高性价比服务器搭建个人网站?  如何通过cPanel快速搭建网站?  c++怎么实现高并发下的无锁队列_c++ std::atomic原子变量与CAS操作【详解】  装修招标网站设计制作流程,装修招标流程?  c++怎么用jemalloc c++替换默认内存分配器【性能】  免费公司网站制作软件,如何申请免费主页空间做自己的网站?  ,怎么在广州志愿者网站注册?  如何在阿里云ECS服务器部署织梦CMS网站?  教学网站制作软件,学习*后期制作的网站有哪些? 

您的项目需求

*请认真填写需求信息,我们会在24小时内与您取得联系。