先说下我在使用X5Webview中碰到的一个bug:在华为Mate9 Android8.0.0手机上webview会自动给Html中的图片添加上点击缩放事件。本来这也没什么,但是因为我项目中本来就要实现这个功能,导致图片会被打开两次,体验很不好,目前没在别的手机上发现相同的问题。
解决思路:先去掉自动添加的图片点击事件,然后再自己使用webview和js交互实现图片的点击缩放预览功能
先贴出解决上述bug的关键代码:
private void addImageClickListner(com.tencent.smtt.sdk.WebView webView) {
//遍历页面中所有img的节点,因为节点里面的图片的url即objs[i].src,保存所有图片的src.
//为每个图片设置点击事件,objs[i].onclick
webView.loadUrl(
"javascript:(function(){" +
"var objs = document.getElementsByTagName(\"img\"); " +
"for(var i=0;i<objs.length;i++) " +
"{" +
"objs[i].addEventListener('click',function(e){" +//去掉默认点击事件
"e.preventDefault();" +
"});" +
"window.imageListener.readImageUrl(objs[i].src); " +//添加图片集合
" objs[i].οnclick=function(){ " +
" window.imageListener.openImage(this.src); " +//添加图片点击事件
" } " +
"}" +
"})()");
}
其中解决上述bug的关键代码就是注释中的去掉默认点击事件的代码:
objs[i].addEventListener('click',function(e){" +//去掉默认点击事件
"e.preventDefault();" +
"});
对了,我用的是腾讯出的X5Webview,原生的Webview应该一样
下面是实现webview与js交互实现图片点击预览保存功能的具体实现,已经会用的小伙伴可以忽略了。先上效果图:
先说用到的第三方库:
加载图片使用的是Glide
图片缩放用的是PhotoView:implementation 'com.github.chrisbanes:PhotoView:2.0.0'
首先需要自定义WebViewClient继承自com.tencent.smtt.sdk.WebViewClient(项目中我用的是腾讯的X5Webview,再次重申一次哈哈),在webview加载结束后添加图片的点击事件,就用到了与js交互:
public class MyWebViewClient extends com.tencent.smtt.sdk.WebViewClient {
private static final String TAG = "MyWebViewClient";
@Override
public void onPageStarted(com.tencent.smtt.sdk.WebView webView, String s, Bitmap bitmap) {
super.onPageStarted(webView, s, bitmap);
}
@Override
public boolean shouldOverrideUrlLoading(com.tencent.smtt.sdk.WebView webView, String s) {
//点击webView中的键接,依然在此webview中显示,而不跳转到别的浏览器
webView.loadUrl(s);
return super.shouldOverrideUrlLoading(webView, s);
}
@Override
public void onPageFinished(com.tencent.smtt.sdk.WebView webView, String s) {
super.onPageFinished(webView, s);
addImageClickListner(webView);//当页面加载完成,就调用我们的addImageListener()函数
}
private void addImageClickListner(com.tencent.smtt.sdk.WebView webView) {
//遍历页面中所有img的节点,因为节点里面的图片的url即objs[i].src,保存所有图片的src.
//为每个图片设置点击事件,objs[i].onclick
webView.loadUrl(
"javascript:(function(){" +
"var objs = document.getElementsByTagName(\"img\"); " +
"for(var i=0;i<objs.length;i++) " +
"{" +
"objs[i].addEventListener('click',function(e){" +//去掉默认点击事件
"e.preventDefault();" +
"});" +
"window.imageListener.readImageUrl(objs[i].src); " +//添加图片集合
" objs[i].οnclick=function(){ " +
" window.imageListener.openImage(this.src); " +//添加图片点击事件
" } " +
"}" +
"})()");
}
}
然后自然就需要添加js接口,自定义JavascriptInterface接口:
public class JavascriptInterface {
private Context context;
private ArrayList<String> listimg;
public JavascriptInterface(Context context) {
this.context = context;
listimg=new ArrayList<>();
}
@android.webkit.JavascriptInterface
public void readImageUrl(String img) {
//把所有图片的url保存在ArrayList<String>中
if (!(img.toLowerCase()).endsWith("gif")){
listimg.add(img);
}
}
@android.webkit.JavascriptInterface //对于targetSdkVersion>=17的,要加这个声明
//点击图片所调用到的函数
public void openImage(String clickimg) {
int index = 0;
if (!(clickimg.toLowerCase()).endsWith("gif")){
for(String url:listimg)
if(url.equals(clickimg)) index = listimg.indexOf(clickimg);//获取点击图片在整个页面图片中的位置
Intent intent = new Intent();
Bundle bundle = new Bundle();
bundle.putStringArrayList("list_image", listimg);
bundle.putInt("index", index);
intent.putExtra("bundle", bundle);//将所有图片的url以及点击图片的位置作为参数传给启动的activity
intent.setClass(context, ShowImgActivity.class);
context.startActivity(intent);//ShowImgActivity,用于显示图片
}
}
}
在WebviewActivity中只需要这样调用即可:
//绑定javasrcipt接口,imageListener为接口的别名
wvArticle.addJavascriptInterface(new JavascriptInterface(
ArticleDetailsActivity.this), "imageListener");
wvArticle.loadDataWithBaseURL(null, mArticleContent, "text/html", "utf-8", null);
wvArticle.setWebViewClient(new MyWebViewClient());
最后是在ShowImgActivity中:
public class ShowImgActivity extends BaseActivity {
@BindView(R.id.view_pager)
ViewPager viewPager;
@BindView(R.id.indicator)
TextView mIndicator;
@BindView(R.id.save)
TextView save;
private View mRootView;
private PhotoView mPhotoView;
private Bundle mBundle;
private ArrayList<String> mImgList;
private int mIndex;
private int mCount;
@Override
protected int setLayoutId() {
return R.layout.activity_show_img;
}
@Override
protected void initView() {
mBundle = getIntent().getBundleExtra("bundle");
mImgList = mBundle.getStringArrayList("list_image");
mIndex = mBundle.getInt("index");
mCount = (mImgList == null ? 0 : mImgList.size());
viewPager.setAdapter(new SamplePagerAdapter());
viewPager.setCurrentItem(mIndex);
}
@Override
protected void setListener() {
save.setOnClickListener(this);
}
@Override
protected void initData() {
}
@Override
public void widgetClick(View v) {
switch (v.getId()) {
case R.id.save:
savePhotoToLocal();
break;
default:
break;
}
}
/**
* 保存图片
*/
private void savePhotoToLocal() {
if (mPhotoView != null) {
BitmapDrawable bitmapDrawable = (BitmapDrawable) mPhotoView.getDrawable();
if (bitmapDrawable == null) {
return;
}
Bitmap bitmap = bitmapDrawable.getBitmap();
if (bitmap == null) {
return;
}
FileUtils.savePhoto(this, bitmap, new FileUtils.SaveResultCallback() {
@Override
public void onSavedSuccess() {
runOnUiThread(new Runnable() {
@Override
public void run() {
ToastUtils.showShort("保存成功");
}
});
}
@Override
public void onSavedFailed() {
runOnUiThread(new Runnable() {
@Override
public void run() {
ToastUtils.showShort("保存失败");
}
});
}
});
}
}
class SamplePagerAdapter extends PagerAdapter {
@Override
public int getCount() {
return mCount;
}
@Override
public View instantiateItem(ViewGroup container, int position) {
mRootView = LayoutInflater.from(ShowImgActivity.this).inflate(R.layout.item_show_img, container, false);
mPhotoView = mRootView.findViewById(R.id.photoview);
Glide.with(ShowImgActivity.this)
.load(mImgList.get(position))
.asBitmap()
.placeholder(R.drawable.loading_img)
.error(R.drawable.loading_img)
.fitCenter()
.into(mPhotoView);
container.addView(mRootView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
return mRootView;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
CharSequence text = getString(R.string.viewpager_indicator, position + 1, mCount);
mIndicator.setText(text);
}
}
}
其中用到的FileUtils关键代码:
public class FileUtils {
public static void savePhoto(final Context context, final Bitmap bmp, final SaveResultCallback saveResultCallback) {
final File sdDir = getSDPath();
if (sdDir == null) {
Toast.makeText(context, "设备自带的存储不可用", Toast.LENGTH_SHORT).show();
return;
}
new Thread(new Runnable() {
@Override
public void run() {
File appDir = new File(sdDir, "学管通");
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 static File getSDPath() {
File sdDir = null;
boolean sdCardExist = Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED); //判断sd卡是否存在
if (sdCardExist) {
sdDir = Environment.getExternalStorageDirectory();//获取跟目录
}
return sdDir;
}
public interface SaveResultCallback {
void onSavedSuccess();
void onSavedFailed();
}
}
WebviewActivity中布局文件只有一个webview
ShowImgActivity中布局文件如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000000">
<android.support.v4.view.ViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<TextView
android:id="@+id/indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:padding="15dp"
android:textSize="16sp"
android:textColor="@android:color/white"
android:gravity="center"
android:text="@string/viewpager_indicator"
android:background="#80000000"/>
<TextView
android:id="@+id/save"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:padding="15dp"
android:textSize="16sp"
android:text="保存"
android:textColor="@android:color/white"
android:background="#80000000"/>
</RelativeLayout>
PagerAdapter的item布局只有一个PhotoView
就酱,拜拜