场景
链接:https://upload-images.jianshu.io/upload_images/564272-8c7ae161a750783b.gif?imageMogr2/auto-orient/strip项目中的广告详情页面是个webView加载的富文本,包含图片,需要点击查看大图,长按识别二维码功能。
实现
- webView基础设置
val webView = fragment.binding.webView
webView.settings.javaScriptEnabled = true
webView.settings.builtInZoomControls = false
webView.settings.displayZoomControls = true
webView.settings.setSupportZoom(false)//不支持缩放功能
webView.scrollBarStyle = View.SCROLLBARS_INSIDE_OVERLAY //取消滚动条白边效果
webView.webChromeClient = WebChromeClient()
webView.webViewClient = MyWebViewClient()
webView.addJavascriptInterface(MJavascriptInterface(fragment.activity,fragment),"imagelistener")
webView.settings.defaultTextEncodingName = "UTF-8"
webView.settings.blockNetworkImage = false
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
webView.settings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW //注意安卓5.0以上的权限
}
if (detail.content.checkNotNull()) {
val css = ""
val html = css + "" + detail.content + ""
webView.loadDataWithBaseURL(null, ImageUtil.getNewContent(html), "text/html", "UTF-8", null)
}
webView.setOnLongClickListener(object : View.OnLongClickListener {
override fun onLongClick(v: View?): Boolean {
val result =webView.hitTestResult
if (!result.checkNotNull()) {
return false
}
val type = result.type
when (type) {
// 超链接
WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE,/*带有链接的图片类型*/WebView.HitTestResult.IMAGE_TYPE -> {
// 处理长按图片的菜单项
val coverUrl = result.extra
if (URLUtil.isValidUrl(coverUrl) || coverUrl.startsWith("data:image")) {
UiUtils.webViewLongClick(fragment, webView.context, coverUrl)
return true
}
}
}
return false
}
})
- webViewLongClick
/*网页长按弹窗(识别二维码,下载图片)*/
@JvmStatic
fun webViewLongClick(fragment: BaseFragment, context: Context, coverUrl: String) {
openSelect(fragment, context, coverUrl)
}
/*网页长按弹窗(识别二维码,下载图片)*/
private fun openSelect(fragment: BaseFragment, context: Context, coverUrl: String) {
val dialog = BottomSheetDialog(context)
val layoutView = LayoutInflater.from(context).inflate(R.layout.dialog_select_qrcode_down_img, null)
layoutView.findViewById(R.id.tv_qrcode).setOnClickListener {
//图片url
Glide.with(context.applicationContext).asBitmap().load(coverUrl)
.into(object : SimpleTarget() {
override fun onResourceReady(resource: Bitmap, transition: Transition?) {
CodeUtils.analyzeBitmap(resource, object : CodeUtils.AnalyzeCallback {
override fun onAnalyzeSuccess(mBitmap: Bitmap, result: String) {
fragment.start(WebFragment.newInstance("", result))
}
override fun onAnalyzeFailed() {
"未识别到二维码".toast()
}
})
dialog.dismiss()
}
})
}
layoutView.findViewById(R.id.tv_download).setOnClickListener {
if (coverUrl.startsWith("data:image")) {
var fileName: String? = null
if (MimeTypeMap.getFileExtensionFromUrl(coverUrl) == "") {
fileName = System.currentTimeMillis().toString() + ".jpg"
} else {
fileName = System.currentTimeMillis().toString() + "." + MimeTypeMap.getFileExtensionFromUrl(coverUrl)
}
val file = BitmapUtils.saveBitmapFile(AndroidtoJs.stringToBitmap(coverUrl), DownLoadImageRunnable.SAVE_DOWNLOAD_IMAGE_PATH + fileName)
if (file.checkNotNull()) {
"已保存".toast()
} else {
"保存失败".toast()
}
} else {
ImageUtil.downLoadImg(context, coverUrl)
}
dialog.dismiss()
}
dialog.setContentView(layoutView)
dialog.show()
}
- 弹窗布局dialog_select_qrcode_down_img.xml
- MyWebViewClient
import android.graphics.Bitmap;
import android.webkit.WebView;
import android.webkit.WebViewClient;
/**
* Created by Administrator on 2017/2/10.
*/
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\");" +
" " + " var array=new Array(); " +
" for(var j=0;j
- MJavascriptInterface
import android.app.Activity;
import android.widget.ImageView;
import com.xlj.app.global.base.BaseFragment;
import com.xlj.app.widget.ninegrid.ImageInfo;
import java.util.ArrayList;
public class MJavascriptInterface {
private Activity context;
private BaseFragment fragment;
private static final String TAG = "SIMON";
ArrayList images = new ArrayList<>();
public MJavascriptInterface(Activity context, BaseFragment fragment) {
this.context = context;
this.fragment = fragment;
}
@android.webkit.JavascriptInterface //必须添加
public void openImage(String img, String[] array) {
int index = 0;
ArrayList imageInfo = new ArrayList<>();
for (int i = 0; i < array.length; i++) {
ImageInfo imInfo = new ImageInfo();
imInfo.thumbnailUrl = array[i];
imInfo.bigImageUrl = array[i];
imageInfo.add(imInfo);
if (array[i].equals(img)) {
index = printArray(array, img);
}
}
ImageView imageView = new ImageView(context);
fragment.previewPhoto(imageView, imageInfo, index, false, true,true);
}
//遍历数组
private int printArray(String[] array, String value) {
for (int i = 0; i < array.length; i++) {
if (array[i].equals(value)) {
return i;
}
}
return -1;//当if条件不成立时,默认返回一个负数值-1
}
}
- 大图查看
//图片预览
fun previewPhoto(clickImageView: ImageView, imageInfo: List, index: Int, finishAnimator: Boolean = true, isShowShare: Boolean = false,isSeeCode:Boolean=false) {
if (imageInfo.isNotEmpty()) {
val info: ImageInfo = imageInfo[0]
imageInfo.forEach {
it.imageViewWidth = clickImageView.width
it.imageViewHeight = clickImageView.height
val points = IntArray(2)
clickImageView.getLocationInWindow(points)
info.imageViewX = points[0]
info.imageViewY = points[1] - StatusBarUtil.getStatusBarHeight(activity)
}
val intent = Intent(context, ImagePreviewActivity::class.java)
val bundle = Bundle()
bundle.putSerializable(ImagePreviewActivity.IMAGE_INFO, imageInfo as Serializable)
bundle.putInt(ImagePreviewActivity.CURRENT_ITEM, index)
bundle.putBoolean(ImagePreviewActivity.FINISH_ANIMATION, finishAnimator)
bundle.putBoolean(ImagePreviewActivity.IS_SHOW_SHARE, isShowShare)
bundle.putBoolean(ImagePreviewActivity.IS_SEE_CODE, isSeeCode)
intent.putExtras(bundle)
context!!.startActivity(intent)
activity!!.overridePendingTransition(0, 0)
}
}
- 下面是图片预览的activity -----ImagePreviewActivity
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.view.ViewPager;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.bigkoo.alertview.AlertView;
import com.bigkoo.alertview.OnItemClickListener;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.target.SimpleTarget;
import com.bumptech.glide.request.transition.Transition;
import com.dtr.zxing.utils.CodeUtils;
import com.kaopiz.kprogresshud.KProgressHUD;
import com.tencent.qcloud.tim.uikit.component.photoview.downloadimg.ImageUtil;
import com.tencent.qcloud.tim.uikit.utils.ToastUtil;
import com.umeng.analytics.MobclickAgent;
import com.umeng.socialize.UMShareAPI;
import com.xlj.app.R;
import com.xlj.app.global.Constant;
import com.xlj.app.mvvm.main.activity.WebActivity;
import com.xlj.app.mvvm.main.fragment.WebFragment;
import com.xlj.app.utils.EventBusUtils;
import com.xlj.app.utils.Toast.ToastUtils;
import com.xlj.app.widget.ninegrid.ImageInfo;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.List;
public class ImagePreviewActivity extends Activity implements ViewTreeObserver.OnPreDrawListener {
public static final String IMAGE_INFO = "IMAGE_INFO";
public static final String CURRENT_ITEM = "CURRENT_ITEM";
public static final String FINISH_ANIMATION = "FINISH_ANIMATION";
public static final String IS_SHOW_SHARE = "IS_SHOW_SHARE";
//是否有 识别二维码
public static final String IS_SEE_CODE = "IS_SEE_CODE";
public static final int ANIMATE_DURATION = 200;
private RelativeLayout rootView;
private ImagePreviewAdapter imagePreviewAdapter;
private List imageInfo;
private int currentItem;
private int imageHeight;
private int imageWidth;
private int screenWidth;
private int screenHeight;
private boolean isUseFinishAnimator, isShowShare, isSeeCode;
private KProgressHUD loading;
private String bigImageUrl;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_preview);
loading = KProgressHUD.create(ImagePreviewActivity.this).setStyle(KProgressHUD.Style.SPIN_INDETERMINATE).setWindowColor(ImagePreviewActivity.this.getResources().getColor(R.color.color_trans)).setCancellable(false);
ViewPager viewPager = findViewById(R.id.viewPager);
final TextView tv_pager = findViewById(R.id.tv_pager);
ImageView imgShare = findViewById(R.id.img_share);
rootView = findViewById(R.id.rootView);
DisplayMetrics metric = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metric);
screenWidth = metric.widthPixels;
screenHeight = metric.heightPixels;
Intent intent = getIntent();
imageInfo = (List) intent.getSerializableExtra(IMAGE_INFO);
currentItem = intent.getIntExtra(CURRENT_ITEM, 0);
isUseFinishAnimator = intent.getBooleanExtra(FINISH_ANIMATION, true);
isShowShare = intent.getBooleanExtra(IS_SHOW_SHARE, false);//是否有弹窗菜单(转发。保存)
isSeeCode = intent.getBooleanExtra(IS_SEE_CODE, false);//是否有弹窗菜单(转发。保存)
imgShare.setVisibility(isShowShare ? View.VISIBLE : View.GONE);
imagePreviewAdapter = new ImagePreviewAdapter(this, imageInfo);
imagePreviewAdapter.setIsShowOrginal(isShowShare);
imagePreviewAdapter.setSeeCode(isSeeCode);
viewPager.setAdapter(imagePreviewAdapter);
viewPager.setCurrentItem(currentItem);
viewPager.getViewTreeObserver().addOnPreDrawListener(this);
viewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(int position) {
currentItem = position;
tv_pager.setText(String.format(getString(R.string.select), currentItem + 1, imageInfo.size()));
}
});
tv_pager.setText(String.format(getString(R.string.select), currentItem + 1, imageInfo.size()));
bigImageUrl = imageInfo.get(currentItem).thumbnailUrl;
imgShare.setOnClickListener(v -> {
if (bigImageUrl != null && !"".equals(bigImageUrl)) {
menuPop(v, bigImageUrl);
}
// DialogUtils.umShare(ImagePreviewActivity.this,imgShare.getContext(),bigImageUrl,"分享到", loading);
/* new Thread(() -> ImageUtil.url2Bitmap(ImagePreviewActivity.this, bigImageUrl, new ImageUtil.GetCallBackInterface() {
@Override
public void onSuccess(Bitmap bitmap) {
//点击分享
if (bitmap != null) {
runOnUiThread(() -> DialogUtils.shareWeChatDialog(ImagePreviewActivity.this, bitmap, "分享到").show());
}
}
@Override
public void onFail(String s) {
}
})).start();*/
});
}
private void menuPop(View v, String bigImageUrl) {
if (isSeeCode) {
showSeeCodeDialog(v);
return;
}
new AlertView("操作", null, "取消", null, new String[]{"转发", "保存"}, v.getContext(), AlertView.Style.ActionSheet, new OnItemClickListener() {
@Override
public void onItemClick(Object o, int position) {
if (position == -1) return;
if (position == 0) {
//转发
if (currentItem == -1) return;
EventBusUtils.forwordImgMessage(currentItem);
onBackPressed();
} else {
if (bigImageUrl.contains("http")) {
ImageUtil.downLoadImg(ImagePreviewActivity.this, bigImageUrl);
} else {
try {
FileInputStream fis = new FileInputStream(bigImageUrl);
Bitmap bitmap = BitmapFactory.decodeStream(fis);
boolean succ = ImageUtil.saveImageToGallery(ImagePreviewActivity.this, bitmap);
String tip = "";
if (succ) {
tip = "已保存到相册";
} else {
tip = "保存失败";
}
ToastUtil.toastShortMessage(tip);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
}
}).setCancelable(true).show();
}
private void showSeeCodeDialog(View v) {
new AlertView("操作", null, "取消", null, new String[]{"识别二维码", "保存"}, v.getContext(), AlertView.Style.ActionSheet, new OnItemClickListener() {
@Override
public void onItemClick(Object o, int position) {
if (position == -1) return;
if (position == 0) {
// 识别二维码
Glide.with(v.getContext()).asBitmap().load(bigImageUrl)
.into(new SimpleTarget() {
@Override
public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition super Bitmap> transition) {
CodeUtils.analyzeBitmap(resource, new CodeUtils.AnalyzeCallback() {
@Override
public void onAnalyzeSuccess(Bitmap mBitmap, String result) {
WebActivity.Companion.start(v.getContext(),result,"");
}
@Override
public void onAnalyzeFailed() {
ToastUtils.showShortToast(v.getContext(), "未识别到二维码");
}
});
}
});
} else {
if (bigImageUrl.contains("http")) {
ImageUtil.downLoadImg(ImagePreviewActivity.this, bigImageUrl);
} else {
try {
FileInputStream fis = new FileInputStream(bigImageUrl);
Bitmap bitmap = BitmapFactory.decodeStream(fis);
boolean succ = ImageUtil.saveImageToGallery(ImagePreviewActivity.this, bitmap);
String tip = "";
if (succ) {
tip = "已保存到相册";
} else {
tip = "保存失败";
}
ToastUtil.toastShortMessage(tip);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
}
}).setCancelable(true).show();
}
@Override
public void onBackPressed() {
finishActivityAnim();
}
/**
* 绘制前开始动画
*/
@Override
public boolean onPreDraw() {
rootView.getViewTreeObserver().removeOnPreDrawListener(this);
final View view = imagePreviewAdapter.getPrimaryItem();
final ImageView imageView = imagePreviewAdapter.getPrimaryImageView();
computeImageWidthAndHeight(imageView);
final ImageInfo imageData = imageInfo.get(currentItem);
final float vx = imageData.imageViewWidth * 1.0f / imageWidth;
final float vy = imageData.imageViewHeight * 1.0f / imageHeight;
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1.0f);
valueAnimator.addUpdateListener(animation -> {
long duration = animation.getDuration();
long playTime = animation.getCurrentPlayTime();
float fraction = duration > 0 ? (float) playTime / duration : 1f;
if (fraction > 1) {
fraction = 1;
}
view.setTranslationX(evaluateInt(fraction, imageData.imageViewX + imageData.imageViewWidth / 2 - imageView.getWidth() / 2, 0));
view.setTranslationY(evaluateInt(fraction, imageData.imageViewY + imageData.imageViewHeight / 2 - imageView.getHeight() / 2, 0));
view.setScaleX(evaluateFloat(fraction, vx, 1));
view.setScaleY(evaluateFloat(fraction, vy, 1));
view.setAlpha(fraction);
rootView.setBackgroundColor(evaluateArgb(fraction, Color.TRANSPARENT, Color.BLACK));
});
addIntoListener(valueAnimator);
valueAnimator.setDuration(ANIMATE_DURATION);
valueAnimator.start();
return true;
}
@Override
protected void onResume() {
super.onResume();
MobclickAgent.onPageStart(this.getClass().getSimpleName());
MobclickAgent.onResume(this);
if (loading.isShowing()) {
loading.dismiss();
}
}
@Override
protected void onPause() {
super.onPause();
MobclickAgent.onPageEnd(this.getClass().getSimpleName());
MobclickAgent.onPause(this);
}
public void longClick(View v) {
if (!isShowShare) return;//
if (bigImageUrl != null && !"".equals(bigImageUrl)) {
menuPop(v, bigImageUrl);
}
}
/**
* activity的退场动画
*/
public void finishActivityAnim() {
if (!isUseFinishAnimator) {
finish();
overridePendingTransition(0, 0);
} else {
final View view = imagePreviewAdapter.getPrimaryItem();
final ImageView imageView = imagePreviewAdapter.getPrimaryImageView();
computeImageWidthAndHeight(imageView);
final ImageInfo imageData = imageInfo.get(currentItem);
final float vx = imageData.imageViewWidth * 1.0f / imageWidth;
final float vy = imageData.imageViewHeight * 1.0f / imageHeight;
final ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1.0f);
valueAnimator.addUpdateListener(animation -> {
long duration = animation.getDuration();
long playTime = animation.getCurrentPlayTime();
float fraction = duration > 0 ? (float) playTime / duration : 1f;
if (fraction > 1) {
fraction = 1;
}
view.setTranslationX(evaluateInt(fraction, 0, imageData.imageViewX + imageData.imageViewWidth / 2 - imageView.getWidth() / 2));
view.setTranslationY(evaluateInt(fraction, 0, imageData.imageViewY + imageData.imageViewHeight / 2 - imageView.getHeight() / 2));
view.setScaleX(evaluateFloat(fraction, 1, vx));
view.setScaleY(evaluateFloat(fraction, 1, vy));
view.setAlpha(1 - fraction);
rootView.setBackgroundColor(evaluateArgb(fraction, Color.BLACK, Color.TRANSPARENT));
});
addOutListener(valueAnimator);
valueAnimator.setDuration(ANIMATE_DURATION);
valueAnimator.start();
}
}
/**
* 计算图片的宽高
*/
private void computeImageWidthAndHeight(ImageView imageView) {
// 获取真实大小
Drawable drawable = imageView.getDrawable();
int intrinsicHeight = drawable.getIntrinsicHeight();
int intrinsicWidth = drawable.getIntrinsicWidth();
// 计算出与屏幕的比例,用于比较以宽的比例为准还是高的比例为准,因为很多时候不是高度没充满,就是宽度没充满
float h = screenHeight * 1.0f / intrinsicHeight;
float w = screenWidth * 1.0f / intrinsicWidth;
if (h > w) h = w;
else w = h;
// 得出当宽高至少有一个充满的时候图片对应的宽高
imageHeight = (int) (intrinsicHeight * h);
imageWidth = (int) (intrinsicWidth * w);
}
/**
* 进场动画过程监听
*/
private void addIntoListener(ValueAnimator valueAnimator) {
valueAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
rootView.setBackgroundColor(0x0);
}
@Override
public void onAnimationEnd(Animator animation) {
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
}
/**
* 退场动画过程监听
*/
private void addOutListener(ValueAnimator valueAnimator) {
valueAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
rootView.setBackgroundColor(0x0);
}
@Override
public void onAnimationEnd(Animator animation) {
finish();
overridePendingTransition(0, 0);
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
}
/**
* Integer 估值器
*/
public Integer evaluateInt(float fraction, Integer startValue, Integer endValue) {
int startInt = startValue;
return (int) (startInt + fraction * (endValue - startInt));
}
/**
* Float 估值器
*/
public Float evaluateFloat(float fraction, Number startValue, Number endValue) {
float startFloat = startValue.floatValue();
return startFloat + fraction * (endValue.floatValue() - startFloat);
}
/**
* Argb 估值器
*/
public int evaluateArgb(float fraction, int startValue, int endValue) {
int startA = (startValue >> 24) & 0xff;
int startR = (startValue >> 16) & 0xff;
int startG = (startValue >> 8) & 0xff;
int startB = startValue & 0xff;
int endA = (endValue >> 24) & 0xff;
int endR = (endValue >> 16) & 0xff;
int endG = (endValue >> 8) & 0xff;
int endB = endValue & 0xff;
return (startA + (int) (fraction * (endA - startA))) << 24
| (startR + (int) (fraction * (endR - startR))) << 16
| (startG + (int) (fraction * (endG - startG))) << 8
| (startB + (int) (fraction * (endB - startB)));
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
UMShareAPI.get(this).onActivityResult(requestCode, resultCode, data);
}
}
- 预览的xml文件activity_preview.xml
- 用到了一个自定义的HackyViewPager
import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;
/** 修复图片在ViewPager控件中缩放报错的BUG */
public class HackyViewPager extends ViewPager {
public HackyViewPager(Context context) {
super(context);
}
public HackyViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
try {
return super.onTouchEvent(ev);
} catch (IllegalArgumentException ex) {
ex.printStackTrace();
}
return false;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
try {
return super.onInterceptTouchEvent(ev);
} catch (IllegalArgumentException ex) {
ex.printStackTrace();
}
return false;
}
}
参考链接
- Android 实现WebView点击图片查看大图列表及图片保存
- webview 获取html中所有的图片资源并给图片添加点击事件
- android 获取webview中所有的图片
- Android-通过WebView获取html内容