Android   发布时间:2022-04-28  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了Android自定义GestureDetector实现手势ImageView大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。

不说废话了,进入我们今天的主题吧。

先贴上前面内容的地址:

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;
 /**
 * 是否@L_616_14@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){ 


 }
}

那我们如果检测到了事件的话该怎么通知@L_616_14@者呢?是的,我们需要用到回调,我们看看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、创建@L_881_5@moveGestureDetector

 public MatrixImageView(Context context,AttributeSet attrs) {
 super(context,attrs);
 initView();
 //创建一个缩放手势监测器
 scaleDetector=new ScaleGestureDetector(context,onScaleGestureListener);
 //创建@L_881_5@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由@L_616_14@者决定
 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标记是由@L_616_14@者决定,
 * 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,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,attrs);
 initView();
 }

 private void initView() {
 //初始化模式为初始状态
 Displaymetrics dm = getresources().getDisplaymetrics();
 //给ImageView设置一张图片(此处为了测试直接在imageview里面设置了一张测试图片)
 Bitmap bitmap = BitmapFactory.decoderesource(getresources(),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,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 setOnPho@R_873_10586@pListener(OnPho@R_873_10586@pListener listener) {
 mAttacher.setOnPho@R_873_10586@pListener(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,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);
 }

可以看到,它也是创建了@L_881_5@mScaleDragDetector跟@L_881_5@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,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,focalY));
 } else {
 mSuppMatrix.setScale(scale,focalY);
 checkAndDisplaymatrix();
 }
 }
 }

其它的类似哈~~~ 代码还是挺多的(虑的情况比较多)可想而之,要写好一个自定义组件还不是那么简单的事哦,不过还是加油吧~!

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程小技巧。

大佬总结

以上是大佬教程为你收集整理的Android自定义GestureDetector实现手势ImageView全部内容,希望文章能够帮你解决Android自定义GestureDetector实现手势ImageView所遇到的程序开发问题。

如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。
标签:手势