在日常开发过程中,有时候会遇到需要在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转化为图片并保存到本地
# 加载
# 的是
# 自定义
# 来实现
# 很简单
# 几点
# 小编
# 这一
# 放在
# 在此
# 上了
# 以及对
# 遍历
# 中有
# 但在
# 等方面
# 交相辉映
# 形成了
# 也很
# 给大家
相关文章:
湖北网站制作公司有哪些,湖北清能集团官网?
如何选择最佳自助建站系统?快速指南解析优劣
网站制作难吗安全吗,做一个网站需要多久时间?
如何选择高性价比服务器搭建个人网站?
网站制作说明怎么写,简述网页设计的流程并说明原因?
网站制作知乎推荐,想做自己的网站用什么工具比较好?
在线制作视频网站免费,都有哪些好的动漫网站?
岳西云建站教程与模板下载_一站式快速建站系统操作指南
猪八戒网站制作视频,开发一个猪八戒网站,大约需要多少?或者自己请程序员,需要什么程序员,多少程序员能完成?
如何在云指建站中生成FTP站点?
如何在Windows环境下新建FTP站点并设置权限?
如何在景安云服务器上绑定域名并配置虚拟主机?
宁波自助建站系统如何快速打造专业企业网站?
如何在阿里云ECS服务器部署织梦CMS网站?
活动邀请函制作网站有哪些,活动邀请函文案?
无锡营销型网站制作公司,无锡网选车牌流程?
制作网站的公司有哪些,做一个公司网站要多少钱?
h5在线制作网站电脑版下载,h5网页制作软件?
东莞专业网站制作公司有哪些,东莞招聘网站哪个好?
建站之星下载版如何获取与安装?
建站之星ASP如何实现CMS高效搭建与安全管理?
建站为何优先选择香港服务器?
创业网站制作流程,创业网站可靠吗?
武汉网站设计制作公司,武汉有哪些比较大的同城网站或论坛,就是里面都是武汉人的?
韩国服务器如何优化跨境访问实现高效连接?
北京营销型网站制作公司,可以用python做一个营销推广网站吗?
建站之星会员如何解锁更多建站功能?
微信小程序 五星评分(包括半颗星评分)实例代码
建站主机默认首页配置指南:核心功能与访问路径优化
如何高效利用亚马逊云主机搭建企业网站?
宝塔建站无法访问?如何排查配置与端口问题?
如何高效生成建站之星成品网站源码?
PHP 500报错的快速解决方法
建站主机无法访问?如何排查域名与服务器问题
详解免费开源的DotNet二维码操作组件ThoughtWorks.QRCode(.NET组件介绍之四)
营销式网站制作方案,销售哪个网站招聘效果最好?
建站之家VIP精选网站模板与SEO优化教程整合指南
建站上市公司网站建设方案与SEO优化服务定制指南
建站主机如何选?高性价比方案全解析
极客网站有哪些,DoNews、36氪、爱范儿、虎嗅、雷锋网、极客公园这些互联网媒体网站有什么差异?
如何选择建站程序?包含哪些必备功能与类型?
,购物网站怎么盈利呢?
建站之星多图banner生成与模板自定义指南
详解jQuery停止动画——stop()方法的使用
浙江网站制作公司有哪些,浙江栢塑信息技术有限公司定制网站做的怎么样?
建站之星导航如何优化提升用户体验?
专业企业网站设计制作公司,如何理解商贸企业的统一配送和分销网络建设?
网站制作的方法有哪些,如何将自己制作的网站发布到网上?
如何在万网自助建站中设置域名及备案?
如何用免费手机建站系统零基础打造专业网站?
*请认真填写需求信息,我们会在24小时内与您取得联系。