91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Android自定義GestureDetector實現手勢ImageView

發布時間:2020-09-10 07:24:48 來源:腳本之家 閱讀:286 作者:vv_小蟲 欄目:移動開發

不說廢話了,進入我們今天的主題吧。

先貼上前面內容的地址:

Android手勢ImageView三部曲(一)

Android手勢ImageView三部曲(二)

Android手勢ImageView三部曲(三)

前面我們講到了ScaleGestureDetector這個工具類,我在疑惑,為什么搞出一個ScaleGestureDetector,不順帶把什么旋轉、移動、做了呢? 好吧~! 谷歌肯定還是想給開發者留一點自己的空間哈。

仿照ScaleGestureDetector,我們來定義一個叫MoveGestureDetector的工具類(專門用于檢測滑動手勢),在定義MoveGestureDetector之前,因為我們還要考慮到之后的RotateGestureDetector等等..于是我們定一個叫BaseGestureDetector把一些公共的方法抽取出來:

public abstract class BaseGestureDetector {
 protected final Context mContext;
 protected boolean mGestureInProgress;

 protected MotionEvent mPrevEvent;
 protected MotionEvent mCurrEvent;

 protected float mCurrPressure;
 protected float mPrevPressure;
 protected long mTimeDelta;


 /**
 * 上一次event的pressure/這一次的pressure,這是一個什么概念呢?
 * 我們想象一下當你手指按下然后滑動并且到離開屏幕,
 * 手指觸碰到屏幕的壓力會越來越小,直到手指移開屏幕
 */
 protected static final float PRESSURE_THRESHOLD = 0.67f;


 public BaseGestureDetector(Context context) {
 mContext = context; 
 }

 /**
 * 跟ScaleGesture一樣,我們也把事件的處理放在此方法中
 * @param event
 * @return
 */
 public boolean onTouchEvent(MotionEvent event){
 //為了獲取到ACTION_POINTER_UP等事件必須加上& MotionEvent.ACTION_MASK
 final int actionCode = event.getAction() & MotionEvent.ACTION_MASK;
 /**
 * 是否調用handleInProgressEvent方法
 */
 if (!mGestureInProgress) {
 //如果mGestureInProgress為false的時候,執行開始操作
 handleStartProgressEvent(actionCode, event);
 } else {
 //處理手勢
 handleInProgressEvent(actionCode, event);
 }
 return true;
 }

 /**
 * 準備處理手勢
 * @param actionCode
 * @param event
 */
 protected abstract void handleStartProgressEvent(int actionCode, MotionEvent event);

 /**
 * 正在處理手勢
 * @param actionCode
 * @param event
 */
 protected abstract void handleInProgressEvent(int actionCode, MotionEvent event);

 /**
 * 更新event的狀態,保存之前的event,獲取當前event
 * @param curr
 */
 protected void updateStateByEvent(MotionEvent curr){
 final MotionEvent prev = mPrevEvent;

 // Reset mCurrEvent
 if (mCurrEvent != null) {
 mCurrEvent.recycle();
 mCurrEvent = null;
 }
 mCurrEvent = MotionEvent.obtain(curr);


 // 之前的event跟現在的event之間的時間差
 mTimeDelta = curr.getEventTime() - prev.getEventTime();

 // 之前的event跟腺癌的event之間的手指壓力值
 mCurrPressure = curr.getPressure(curr.getActionIndex());
 mPrevPressure = prev.getPressure(prev.getActionIndex());
 }

 /**
 * 重置所有狀態
 */
 protected void resetState() {
 if (mPrevEvent != null) {
 mPrevEvent.recycle();
 mPrevEvent = null;
 }
 if (mCurrEvent != null) {
 mCurrEvent.recycle();
 mCurrEvent = null;
 }
 mGestureInProgress = false;
 }


 /**
 * Returns {@code true} if a gesture is currently in progress.
 * @return {@code true} if a gesture is currently in progress, {@code false} otherwise.
 */
 public boolean isInProgress() {
 return mGestureInProgress;
 }

 /**
 * Return the time difference in milliseconds between the previous accepted
 * GestureDetector event and the current GestureDetector event.
 * 
 * @return Time difference since the last move event in milliseconds.
 */
 public long getTimeDelta() {
 return mTimeDelta;
 }

 /**
 * Return the event time of the current GestureDetector event being
 * processed.
 * 
 * @return Current GestureDetector event time in milliseconds.
 */
 public long getEventTime() {
 return mCurrEvent.getEventTime();
 }

}

然后我們定義一個叫MoveGestureDetector的類去繼承BaseGestureDetector,然后事件兩個抽象方法:

public class MoveGestureDetector extends BaseGestureDetector{
 @Override
 protected void handleStartProgressEvent(int actionCode, MotionEvent event){
 }
 @Override
 protected void handleInProgressEvent(int actionCode, MotionEvent event){ 


 }
}

那我們如果檢測到了事件的話該怎么通知調用者呢?是的,我們需要用到回調,我們看看ScaleGestureDetector的回調接口咋定義的:

public interface OnScaleGestureListener {
 public boolean onScale(ScaleGestureDetector detector);
 public boolean onScaleBegin(ScaleGestureDetector detector);
 public void onScaleEnd(ScaleGestureDetector detector);
 }
 public static class SimpleOnScaleGestureListener implements OnScaleGestureListener {

 public boolean onScale(ScaleGestureDetector detector) {
 return false;
 }

 public boolean onScaleBegin(ScaleGestureDetector detector) {
 return true;
 }

 public void onScaleEnd(ScaleGestureDetector detector) {
 // Intentionally empty
 }
 }

里面定義了一個接口一個叫OnScaleGestureListener,一個類叫SimpleOnScaleGestureListener,SimpleOnScaleGestureListener是實現了OnScaleGestureListener,于是我們MoveGestureDetector的接口可以這么定義了:

/**
 * 仿照ScaleGestureDetector我們也定義三個方法
 */
 public interface OnMoveGestureListener {
 /**
 * 移動的時候回調
 */
 public boolean onMove(MoveGestureDetector detector);
 /**
 * 移動開始的時候回調
 */
 public boolean onMoveBegin(MoveGestureDetector detector);
 /**
 * 移動結束的時候回調
 */
 public void onMoveEnd(MoveGestureDetector detector);
 }

 public static class SimpleOnMoveGestureListener implements OnMoveGestureListener {
 public boolean onMove(MoveGestureDetector detector) {
 return false;
 }

 public boolean onMoveBegin(MoveGestureDetector detector) {
 return true;
 }

 public void onMoveEnd(MoveGestureDetector detector) {
 // Do nothing, overridden implementation may be used
 }
 }

好啦!框子都搭好了,我們用的時候呢,就可以這么用了:

1、創建一個MoveGestureDetector

 public MatrixImageView(Context context, AttributeSet attrs) {
 super(context, attrs);
 initView();
 //創建一個縮放手勢監測器
 scaleDetector=new ScaleGestureDetector(context,onScaleGestureListener);
 //創建一個MoveGestureDetector
 moveGestureDetector=new MoveGestureDetector(context,onMoveGestureListener);
 }

2、把事件給MoveGestureDetector

 @Override
 public boolean onTouchEvent(MotionEvent event) {
 //把事件給scaleDetector
 scaleDetector.onTouchEvent(event);
 //把事件給moveGestureDetector
 moveGestureDetector.onTouchEvent(event);
 return true;
 }

3、獲取回調值

private MoveGestureDetector.SimpleOnMoveGestureListener onMoveGestureListener=new MoveGestureDetector.SimpleOnMoveGestureListener(){
 @Override
 public boolean onMove(MoveGestureDetector detector) {

 return super.onMove(detector);
 }
 };

怎么樣?是不是跟ScaleGestureDetector一樣了呢?清晰明了哈,框子是搭起來了,下面我們來實現下它的邏輯(也就是實現下handleStartProgressEvent跟handleInProgressEvent方法):

每行都有注釋,我就直接上代碼了

 */
public class MoveGestureDetector extends BaseGestureDetector {

 /**
 * 仿照ScaleGestureDetector我們也定義三個方法
 */
 public interface OnMoveGestureListener {
 /**
 * 移動的時候回調
 */
 public boolean onMove(MoveGestureDetector detector);
 /**
 * 移動開始的時候回調
 */
 public boolean onMoveBegin(MoveGestureDetector detector);
 /**
 * 移動結束的時候回調
 */
 public void onMoveEnd(MoveGestureDetector detector);
 }

 public static class SimpleOnMoveGestureListener implements OnMoveGestureListener {
 public boolean onMove(MoveGestureDetector detector) {
 return false;
 }

 public boolean onMoveBegin(MoveGestureDetector detector) {
 return true;
 }

 public void onMoveEnd(MoveGestureDetector detector) {
 // Do nothing, overridden implementation may be used
 }
 }

 private static final PointF FOCUS_DELTA_ZERO = new PointF();

 private final OnMoveGestureListener mListener;

 private PointF mCurrFocusInternal;
 private PointF mPrevFocusInternal; 
 private PointF mFocusExternal = new PointF();
 private PointF mFocusDeltaExternal = new PointF();


 public MoveGestureDetector(Context context, OnMoveGestureListener listener) {
 super(context);
 mListener = listener;
 }

 @Override
 protected void handleStartProgressEvent(int actionCode, MotionEvent event){
 switch (actionCode) {
 //當手指按下的時候
 case MotionEvent.ACTION_DOWN:
 //重置一下所有狀態(currevent跟preevent)
 resetState(); // In case we missed an UP/CANCEL event
 //獲取當前event作為mPrevEvent
 mPrevEvent = MotionEvent.obtain(event);
 //重置兩次event的時間間隔
 mTimeDelta = 0;
 //更新state
 updateStateByEvent(event);
 break;

 case MotionEvent.ACTION_MOVE:
 //回調onMoveBegin,mGestureInProgress決定是否繼續處理事件(執行handleInProgressEvent)
 //mGestureInProgress由調用者決定
 mGestureInProgress = mListener.onMoveBegin(this);
 break;
 }
 }

 /**
 * 處理移動事件
 */
 @Override
 protected void handleInProgressEvent(int actionCode, MotionEvent event){ 
 switch (actionCode) {
 //當抬起或者取消的時候
 case MotionEvent.ACTION_UP:
 case MotionEvent.ACTION_CANCEL:
 //回調onMoveEnd,move處理結束
 mListener.onMoveEnd(this);
 //重置所有的state
 resetState();
 break;

 case MotionEvent.ACTION_MOVE:
 //更新狀態
 updateStateByEvent(event);
 //當上一次event的press值/這一次event值大于臨界值的時候開始觸發onMove
 //因為如果CurrPressure / mPrevPressure很小的話,可能手指已經離開屏幕了
 if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD) {
 /**
 * 回調onMove方法,并獲取updatePrevious
 * updatePrevious標記是由調用者決定,
 * updatePrevious是否更新之前的event,
 * 如果為false的話mPrevEvent一直是我們在down的時候賦值的event
 * 如果為true的話,每次move事件處理完都會把最新的event賦給mPrevEvent
 */
 final boolean updatePrevious = mListener.onMove(this);
 if (updatePrevious) {
 mPrevEvent.recycle();
 mPrevEvent = MotionEvent.obtain(event);
 }
 }
 break;
 }
 }

 /**
 * 參考ScaleGestureDetector
 * move核心處理方法
 * 重寫父類的updateStateByEvent
 *
 */

 protected void updateStateByEvent(MotionEvent curr) {
 super.updateStateByEvent(curr);

 final MotionEvent prev = mPrevEvent;

 // 獲取當前所有手指的中心點
 mCurrFocusInternal = determineFocalPoint(curr);
 //獲取之前event所有手指的中心點
 mPrevFocusInternal = determineFocalPoint(prev);

 //判斷是否有手指中途添加或者移除
 boolean mSkipNextMoveEvent = prev.getPointerCount() != curr.getPointerCount();
 //有移除的話mFocusDeltaExternal就等于空(0,0),沒有的話就算出前面event跟當前event中心點距離
 mFocusDeltaExternal = mSkipNextMoveEvent ? FOCUS_DELTA_ZERO : new PointF(mCurrFocusInternal.x - mPrevFocusInternal.x, mCurrFocusInternal.y - mPrevFocusInternal.y);
 //累加距離值
 mFocusExternal.x += mFocusDeltaExternal.x;
 mFocusExternal.y += mFocusDeltaExternal.y; 
 }

 /**
 * 獲取所有手指的中間點坐標(參考ScaleGestureDetector)
 */
 private PointF determineFocalPoint(MotionEvent e){
 // Number of fingers on screen
 final int pCount = e.getPointerCount(); 
 float x = 0f;
 float y = 0f;

 for(int i = 0; i < pCount; i++){
 x += e.getX(i);
 y += e.getY(i);
 }

 return new PointF(x/pCount, y/pCount);
 }

 /**
 * 獲取距離值累加過后的值
 */
 public float getFocusX() {
 return mFocusExternal.x;
 }

 public float getFocusY() {
 return mFocusExternal.y;
 }

 /**
 * 獲取上一個事件到下一個事件之間的x跟y的距離值
 */
 public PointF getFocusDelta() {
 return mFocusDeltaExternal;
 }

}

好啦!!寫完哈,我們來使用一下:

package com.leo.gestureimageview;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.widget.ImageView;

import com.leo.gestureimageview.GestureDetectors.MoveGestureDetector;

public class MatrixImageView extends ImageView {
 private Matrix currMatrix;
 private float scaleFactor=1f;//當前圖片的縮放值

 private float transX,transY;
 private ScaleGestureDetector scaleDetector;
 private MoveGestureDetector moveGestureDetector;
 public MatrixImageView(Context context, AttributeSet attrs) {
 super(context, attrs);
 initView();
 //創建一個縮放手勢監測器
 scaleDetector=new ScaleGestureDetector(context,onScaleGestureListener);
 //創建一個MoveGestureDetector
 moveGestureDetector=new MoveGestureDetector(context,onMoveGestureListener);
 }

 private void initView() {
 currMatrix = new Matrix();
 DisplayMetrics dm = getResources().getDisplayMetrics();
 Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test);
 bitmap = Bitmap.createScaledBitmap(bitmap, dm.widthPixels, dm.heightPixels, true);
 setImageBitmap(bitmap);
 }
 @Override
 public boolean onTouchEvent(MotionEvent event) {
 //把事件給scaleDetector
 scaleDetector.onTouchEvent(event);
 //把事件給moveGestureDetector
 moveGestureDetector.onTouchEvent(event);
 return true;
 }
 private void setMatrix(){
 currMatrix.reset();
 currMatrix.postScale(scaleFactor,scaleFactor,getMeasuredWidth()/2,getMeasuredHeight()/2);
 currMatrix.postTranslate(transX,transY);
 setImageMatrix(currMatrix);
 }
 private ScaleGestureDetector.SimpleOnScaleGestureListener onScaleGestureListener=new ScaleGestureDetector.SimpleOnScaleGestureListener(){
 @Override
 public boolean onScale(ScaleGestureDetector detector) {
 scaleFactor *= detector.getScaleFactor(); // scale change since previous event
 // Don't let the object get too small or too large.
 scaleFactor = Math.max(0.1f, Math.min(scaleFactor, 10.0f));
 setMatrix();
 /**
 * 因為getScaleFactor=當前兩個手指之間的距離(preEvent)/手指按下時候兩個點的距離(currEvent)
 * 這里如果返回true的話,會在move操作的時候去更新之前的event,
 * 如果為false的話,不會去更新之前按下時候保存的event
 */
 return true;
 }
 };
 private MoveGestureDetector.SimpleOnMoveGestureListener onMoveGestureListener=new MoveGestureDetector.SimpleOnMoveGestureListener(){
 @Override
 public boolean onMove(MoveGestureDetector detector) {
 transX=detector.getFocusX();
 transY=detector.getFocusY();
 setMatrix();
 return true;
 }
 };
}

好啦~!! 短短幾行代碼就可以玩起來了,效果圖我就不附了哈,小伙伴自己運行一下,那么MoveGestureDetector我們實現了,想必RotateGestureDetector也是很快就會實現了,哈哈~~! 我就直接用貼上國外大神寫的代碼了:

public class RotateGestureDetector extends TwoFingerGestureDetector {

 /**
 * Listener which must be implemented which is used by RotateGestureDetector
 * to perform callbacks to any implementing class which is registered to a
 * RotateGestureDetector via the constructor.
 * 
 * @see SimpleOnRotateGestureListener
 */
 public interface OnRotateGestureListener {
 public boolean onRotate(RotateGestureDetector detector);
 public boolean onRotateBegin(RotateGestureDetector detector);
 public void onRotateEnd(RotateGestureDetector detector);
 }

 /**
 * Helper class which may be extended and where the methods may be
 * implemented. This way it is not necessary to implement all methods
 * of OnRotateGestureListener.
 */
 public static class SimpleOnRotateGestureListener implements OnRotateGestureListener {
 public boolean onRotate(RotateGestureDetector detector) {
 return false;
 }

 public boolean onRotateBegin(RotateGestureDetector detector) {
 return true;
 }

 public void onRotateEnd(RotateGestureDetector detector) {
 // Do nothing, overridden implementation may be used
 }
 }


 private final OnRotateGestureListener mListener;
 private boolean mSloppyGesture;

 public RotateGestureDetector(Context context, OnRotateGestureListener listener) {
 super(context);
 mListener = listener;
 }

 @Override
 protected void handleStartProgressEvent(int actionCode, MotionEvent event){
 switch (actionCode) {
 case MotionEvent.ACTION_POINTER_DOWN:
 // At least the second finger is on screen now

 resetState(); // In case we missed an UP/CANCEL event
 mPrevEvent = MotionEvent.obtain(event);
 mTimeDelta = 0;

 updateStateByEvent(event);

 // See if we have a sloppy gesture
 mSloppyGesture = isSloppyGesture(event);
 if(!mSloppyGesture){
 // No, start gesture now
 mGestureInProgress = mListener.onRotateBegin(this);
 } 
 break;

 case MotionEvent.ACTION_MOVE:
 if (!mSloppyGesture) {
 break;
 }

 // See if we still have a sloppy gesture
 mSloppyGesture = isSloppyGesture(event);
 if(!mSloppyGesture){
 // No, start normal gesture now
 mGestureInProgress = mListener.onRotateBegin(this);
 }

 break;

 case MotionEvent.ACTION_POINTER_UP:
 if (!mSloppyGesture) {
 break;
 }

 break; 
 }
 }


 @Override
 protected void handleInProgressEvent(int actionCode, MotionEvent event){ 
 switch (actionCode) {
 case MotionEvent.ACTION_POINTER_UP:
 // Gesture ended but 
 updateStateByEvent(event);

 if (!mSloppyGesture) {
 mListener.onRotateEnd(this);
 }

 resetState();
 break;

 case MotionEvent.ACTION_CANCEL:
 if (!mSloppyGesture) {
 mListener.onRotateEnd(this);
 }

 resetState();
 break;

 case MotionEvent.ACTION_MOVE:
 updateStateByEvent(event);

 // Only accept the event if our relative pressure is within
 // a certain limit. This can help filter shaky data as a
 // finger is lifted.
 if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD) {
 final boolean updatePrevious = mListener.onRotate(this);
 if (updatePrevious) {
 mPrevEvent.recycle();
 mPrevEvent = MotionEvent.obtain(event);
 }
 }
 break;
 }
 }

 @Override
 protected void resetState() {
 super.resetState();
 mSloppyGesture = false;
 }


 /**
 * Return the rotation difference from the previous rotate event to the current
 * event. 
 * 
 * @return The current rotation //difference in degrees.
 */
 public float getRotationDegreesDelta() {
 double diffRadians = Math.atan2(mPrevFingerDiffY, mPrevFingerDiffX) - Math.atan2(mCurrFingerDiffY, mCurrFingerDiffX);
 return (float) (diffRadians * 180 / Math.PI);
 }
}

最后把我們結合了ScaleDetector、MoveDetector、RotateDetector的一個手勢縮放ImageView的代碼給大家:

package com.leo.gestureimageview;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.widget.ImageView;

import com.leo.gestureimageview.GestureDetectors.MoveGestureDetector;
import com.leo.gestureimageview.GestureDetectors.RotateGestureDetector;

public class MatrixImageView2 extends ImageView {
 private Matrix mMatrix = new Matrix();
 private float mScaleFactor =1f;
 private float mRotationDegrees = 0.f;
 private float mFocusX = 0.f;
 private float mFocusY = 0.f;


 private ScaleGestureDetector mScaleDetector;
 private RotateGestureDetector mRotateDetector;
 private MoveGestureDetector mMoveDetector;
 public MatrixImageView2(Context context, AttributeSet attrs) {
 super(context, attrs);
 initView();
 }

 private void initView() {
 //初始化模式為初始狀態
 DisplayMetrics dm = getResources().getDisplayMetrics();
 //給ImageView設置一張圖片(此處為了測試直接在imageview里面設置了一張測試圖片)
 Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test);
 bitmap = Bitmap.createScaledBitmap(bitmap, dm.widthPixels, dm.heightPixels, true);
 setImageBitmap(bitmap);
 mScaleDetector = new ScaleGestureDetector(getContext(), new ScaleListener());
 mRotateDetector = new RotateGestureDetector(getContext(), new RotateListener());
 mMoveDetector = new MoveGestureDetector(getContext(), new MoveListener());
 mFocusX = dm.widthPixels/2f;
 mFocusY = dm.heightPixels/2f;

 }

 @Override
 public boolean onTouchEvent(MotionEvent event) {
 //把縮放事件給mScaleDetector
 mScaleDetector.onTouchEvent(event);
 //把旋轉事件個mRotateDetector
 mRotateDetector.onTouchEvent(event);
 //把移動事件給mMoveDetector
 mMoveDetector.onTouchEvent(event);
 return true;
 }
 private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
 @Override
 public boolean onScale(ScaleGestureDetector detector) {
 mScaleFactor *= detector.getScaleFactor(); // scale change since previous event
 // Don't let the object get too small or too large.
 mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 10.0f));
 changeMatrix();
 return true;
 }
 }
 private class RotateListener extends RotateGestureDetector.SimpleOnRotateGestureListener {
 @Override
 public boolean onRotate(RotateGestureDetector detector) {
 mRotationDegrees -= detector.getRotationDegreesDelta();
 changeMatrix();
 return true;
 }
 }
 private class MoveListener extends MoveGestureDetector.SimpleOnMoveGestureListener {
 @Override
 public boolean onMove(MoveGestureDetector detector) {
 PointF d = detector.getFocusDelta();
 mFocusX += d.x;
 mFocusY += d.y;
 changeMatrix();
 return true;
 }

 }
 private void changeMatrix(){
 float scaledImageCenterX = (getDrawable().getIntrinsicWidth()*mScaleFactor)/2;
 float scaledImageCenterY = (getDrawable().getIntrinsicHeight()*mScaleFactor)/2;
 mMatrix.reset();
 mMatrix.postScale(mScaleFactor, mScaleFactor);
 mMatrix.postRotate(mRotationDegrees, scaledImageCenterX, scaledImageCenterY);
 mMatrix.postTranslate(mFocusX - scaledImageCenterX, mFocusY - scaledImageCenterY);
 setImageMatrix(mMatrix);
 }
}

好啦~~~小伙伴也可以自己下載一下這個框架的代碼去研究,我這呢也只是把自己學習的心得分享給大家。
https://github.com/Almeros/android-gesture-detectors

嗯嗯!說了那么多,最后讓我們看看傳說中的PhotoView到底是咋實現的。

photoview的github鏈接:
https://github.com/chrisbanes/PhotoViewary/

看完我們之前的內容,再去看PhotoView的話,你可能不會那么迷茫了,下面讓我們一起揭開它的神秘面紗:

首先PhotoView的用法呢,很簡單,小伙伴像用ImageView一樣用它就可以了:

<uk.co.senab.photoview.PhotoView
 android:clickable="true"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:scaleType="fitxy"
 />

好啦!!現在就可以對圖片進行縮放、旋轉、移動操作啦~是不是很爽呢?

但是注意:

photoview的縮放類型不支持,不然就直接報錯退出了:

android:scaleType="matrix"

我們來看看它的源碼:

public class PhotoView extends ImageView implements IPhotoView {

 private PhotoViewAttacher mAttacher;

 private ScaleType mPendingScaleType;

 public PhotoView(Context context) {
 this(context, null);
 }

 public PhotoView(Context context, AttributeSet attr) {
 this(context, attr, 0);
 }

 public PhotoView(Context context, AttributeSet attr, int defStyle) {
 super(context, attr, defStyle);
 super.setScaleType(ScaleType.MATRIX);
 init();
 }

 protected void init() {
 if (null == mAttacher || null == mAttacher.getImageView()) {
 mAttacher = new PhotoViewAttacher(this);
 }

 if (null != mPendingScaleType) {
 setScaleType(mPendingScaleType);
 mPendingScaleType = null;
 }
 }

 @Override
 public void setRotationTo(float rotationDegree) {
 mAttacher.setRotationTo(rotationDegree);
 }

 @Override
 public void setRotationBy(float rotationDegree) {
 mAttacher.setRotationBy(rotationDegree);
 }

 @Override
 public boolean canZoom() {
 return mAttacher.canZoom();
 }

 @Override
 public RectF getDisplayRect() {
 return mAttacher.getDisplayRect();
 }

 @Override
 public void getDisplayMatrix(Matrix matrix) {
 mAttacher.getDisplayMatrix(matrix);
 }

 @Override
 public boolean setDisplayMatrix(Matrix finalRectangle) {
 return mAttacher.setDisplayMatrix(finalRectangle);
 }

 @Override
 public float getMinimumScale() {
 return mAttacher.getMinimumScale();
 }

 @Override
 public float getMediumScale() {
 return mAttacher.getMediumScale();
 }

 @Override
 public float getMaximumScale() {
 return mAttacher.getMaximumScale();
 }

 @Override
 public float getScale() {
 return mAttacher.getScale();
 }

 @Override
 public ScaleType getScaleType() {
 return mAttacher.getScaleType();
 }

 @Override
 public Matrix getImageMatrix() {
 return mAttacher.getImageMatrix();
 }

 @Override
 public void setAllowParentInterceptOnEdge(boolean allow) {
 mAttacher.setAllowParentInterceptOnEdge(allow);
 }

 @Override
 public void setMinimumScale(float minimumScale) {
 mAttacher.setMinimumScale(minimumScale);
 }

 @Override
 public void setMediumScale(float mediumScale) {
 mAttacher.setMediumScale(mediumScale);
 }

 @Override
 public void setMaximumScale(float maximumScale) {
 mAttacher.setMaximumScale(maximumScale);
 }

 @Override
 public void setScaleLevels(float minimumScale, float mediumScale, float maximumScale) {
 mAttacher.setScaleLevels(minimumScale, mediumScale, maximumScale);
 }

 @Override
 // setImageBitmap calls through to this method
 public void setImageDrawable(Drawable drawable) {
 super.setImageDrawable(drawable);
 if (null != mAttacher) {
 mAttacher.update();
 }
 }

 @Override
 public void setImageResource(int resId) {
 super.setImageResource(resId);
 if (null != mAttacher) {
 mAttacher.update();
 }
 }

 @Override
 public void setImageURI(Uri uri) {
 super.setImageURI(uri);
 if (null != mAttacher) {
 mAttacher.update();
 }
 }

 @Override
 protected boolean setFrame(int l, int t, int r, int b) {
 boolean changed = super.setFrame(l, t, r, b);
 if (null != mAttacher) {
 mAttacher.update();
 }
 return changed;
 }

 @Override
 public void setOnMatrixChangeListener(OnMatrixChangedListener listener) {
 mAttacher.setOnMatrixChangeListener(listener);
 }

 @Override
 public void setOnLongClickListener(OnLongClickListener l) {
 mAttacher.setOnLongClickListener(l);
 }

 @Override
 public void setOnPhotoTapListener(OnPhotoTapListener listener) {
 mAttacher.setOnPhotoTapListener(listener);
 }

 @Override
 public void setOnViewTapListener(OnViewTapListener listener) {
 mAttacher.setOnViewTapListener(listener);
 }

 @Override
 public void setScale(float scale) {
 mAttacher.setScale(scale);
 }

 @Override
 public void setScale(float scale, boolean animate) {
 mAttacher.setScale(scale, animate);
 }

 @Override
 public void setScale(float scale, float focalX, float focalY, boolean animate) {
 mAttacher.setScale(scale, focalX, focalY, animate);
 }

 @Override
 public void setScaleType(ScaleType scaleType) {
 if (null != mAttacher) {
 mAttacher.setScaleType(scaleType);
 } else {
 mPendingScaleType = scaleType;
 }
 }

 @Override
 public void setZoomable(boolean zoomable) {
 mAttacher.setZoomable(zoomable);
 }

 @Override
 public Bitmap getVisibleRectangleBitmap() {
 return mAttacher.getVisibleRectangleBitmap();
 }

 @Override
 public void setZoomTransitionDuration(int milliseconds) {
 mAttacher.setZoomTransitionDuration(milliseconds);
 }

 @Override
 public IPhotoView getIPhotoViewImplementation() {
 return mAttacher;
 }

 @Override
 public void setOnDoubleTapListener(GestureDetector.OnDoubleTapListener newOnDoubleTapListener) {
 mAttacher.setOnDoubleTapListener(newOnDoubleTapListener);
 }

 @Override
 public void setOnScaleChangeListener(PhotoViewAttacher.OnScaleChangeListener onScaleChangeListener) {
 mAttacher.setOnScaleChangeListener(onScaleChangeListener);
 }

 @Override
 public void setOnSingleFlingListener(PhotoViewAttacher.OnSingleFlingListener onSingleFlingListener) {
 mAttacher.setOnSingleFlingListener(onSingleFlingListener);
 }

 @Override
 protected void onDetachedFromWindow() {
 mAttacher.cleanup();
 mAttacher = null;
 super.onDetachedFromWindow();
 }

 @Override
 protected void onAttachedToWindow() {
 init();
 super.onAttachedToWindow();
 }
}

可以看到,代碼并不多,才200多行(哈哈!!我們自己實現的MatrixImageView 100行都還不到呢!!開玩笑哈,PhotoView里面考慮的東西跟兼容性,我們寫的MatrixImageView遠遠不及哈),主要的處理所及都在PhotoViewAttacher這個類中:

PhotoViewAttacher.java:

代碼太多,我們看看它的構造方法

 public PhotoViewAttacher(ImageView imageView, boolean zoomable) {
 mImageView = new WeakReference<>(imageView);

 imageView.setDrawingCacheEnabled(true);
 imageView.setOnTouchListener(this);

 ViewTreeObserver observer = imageView.getViewTreeObserver();
 if (null != observer)
 observer.addOnGlobalLayoutListener(this);

 // Make sure we using MATRIX Scale Type
 setImageViewScaleTypeMatrix(imageView);

 if (imageView.isInEditMode()) {
 return;
 }
 // Create Gesture Detectors...
 mScaleDragDetector = VersionedGestureDetector.newInstance(
 imageView.getContext(), this);

 mGestureDetector = new GestureDetector(imageView.getContext(),
 new GestureDetector.SimpleOnGestureListener() {

 // forward long click listener
 @Override
 public void onLongPress(MotionEvent e) {
 if (null != mLongClickListener) {
 mLongClickListener.onLongClick(getImageView());
 }
 }

 @Override
 public boolean onFling(MotionEvent e1, MotionEvent e2,
  float velocityX, float velocityY) {
 if (mSingleFlingListener != null) {
 if (getScale() > DEFAULT_MIN_SCALE) {
 return false;
 }

 if (MotionEventCompat.getPointerCount(e1) > SINGLE_TOUCH
  || MotionEventCompat.getPointerCount(e2) > SINGLE_TOUCH) {
 return false;
 }

 return mSingleFlingListener.onFling(e1, e2, velocityX, velocityY);
 }
 return false;
 }
 });

 mGestureDetector.setOnDoubleTapListener(new DefaultOnDoubleTapListener(this));
 mBaseRotation = 0.0f;

 // Finally, update the UI so that we're zoomable
 setZoomable(zoomable);
 }

可以看到,它也是創建了一個mScaleDragDetector跟一個mGestureDetector用于監聽手勢變幻,那么事件處理在什么地方呢?

我們在構造方法還發現了一行代碼,給當前imageView設置觸碰監聽:

imageView.setOnTouchListener(this);

小伙伴猜都猜到了,現在就是把事件給事件監聽器了:

@Override
 public boolean onTouch(View v, MotionEvent ev) {
 boolean handled = false;

 if (mZoomEnabled && hasDrawable((ImageView) v)) {
 ViewParent parent = v.getParent();
 switch (ev.getAction()) {
 case ACTION_DOWN:
 // First, disable the Parent from intercepting the touch
 // event
 if (null != parent) {
 parent.requestDisallowInterceptTouchEvent(true);
 } else {
 LogManager.getLogger().i(LOG_TAG, "onTouch getParent() returned null");
 }

 // If we're flinging, and the user presses down, cancel
 // fling
 cancelFling();
 break;

 case ACTION_CANCEL:
 case ACTION_UP:
 // If the user has zoomed less than min scale, zoom back
 // to min scale
 if (getScale() < mMinScale) {
 RectF rect = getDisplayRect();
 if (null != rect) {
 v.post(new AnimatedZoomRunnable(getScale(), mMinScale,
  rect.centerX(), rect.centerY()));
 handled = true;
 }
 }
 break;
 }

 // Try the Scale/Drag detector
 if (null != mScaleDragDetector) {
 boolean wasScaling = mScaleDragDetector.isScaling();
 boolean wasDragging = mScaleDragDetector.isDragging();

 handled = mScaleDragDetector.onTouchEvent(ev);

 boolean didntScale = !wasScaling && !mScaleDragDetector.isScaling();
 boolean didntDrag = !wasDragging && !mScaleDragDetector.isDragging();

 mBlockParentIntercept = didntScale && didntDrag;
 }

 // Check to see if the user double tapped
 if (null != mGestureDetector && mGestureDetector.onTouchEvent(ev)) {
 handled = true;
 }

 }

 return handled;
 }

最后處理完畢事件后,就是一系列的回調了,回調完畢后就應該給ImageView重新設置matrix對象了,比如縮放:

@Override
 public void setScale(float scale, float focalX, float focalY,
 boolean animate) {
 ImageView imageView = getImageView();

 if (null != imageView) {
 // Check to see if the scale is within bounds
 if (scale < mMinScale || scale > mMaxScale) {
 LogManager
 .getLogger()
 .i(LOG_TAG,
 "Scale must be within the range of minScale and maxScale");
 return;
 }

 if (animate) {
 imageView.post(new AnimatedZoomRunnable(getScale(), scale,
 focalX, focalY));
 } else {
 mSuppMatrix.setScale(scale, scale, focalX, focalY);
 checkAndDisplayMatrix();
 }
 }
 }

其它的類似哈~~~ 代碼還是挺多的(考慮的情況比較多)可想而之,要寫好一個自定義組件還不是那么簡單的事哦,不過還是加油吧~!

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

什邡市| 阜城县| 永昌县| 比如县| 北川| 台江县| 达孜县| 甘谷县| 菏泽市| 凤庆县| 麻城市| 孝义市| 共和县| 喀什市| 龙南县| 罗田县| 奉化市| 乐山市| 新晃| 杭州市| 西平县| 包头市| 上犹县| 榆社县| 萍乡市| 永丰县| 凤阳县| 陆河县| 天等县| 栖霞市| 滨州市| 尚义县| 岗巴县| 白沙| 景泰县| 华阴市| 英德市| 宁波市| 静海县| 谢通门县| 集安市|