韦德国际bv1946_www.bv1946com_韦德国际1946手机版
做最好的网站

以下是我的版本

日期:2020-03-02编辑作者:韦德国际bv1946计算机

我之前的个人项目里, 用到了图片浏览, 大图浏览时, 大多都采用第三方的方案, 但是作为一个开发者,我们也需要知道人家到底是怎么做的. 目前大家普遍用的是Github上 PinchImageView ,或者PhotoView ,我们自己也可以实现一个简单版本的.

以下是我的版本

EnhancedImageView

一个增强的自定义ImageView,具备手势放大缩小等功能,主要原理的是 Matrix ScaleGestureDetector GestureDetector 进行对图片进行移动与裁剪

图片 1效果图

目前功能有

  • 单指滑动
  • 多指滑动
  • 双击放大(GestureDetector onDoubleTap)
  • 放大状态双击恢复
  • 自由手势放大 (ScaleGestureDetector.OnScaleGestureListener)
  • 解决与ViewPager滑动冲突冲突原因:ViewPager屏蔽了子View的左右移动事件 解决:在放大状态下: getParent().requestDisallowInterceptTouchEvent;

只是做了上面那些功能, 但是比如滑动时的惯性效果, 以及缩小到比初始状态还小时的动画恢复等, 都没有做. 正常情况下使用的是Github上体验比较棒的 PinchImageView

难点

  1. 在拖动时,边界控制,需要每次触摸后都要判断 RectF里面的参数情况
  2. 放大缩小时的中心点处理

细节很多,需要考虑的东西很多.做出来简单, 做好了很难.

Code

具体工程见:

package com.jerey.imageview;import android.animation.ValueAnimator;import android.content.Context;import android.graphics.Matrix;import android.graphics.RectF;import android.graphics.drawable.Drawable;import android.support.annotation.Nullable;import android.util.AttributeSet;import android.util.Log;import android.view.GestureDetector;import android.view.MotionEvent;import android.view.ScaleGestureDetector;import android.view.View;import android.view.ViewConfiguration;import android.view.ViewTreeObserver;import android.widget.ImageView;/** * OnGlobalLayoutListener 获取控件宽高 */public class EnhancedImageView extends ImageView implements ViewTreeObserver.OnGlobalLayoutListener , ScaleGestureDetector.OnScaleGestureListener , View.OnTouchListener { private static final String TAG = "EnhancedImageView"; private static final boolean DEBUG = true; //初始化标志 private boolean mInitOnce = false; //初始化缩放值 private float mInitScale; private float mMinScale; //双击放大的值 private float mMidScale; //放大最大值 private float mMaxScale; //负责图片的平移缩放 private Matrix mScaleMatrix; //为缩放而生的类,捕获缩放比例 private ScaleGestureDetector mScaleGestureDetector; /****************************自由移动***************/ //记录手指数量 private int mLastPointerCount; //记录上次手指触摸位置 private float mLastX; private float mLastY; //触摸移动距离 private int mTouchSlop; //是否可以拖动 private boolean isCanDrag; //边界检查时用 private boolean isCheckLeftAndRight; private boolean isCheckTopAndBottom; /*****************双击放大********************************/ private GestureDetector mGestureDetector; public EnhancedImageView(Context context) { this(context, null); } public EnhancedImageView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public EnhancedImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mScaleMatrix = new Matrix(); //覆盖用户设置 super.setScaleType(ScaleType.MATRIX); mScaleGestureDetector = new ScaleGestureDetector(context, this); setOnTouchListener; //获取系统默认缩放 mTouchSlop = ViewConfiguration.get.getScaledTouchSlop(); mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { @Override public boolean onDoubleTap(MotionEvent e) { float x = e.getX(); float y = e.getY(); log("当前scale "   getScale()   "mid: "   mMidScale); if (getScale() < mMidScale) { mScaleMatrix.postScale(mMidScale/getScale(), mMidScale/getScale(),getWidth()/2,getHeight; setImageMatrix(mScaleMatrix); } else { log; //计算将图片移动至中间距离 int dx = getWidth() / 2 - getDrawable().getIntrinsicWidth() / 2; int dy = getHeight() / 2 - getDrawable().getIntrinsicHeight() / 2; mScaleMatrix.reset(); mScaleMatrix.postTranslate; mScaleMatrix.postScale(mInitScale, mInitScale,getWidth()/2,getHeight; setImageMatrix(mScaleMatrix); } return true; } }); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); getViewTreeObserver().addOnGlobalLayoutListener; } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); getViewTreeObserver().removeGlobalOnLayoutListener; } /** * 全局布局完成后会调用 */ @Override public void onGlobalLayout() { if (!mInitOnce) { //得到控件的宽和高 int width = getWidth(); int height = getHeight(); //拿到图片的宽高 Drawable drawable = getDrawable(); if (drawable == null) { return; } int drawableWidth = drawable.getIntrinsicWidth(); int drawableHeight = drawable.getIntrinsicHeight(); float scale = 1.0f; //若图片宽度大于控件宽度 高度小于空间高度 if (drawableWidth > width && drawableHeight < height) { log("若图片宽度大于控件宽度 高度小于空间高度"); scale = width * 1.0f / drawableWidth; //图片的高度大于控件高度 宽度小于控件宽度 } else if (drawableHeight > height && drawableWidth < width) { log("图片的高度大于控件高度 宽度小于控件宽度"); scale = height * 1.0f / drawableHeight; } else if (drawableWidth > width && drawableHeight > height) { log; scale = Math.min(width * 1.0f / drawableWidth, height * 1.0f / drawableHeight); } else if (drawableWidth < width && drawableHeight < height) { log; scale = Math.min(width * 1.0f / drawableWidth, height * 1.0f / drawableHeight); } mInitScale = scale; mMinScale = scale; mMidScale = scale * 2; mMaxScale = scale * 5; //计算将图片移动至中间距离 int dx = getWidth() / 2 - drawableWidth / 2; int dy = getHeight() / 2 - drawableHeight / 2; mScaleMatrix.postTranslate; //xy方向不变形,必须传一样的 mScaleMatrix.postScale(mInitScale, mInitScale, width / 2, height / 2); setImageMatrix(mScaleMatrix); mInitOnce = true; } } /** * 获取当前图片的缩放值 * * @return */ public float getScale() { float[] values = new float[9]; mScaleMatrix.getValues; return values[Matrix.MSCALE_X]; } /** * 为缩放而生的类:ScaleGestureDetector * * @param detector * @return */ @Override public boolean onScale(ScaleGestureDetector detector) { float scale = getScale(); float scaleFactor = detector.getScaleFactor(); log("多点触控时候的缩放值: "   scaleFactor); if (getDrawable() == null) { return true; } //缩放范围的控制, 放大时需要小于最大,缩小时需要大于最小 if ((scale < mMaxScale && scaleFactor > 1.0f) || (scale > mMinScale && scaleFactor < 1.0f)) { if (scale * scaleFactor < mMinScale) { scaleFactor = mMinScale / scale; } if (scale * scaleFactor > mMaxScale) { scaleFactor = mMaxScale / scale; } log("设置最终缩放值 "   scaleFactor); mScaleMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY; checkBorderForScale(); setImageMatrix(mScaleMatrix); } return true; } /** * 获得图片放大缩小以后的宽和高 * * @return */ private RectF getMatrixRectF() { Matrix matrix = mScaleMatrix; RectF rectF = new RectF(); Drawable drawable = getDrawable(); if (drawable != null) { rectF.set(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight; //用matrix进行map一下 matrix.mapRect; } return rectF; } /** * 缩放时候进行边界控制等 */ private void checkBorderForScale() { RectF rect = getMatrixRectF(); float deltaX = 0; float deltaY = 0; int width = getWidth(); int height = getHeight(); if (rect.width() >= width) { if (rect.left > 0) { //和屏幕左边有空隙 deltaX = -rect.left; //左边移动 } // 和屏幕as if (rect.right < width) { deltaX = width - rect.right; } } if (rect.height() >= height) { if (rect.top > 0) { deltaY = -rect.top; } if (rect.bottom < height) { deltaY = height - rect.bottom; } } //如果宽度或者高度小于控件的宽和高 居中处理 if (rect.width() < width) { deltaX = getWidth() / 2 - rect.right   rect.width() / 2; } if (rect.height() < height) { deltaY = getHeight() / 2 - rect.bottom   rect.height() / 2; } mScaleMatrix.postTranslate(deltaX, deltaY); } /** * 当移动时,进行边界检查. */ private void checkBorderForTraslate() { RectF rectF = getMatrixRectF(); float deltaX = 0; float deltaY = 0; int width = getWidth(); int height = getHeight(); //上边有空白,往上移动 if (rectF.top > 0 && isCheckTopAndBottom) { deltaY = -rectF.top; } if (rectF.bottom < height && isCheckTopAndBottom) { deltaY = height - rectF.bottom; } //左边和空白往左边移动 if (rectF.left > 0 && isCheckLeftAndRight) { deltaX = -rectF.left; } if (rectF.right < width && isCheckLeftAndRight) { deltaX = width - rectF.right; } mScaleMatrix.postTranslate(deltaX, deltaY); } @Override public boolean onScaleBegin(ScaleGestureDetector detector) { return true; } @Override public void onScaleEnd(ScaleGestureDetector detector) { } private void log(String str) { if  { Log.i; } } /** * 为了让mScaleGestureDetector拿到手势 * * @param v * @param event * @return */ @Override public boolean onTouch(View v, MotionEvent event) { if (mGestureDetector.onTouchEvent { return true; } mScaleGestureDetector.onTouchEvent; /** * 计算多指触控中心点 */ float currentX = 0; float currentY = 0; int pointCount = event.getPointerCount(); for (int i = 0; i < pointCount; i  ) { currentX  = event.getX; currentY  = event.getY; } currentX /= pointCount; currentY /= pointCount; if (mLastPointerCount != pointCount) { isCanDrag = false; mLastX = currentX; mLastY = currentY; } mLastPointerCount = pointCount; RectF rectF = getMatrixRectF(); switch (event.getAction { case MotionEvent.ACTION_DOWN: //请求不被拦截 if(rectF.width() > getWidth() || rectF.height() > getHeight{ getParent().requestDisallowInterceptTouchEvent; } break; case MotionEvent.ACTION_MOVE: if(rectF.width() > (getWidth || rectF.height() > (getHeight){ getParent().requestDisallowInterceptTouchEvent; } float dx = currentX - mLastX; float dy = currentY - mLastY; if (!isCanDrag) { isCanDrag = isMoveAction; } if (isCanDrag) { RectF rectf = getMatrixRectF(); if (getDrawable() != null) { isCheckLeftAndRight = isCheckTopAndBottom = true; //如果宽度小于控件宽度,不允许横向移动 if (rectf.width() < getWidth { dx = 0; isCheckLeftAndRight = false; } //若高度小于控件高度,不允许纵向移动 if (rectf.height() < getHeight { dy = 0; isCheckTopAndBottom = false; } mScaleMatrix.postTranslate; checkBorderForTraslate(); setImageMatrix(mScaleMatrix); } } mLastX = currentX; mLastY = currentY; break; //结束时,将手指数量置0 case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: mLastPointerCount = 0; break; default: break; } return true; } /** * 判断当前移动距离是否大于系统默认最小移动距离 * * @param dx * @param dy * @return */ private boolean isMoveAction(float dx, float dy) { return Math.sqrt(dx * dx   dy * dy) > mTouchSlop; } private void resetToInit(){ //得到控件的宽和高 int width = getWidth(); int height = getHeight(); //拿到图片的宽高 Drawable drawable = getDrawable(); if (drawable == null) { return; } int drawableWidth = drawable.getIntrinsicWidth(); int drawableHeight = drawable.getIntrinsicHeight(); float scale = 1.0f; //若图片宽度大于控件宽度 高度小于空间高度 if (drawableWidth > width && drawableHeight < height) { log("若图片宽度大于控件宽度 高度小于空间高度"); scale = width * 1.0f / drawableWidth; //图片的高度大于控件高度 宽度小于控件宽度 } else if (drawableHeight > height && drawableWidth < width) { log("图片的高度大于控件高度 宽度小于控件宽度"); scale = height * 1.0f / drawableHeight; } else if (drawableWidth > width && drawableHeight > height) { log; scale = Math.min(width * 1.0f / drawableWidth, height * 1.0f / drawableHeight); } else if (drawableWidth < width && drawableHeight < height) { log; scale = Math.min(width * 1.0f / drawableWidth, height * 1.0f / drawableHeight); } mInitScale = scale; mMidScale = scale * 2; mMaxScale = scale * 5; //计算将图片移动至中间距离 int dx = getWidth() / 2 - drawableWidth / 2; int dy = getHeight() / 2 - drawableHeight / 2; mScaleMatrix.postTranslate; ValueAnimator valueAnimator = new ValueAnimator(); }}

图片 2封面图.png

本文作者:Anderson/Jerey_Jobs

博客地址 : 夏敏的博客/Anderson大码渣/Jerey_Jobs 简书地址 : Anderson大码渣 github地址 : Jerey_Jobs

本文由韦德国际bv1946手机版发布于韦德国际bv1946计算机,转载请注明出处:以下是我的版本

关键词: 自定义 手势 来写