Android 双击和手势的图片缩放
生活随笔
收集整理的這篇文章主要介紹了
Android 双击和手势的图片缩放
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
2019獨角獸企業重金招聘Python工程師標準>>>
代碼:
package com.mooc.view; import android.content.Context; import android.graphics.Matrix; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.Log; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.ScaleGestureDetector; import android.view.ScaleGestureDetector.OnScaleGestureListener; import android.view.View; import android.view.View.OnTouchListener; import android.view.ViewConfiguration; import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.widget.ImageView; /** * * @author Administrator * */ public class ZoomImageView extends ImageView implements OnGlobalLayoutListener, OnScaleGestureListener, OnTouchListener { /** * 只進行一次的圖片縮放處理 */ private boolean mOnce; /** * 初始化時縮放的值 */ private float mInitScale; /** * 雙擊放大達到的值 */ private float mMidScale; // 縮放的值在mInitScale和mMaxScale之間 /** * 放大的極限 */ private float mMaxScale; /** * 控制縮放位移的矩陣 */ private Matrix mScaleMatrix; // -------------------------------- /** * 捕獲用戶多指觸控時縮放的比例 */ private ScaleGestureDetector mScaleGestureDetector; // -------------------->自由移動------------------ /** * 記錄上一次多點觸控的數量 */ private int mLastPointCount; // 最后觸控的中心點位置 private float mLastX; private float mLastY; /** * 一個值,用于判斷是否是移動的 */ private int mTouchSlop; /** * 是否可以移動 */ private boolean isCanDrag; private boolean isCheckLeftAndRight; private boolean isCheckTopAndBottom; // ----------------------雙擊放大縮小-------- private GestureDetector mGestureDetector; /** * 用于判斷當前是否處于縮放狀態。防止用戶重復雙擊 */ private boolean isAutoScale; public ZoomImageView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // init mScaleMatrix = new Matrix(); setScaleType(ScaleType.MATRIX); mScaleGestureDetector = new ScaleGestureDetector(context, this); // 設置監聽事件 setOnTouchListener(this); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { @Override public boolean onDoubleTap(MotionEvent e) { if (isAutoScale) { return true; } // 點擊的中心 float x = e.getX(); float y = e.getY(); if (getScale() < mMidScale) { // mScaleMatrix.postScale(mMidScale / getScale(), // mMidScale / getScale(), x, y);// 放大到指定大小 // setImageMatrix(mScaleMatrix); postDelayed(new AutoScaleRunnable(mMidScale, x, y), 16); // isAutoScale = true; } else { // 縮小 // mScaleMatrix.postScale(mInitScale / getScale(), // mInitScale / getScale(), x, y);// 放大到指定大小 // setImageMatrix(mScaleMatrix); postDelayed( new AutoScaleRunnable(mInitScale, x, y), 16); // isAutoScale = true; } return true; } }); } /** * 自動放大與縮小 * * @author Zhang * */ private class AutoScaleRunnable implements Runnable { /** * 縮放的目標值 */ private float mTargetScale; // 縮放的中心點 private float x; private float y; /** * 縮放的梯度,每次1.07f 放大,0.93的縮小 */ private final float BIGGER = 1.07f; private final float SMALL = 0.93f; private float temScale;// 臨時 public AutoScaleRunnable(float mTargetScale, float x, float y) { this.mTargetScale = mTargetScale; this.x = x; this.y = y; if (getScale() < mTargetScale) { temScale = BIGGER;// 代表還要放大 } if (getScale() >mTargetScale) { temScale = SMALL; } } @Override public void run() { // 進行縮放 mScaleMatrix.postScale(temScale, temScale, x, y); checkBorderAndCenterWhenScale();// 檢測 setImageMatrix(mScaleMatrix); float currentScale = getScale(); // 當temScale大于1.且當前的scale還小于目標值,可以放大,反之可以縮小 if ((temScale > 1.0f && currentScale < mTargetScale) || (temScale < 1.0f && currentScale > mTargetScale)) { postDelayed(this, 16);// 每16秒執行這個方法,肉眼看起來是順暢的縮放動作 } else { // 達到目標值,設置目標值 float scale = mTargetScale / currentScale; mScaleMatrix.postScale(scale, scale, x, y); checkBorderAndCenterWhenScale(); setImageMatrix(mScaleMatrix); // 結束 isAutoScale = false; } } } public ZoomImageView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public ZoomImageView(Context context) { this(context, null); } /** * 當onAttachedToWindow */ @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); getViewTreeObserver().addOnGlobalLayoutListener(this); } /** * 當onDetachedFromWindow */ @SuppressWarnings("deprecation") @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); getViewTreeObserver().removeGlobalOnLayoutListener(this); } /** * 全局的布局加載完成后,調用此方法 。 獲取ImageView加載完成的圖片,使圖片居中縮放 */ @Override public void onGlobalLayout() { if (!mOnce) { // 得到圖片的寬高 int width = getWidth(); int height = getHeight(); // 得到圖片,以及寬和高 Drawable d = getDrawable(); if (d == null) return; // 拉伸后的寬度.而不是真正圖片的寬度 int dw = d.getIntrinsicWidth(); int dh = d.getIntrinsicHeight(); Log.d("Debug", "width:" + width + ",height:" + height + ",dw:" + dw + ",dh:" + dh); float scale = 1.0f;// 默認縮放的值 // 將圖片的寬高和控件的寬高作對比,如果圖片比較小,則將圖片放大,反之亦然。 // 如果圖片的寬度大于控件的寬度,并且圖片高度小于控件高度 if (dw > width && dh < height) { scale = width * 1.0f / dw;// 圖片太寬,寬度縮放 Log.d("Debug", "圖片寬大,高小"); } if (dh > height && dw < width) { scale = height * 1.0f / dh;// 圖片太高,高度縮放 Log.d("Debug", "圖片高大,寬小"); } // // 如果寬高都大于控件寬高?? // if (dw > width && dh > height) { // scale = Math.min(width * 1.0f / dw, height * 1.0f / dw); // } // // 圖片寬高都小于控件的寬高 上下兩個一樣 // if (dw < width && dh < height) { // scale = Math.min(width * 1.0f / dw, height * 1.0f / dh); // } if (dw < width && dh < height || dw > width && dh > height) { scale = Math.min(width * 1.0f / dw, height * 1.0f / dh); Log.d("Debug", "圖片寬高都大,或都小"); } // 得到初始化縮放的比例 mInitScale = scale;// 原大小 mMaxScale = mInitScale * 4;// 4倍 mMidScale = mInitScale * 2;// 2倍,雙擊放大達到的值 // 將圖片移動到控件的中心 int dx = getWidth() / 2 - dw / 2;// 向x軸移動dx距離 int dy = getHeight() / 2 - dh / 2;// 向y軸移動dx距離 /** * matrix: xScale xSkew xTrans 需要9個 ySkew yScale yTrans 0 0 0 */ mScaleMatrix.postTranslate(dx, dy);// 平移 mScaleMatrix.postScale(mInitScale, mInitScale, width / 2, height / 2);// 縮放,正常顯示width/2,height/2中心 setImageMatrix(mScaleMatrix); mOnce = true; } } /** * 得到當前縮圖片放值 * * @return */ public float getScale() { float[] values = new float[9]; mScaleMatrix.getValues(values); return values[Matrix.MSCALE_X]; } // 縮放---------------------------------- // 縮放的區間,initScale 和maxScale之間 @Override public boolean onScale(ScaleGestureDetector detector) { float scale = getScale(); // 得到縮放的值 // getScaleFactor()這個方法很重要,它的含義是根據你的手勢縮放程度預期得到的圖片大小和當前圖片大小的一個比值, // 當達到最大或最小值時讓縮放的量為1就行,按老師那樣的計算,在縮放到最大值或最小值后,有可能出現不能再縮放的情況 float scaleFactor = detector.getScaleFactor();// 返回從前一個伸縮事件至當前伸縮事件的伸縮比率, if (getDrawable() == null) { return true; } // Log.d("Debug", "scaleFactor--> " + scaleFactor + "."); // 縮放范圍的控制 // 當前縮放在縮放極限內,縮放一因子大于1,說明想放大,小于1說明想縮小 if ((scale < mMaxScale && scaleFactor > 1.0f) || (scale > mInitScale && scaleFactor < 1.0f)) { // 不能小于最小值 if (scale * scaleFactor < mInitScale) { // 小于的話重置 scaleFactor = mInitScale / scale;// scale=mInitScale/scaleFactory } if (scale * scaleFactor > mMaxScale) { // 大于也充值 scaleFactor = mMaxScale / scale;// scale當前縮放 } // 縮放,大的或太小的復原 /* * scale 表示的是當前圖片基于原圖放大的比例 mScaleMatix.postScale(scaleFactor, * scaleFactor, getWidth() / 2,getHeight() / * 2);中的scaleFactor參數表示基于當前已放大的圖片再放大scaleFactor倍 * 。所以圖片的實際放大的大小是原圖的scaleFactor * *scale倍。也就是說,當前方法postScale傳入的參數是scaleFactor * ,則圖片實際基于原圖放大的倍數是scaleFactor*scale 等式scaleFactor=mInitScale/scale * 可以推導出---》 scaleFactor*scale = mInitScale -----》 此時的scaleFactor * 作為postScale的參數,實際圖片的縮放大小就是mInitScale */ mScaleMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY());// detector.getFocusX(), // detector.getFocusY()獲得觸摸的中心點,這樣放大是可以按照觸摸的中心放大,縮小就會有問題,出現圖片顯示偏移。 // 不斷的檢測, checkBorderAndCenterWhenScale(); setImageMatrix(mScaleMatrix); } return true; } /** * 要拿到圖片放大縮小以后的四角的坐標,以及寬高 * * @return */ private RectF getMatrixRectF() { Matrix matrix = mScaleMatrix; RectF rectF = new RectF(); Drawable d = getDrawable(); // 得到圖片放大或縮小以后的寬和高 if (d != null) { rectF.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight()); matrix.mapRect(rectF); } return rectF; } /** * 在縮放的時候進行邊界控制,以及我們的位置的控制 */ private void checkBorderAndCenterWhenScale() { RectF rect = getMatrixRectF(); // 要移動的偏移距離。 float deltaX = 0; float deltaY = 0; int width = getWidth(); int height = getHeight(); // 縮放時,進行邊界檢測,防止出現白邊。 if (rect.width() >= width) {// rect.width()放大或縮小后的寬度 // 放大縮小后的圖片和屏幕左邊的空隙。 if (rect.left > 0) {// 加入圖片沒有完全在屏幕中,而是往右邊移動了一小段距離,rect.left:估計是個坐標 // Log.d("Debug", "rect.left的偏移距離是:" + rect.left); deltaX = -(rect.left); } // 右邊里屏幕有空隙 if (rect.right < width) { deltaX = width - rect.right;// 需要向右邊移動 // Log.d("Debug", " X軸rect.right < width"); } } if (rect.height() >= height) { if (rect.top > 0) { deltaY = -rect.top; // Log.d("Debug", " Y軸rect.height() >= height"); } if (rect.bottom < height) { deltaY = height - rect.bottom; // Log.d("Debug", " Y軸rect.bottom < height"); } } // 如果寬度或高度小于控件的寬度或高度,則讓其居中 if (rect.width() < width) { deltaX = width / 2f - rect.right + rect.width() / 2f;// x軸移動的距離。移到中心點 // Log.d("Debug", " 寬度小于控件的寬度rect.right < width"); } if (rect.height() < height) { deltaY = height / 2f - rect.bottom + rect.height() / 2f; // Log.d("Debug", " 高度小于控件的高度rect.height() < height"); } mScaleMatrix.postTranslate(deltaX, deltaY); } @Override public boolean onScaleBegin(ScaleGestureDetector detector) { return true;// 得return true; } @Override public void onScaleEnd(ScaleGestureDetector detector) { } @Override public boolean onTouch(View v, MotionEvent event) { if (mGestureDetector.onTouchEvent(event)) { return true; } mScaleGestureDetector.onTouchEvent(event);// 給mScaleGestureDetector處理 // 多點觸控的中心點 float x = 0; float y = 0; // 拿到多點觸控的數量 int pointerCount = event.getPointerCount(); for (int i = 0; i < pointerCount; i++) { // 拿到所有點的x,y軸的值,除以pointerCount,得到中心 x += event.getX(i); y += event.getY(i); } x /= pointerCount; y /= pointerCount; // 如果最后觸控點數量,,一開始是不相等的 if (mLastPointCount != pointerCount) { isCanDrag = false; mLastX = x; mLastY = y; } mLastPointCount = pointerCount; switch (event.getAction()) { case MotionEvent.ACTION_MOVE: // 記錄偏移量 float dx = x - mLastX; float dy = y - mLastY; if (!isCanDrag) { isCanDrag = isMoveAction(dx, dy); } // 判斷是否可以移動 if (isCanDrag) { RectF rectF = getMatrixRectF();// 得到縮放后的圖片的寬高 if (getDrawable() != null) { // 默認可以全部檢測 isCheckLeftAndRight = isCheckTopAndBottom = true; // 如果圖片小于控件的寬度 if (rectF.width() < getWidth()) { isCheckLeftAndRight = false; dx = 0;// 不允許橫向移動 } // 如果圖片的高度小于控件的高度 if (rectF.height() < getHeight()) { isCheckTopAndBottom = false;// 不用檢測,不移動的時候 dy = 0;// 不允許縱向移動 } mScaleMatrix.postTranslate(dx, dy); checkBorderWhenTranslate();// 判斷移動時,是否到邊界 setImageMatrix(mScaleMatrix);// 移動 } } // 記錄 mLastX = x; mLastY = y; break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: mLastPointCount = 0; break; } return true; } /** * 當移動時,進行邊界檢查 */ private void checkBorderWhenTranslate() { RectF rectF = getMatrixRectF(); float deltaX = 0; float deltaY = 0; int width = getWidth(); int height = getHeight(); if (rectF.top > 0 && isCheckTopAndBottom) { Log.d("Debug", " top大于0:rectF.top=" + rectF.top); deltaY = -rectF.top; } if (rectF.bottom < height && isCheckTopAndBottom) { deltaY = height - rectF.bottom; Log.d("Debug", " bottom小于高度,rectF.bottom=" + rectF.bottom); } if (rectF.left > 0 && isCheckLeftAndRight) { deltaX = -rectF.left; Log.d("Debug", " left大于0,rectF.left=" + rectF.left); } if (rectF.right < width && isCheckLeftAndRight) { deltaX = width - rectF.right; Log.d("Debug", " 右邊小于寬度,rectF.right=" + rectF.right); } mScaleMatrix.postTranslate(deltaX, deltaY); } /** * 判斷是否足矣觸發移動 * * @param dx * @param dy * @return */ private boolean isMoveAction(float dx, float dy) { return Math.sqrt(dx * dx + dy * dy) > mTouchSlop;// 對角線的長度 } }轉載于:https://my.oschina.net/yongqingfan/blog/803201
總結
以上是生活随笔為你收集整理的Android 双击和手势的图片缩放的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux课程第二十一天学习笔记
- 下一篇: mybatis表关联彻底理解