先说下我在使用X5Webview中碰到的一个bug:在华为Mate9 Android8.0.0手机上webview会自动给Html中的图片添加上点击缩放事件。本来这也没什么,但是因为我项目中本来就要实现这个功能,导致图片会被打开两次,体验很不好,目前没在别的手机上发现相同的问题。
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其中解决上述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然后自然就需要添加js接口,自定义JavascriptInterface接口:
public class JavascriptInterface { private Context context; private ArrayListlistimg; public JavascriptInterface(Context context) { this.context = context; listimg=new ArrayList<>(); } @android.webkit.JavascriptInterface public void readImageUrl(String img) { //把所有图片的url保存在ArrayList 中 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 ArrayListmImgList;
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中布局文件如下:
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:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
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"/>
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"/>
PagerAdapter的item布局只有一个PhotoView
就酱,拜拜