失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > Android 手势放大图片

Android 手势放大图片

时间:2024-05-21 21:07:58

相关推荐

Android 手势放大图片

效果图:

放大前:

放大后:

ZoomImageView.java

@SuppressLint("AppCompatCustomView")public class ZoomImageView extends ImageView implements ScaleGestureDetector.OnScaleGestureListener, View.OnTouchListener, ViewTreeObserver.OnGlobalLayoutListener {private static final String TAG = ZoomImageView.class.getSimpleName();public static final float SCALE_MAX = 3.0f;private static final float SCALE_MID = 1.5f;/*** 初始化时的缩放比例,如果图片宽或高大于屏幕,此值将小于0*/private float initScale = 1.0f;private boolean once = true;/*** 用于存放矩阵的9个值*/private final float[] matrixValues = new float[9];/*** 缩放的手势检测*/private ScaleGestureDetector mScaleGestureDetector = null;private final Matrix mScaleMatrix = new Matrix();/*** 用于双击检测*/private GestureDetector mGestureDetector;private boolean isAutoScale;private int mTouchSlop;private float mLastX;private float mLastY;private boolean isCanDrag;private int lastPointerCount;private boolean isCheckTopAndBottom = true;private boolean isCheckLeftAndRight = true;public ZoomImageView(Context context) {this(context, null);}public ZoomImageView(Context context, AttributeSet attrs) {super(context, attrs);super.setScaleType(ScaleType.MATRIX);mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {@Overridepublic boolean onDoubleTap(MotionEvent e) {if (isAutoScale == true)return true;float x = e.getX();float y = e.getY();Log.e("DoubleTap", getScale() + " , " + initScale);if (getScale() < SCALE_MID) {//postDelayed(); 16 :多久实现一次的定时器操作ZoomImageView.this.postDelayed(new AutoScaleRunnable(SCALE_MID, x, y), 16);isAutoScale = true;} /*else if (getScale() >= SCALE_MID //连续双击放大 可放开&& getScale() < SCALE_MAX) {ZoomImageView.this.postDelayed(new AutoScaleRunnable(SCALE_MAX, x, y), 16);isAutoScale = true;}*/ else {ZoomImageView.this.postDelayed(new AutoScaleRunnable(initScale, x, y), 16);isAutoScale = true;}return true;}});mScaleGestureDetector = new ScaleGestureDetector(context, this);this.setOnTouchListener(this);}/*** 自动缩放的任务* @author zhy*/private class AutoScaleRunnable implements Runnable {static final float BIGGER = 1.07f;static final float SMALLER = 0.93f;private float mTargetScale;private float tmpScale;/*** 缩放的中心*/private float x;private float y;/*** 传入目标缩放值,根据目标值与当前值,判断应该放大还是缩小* @param targetScale*/public AutoScaleRunnable(float targetScale, float x, float y) {this.mTargetScale = targetScale;this.x = x;this.y = y;if (getScale() < mTargetScale) {tmpScale = BIGGER;} else {tmpScale = SMALLER;}}@Overridepublic void run() {// 进行缩放mScaleMatrix.postScale(tmpScale, tmpScale, x, y);checkBorderAndCenterWhenScale();setImageMatrix(mScaleMatrix);final float currentScale = getScale();// 如果值在合法范围内,继续缩放if (((tmpScale > 1f) && (currentScale < mTargetScale)) || ((tmpScale < 1f) && (mTargetScale < currentScale))) {ZoomImageView.this.postDelayed(this, 16);} else {// 设置为目标的缩放比例final float deltaScale = mTargetScale / currentScale;mScaleMatrix.postScale(deltaScale, deltaScale, x, y);checkBorderAndCenterWhenScale();setImageMatrix(mScaleMatrix);isAutoScale = false;}}}/*** 对图片进行缩放的控制,首先进行缩放范围的判断,然后设置mScaleMatrix的scale值* @param detector* @return*/@SuppressLint("NewApi")@Overridepublic boolean onScale(ScaleGestureDetector detector) {float scale = getScale();float scaleFactor = detector.getScaleFactor();if (getDrawable() == null)return true;/*** 缩放的范围控制*/if ((scale < SCALE_MAX && scaleFactor > 1.0f) || (scale > initScale && scaleFactor < 1.0f)) {/*** 最大值最小值判断*/if (scaleFactor * scale < initScale) {scaleFactor = initScale / scale;}if (scaleFactor * scale > SCALE_MAX) {scaleFactor = SCALE_MAX / scale;}/*** 设置缩放比例*/mScaleMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY());checkBorderAndCenterWhenScale();setImageMatrix(mScaleMatrix);}return true;}/*** 在缩放时,进行图片显示范围的控制*/private void checkBorderAndCenterWhenScale() {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;}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 = width * 0.5f - rect.right + 0.5f * rect.width();}if (rect.height() < height) {deltaY = height * 0.5f - rect.bottom + 0.5f * rect.height();}Log.e(TAG, "deltaX = " + deltaX + " , deltaY = " + deltaY);mScaleMatrix.postTranslate(deltaX, deltaY);}/*** 根据当前图片的Matrix获得图片的范围* @return*/private RectF getMatrixRectF() {Matrix matrix = mScaleMatrix;RectF rect = new RectF();Drawable d = getDrawable();if (null != d) {rect.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());matrix.mapRect(rect);}return rect;}@Overridepublic boolean onScaleBegin(ScaleGestureDetector detector) {return true;}@Overridepublic void onScaleEnd(ScaleGestureDetector detector) {}/*** 我们让OnTouchListener的MotionEvent交给ScaleGestureDetector进行处理* public boolean onTouch(View v, MotionEvent event){* return mScaleGestureDetector.onTouchEvent(event);* }*/@Overridepublic boolean onTouch(View v, MotionEvent event) {if (mGestureDetector.onTouchEvent(event))return true;mScaleGestureDetector.onTouchEvent(event);float x = 0, y = 0;// 拿到触摸点的个数final int pointerCount = event.getPointerCount();// 得到多个触摸点的x与y均值for (int i = 0; i < pointerCount; i++) {x += event.getX(i);y += event.getY(i);}x = x / pointerCount;y = y / pointerCount;/*** 每当触摸点发生变化时,重置mLasX , mLastY*/if (pointerCount != lastPointerCount) {isCanDrag = false;mLastX = x;mLastY = y;}lastPointerCount = pointerCount;RectF rectF = getMatrixRectF();switch (event.getAction()) {case MotionEvent.ACTION_DOWN:if (rectF.width() > getWidth() || rectF.height() > getHeight()) {getParent().requestDisallowInterceptTouchEvent(true);}break;case MotionEvent.ACTION_MOVE:if (rectF.width() > getWidth() || rectF.height() > getHeight()) {getParent().requestDisallowInterceptTouchEvent(true);}Log.e(TAG, "ACTION_MOVE");float dx = x - mLastX;float dy = y - mLastY;if (!isCanDrag) {isCanDrag = isCanDrag(dx, dy);}if (isCanDrag) {if (getDrawable() != null) {// if (getMatrixRectF().left == 0 && dx > 0)// {// getParent().requestDisallowInterceptTouchEvent(false);// }//// if (getMatrixRectF().right == getWidth() && dx < 0)// {// getParent().requestDisallowInterceptTouchEvent(false);// }isCheckLeftAndRight = isCheckTopAndBottom = true;// 如果宽度小于屏幕宽度,则禁止左右移动if (rectF.width() < getWidth()) {dx = 0;isCheckLeftAndRight = false;}// 如果高度小雨屏幕高度,则禁止上下移动if (rectF.height() < getHeight()) {dy = 0;isCheckTopAndBottom = false;}//设置偏移量mScaleMatrix.postTranslate(dx, dy);//再次校验checkMatrixBounds();setImageMatrix(mScaleMatrix);}}mLastX = x;mLastY = y;break;case MotionEvent.ACTION_UP:case MotionEvent.ACTION_CANCEL:Log.e(TAG, "ACTION_UP");lastPointerCount = 0;break;}return true;}/*** 获得当前的缩放比例* @return*/public final float getScale() {mScaleMatrix.getValues(matrixValues);return matrixValues[Matrix.MSCALE_X];}@Overrideprotected void onAttachedToWindow() {super.onAttachedToWindow();getViewTreeObserver().addOnGlobalLayoutListener(this);}@SuppressWarnings("deprecation")@Overrideprotected void onDetachedFromWindow() {super.onDetachedFromWindow();getViewTreeObserver().removeGlobalOnLayoutListener(this);}/*** 根据图片的宽和高以及屏幕的宽和高,对图片进行缩放以及移动至屏幕的中心。* 如果图片很小,那就正常显示,不放大了~*/@Overridepublic void onGlobalLayout() {if (once) {Drawable d = getDrawable();if (d == null)return;Log.e(TAG, d.getIntrinsicWidth() + " , " + d.getIntrinsicHeight());int width = getWidth();int height = getHeight();// 拿到图片的宽和高int dw = d.getIntrinsicWidth();int dh = d.getIntrinsicHeight();float scale = 1.0f;// 如果图片的宽或者高大于屏幕,则缩放至屏幕的宽或者高if (dw > width && dh <= height) {scale = width * 1.0f / dw;}if (dh > height && dw <= width) {scale = height * 1.0f / dh;}// 如果宽和高都大于屏幕,则让其按按比例适应屏幕大小if (dw > width && dh > height) {scale = Math.min(width * 1.0f / dw, height * 1.0f / dh);}initScale = scale;Log.e(TAG, "initScale = " + initScale);mScaleMatrix.postTranslate((width - dw) / 2, (height - dh) / 2);mScaleMatrix.postScale(scale, scale, getWidth() / 2, getHeight() / 2);// 图片移动至屏幕中心setImageMatrix(mScaleMatrix);once = false;}}/*** 移动时,进行边界判断,主要判断宽或高大于屏幕的*/private void checkMatrixBounds() {RectF rect = getMatrixRectF();float deltaX = 0, deltaY = 0;final float viewWidth = getWidth();final float viewHeight = getHeight();// 判断移动或缩放后,图片显示是否超出屏幕边界if (rect.top > 0 && isCheckTopAndBottom) {deltaY = -rect.top;}if (rect.bottom < viewHeight && isCheckTopAndBottom) {deltaY = viewHeight - rect.bottom;}if (rect.left > 0 && isCheckLeftAndRight) {deltaX = -rect.left;}if (rect.right < viewWidth && isCheckLeftAndRight) {deltaX = viewWidth - rect.right;}mScaleMatrix.postTranslate(deltaX, deltaY);}/*** 是否是推动行为** @param dx* @param dy* @return*/private boolean isCanDrag(float dx, float dy) {return Math.sqrt((dx * dx) + (dy * dy)) >= mTouchSlop;}}

MainActivity.java

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="/apk/res/android"xmlns:app="/apk/res-auto"xmlns:tools="/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><com.turbosnail.android_app02.ZoomImageViewandroid:layout_width="300dp"android:layout_height="300dp"android:layout_centerInParent="true"android:src="@drawable/img_activity04_1"tools:ignore="MissingConstraints" /></RelativeLayout>

如果觉得《Android 手势放大图片》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。