前言:本来是打算周末把手势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;        /**         * 是否调用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一样用它就可以了:

.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();            }        }    }

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

end~

更多相关文章

  1. Android TextView设置自动识别的超链接字体颜色,及自身点击事件无
  2. Android事件分发机制完全解析,带你从源码的角度彻底理解
  3. 巧用布局文件实现Android中实现事件监听机制
  4. 一步步探索学习Android Touch事件分发传递机制(一)
  5. 【Android】ViewPager实现图片左右滑动播放及添加点击事件
  6. Android Touch事件分发响应机制
  7. Android的按钮监听事件&自定义回调函数
  8. Android : GestureDetector手势检测

随机推荐

  1. android-屏幕分辨率那点事儿
  2. android 数据存储初探
  3. android CTS SELinuxDomainTest# testIni
  4. Android(安卓)getResources的作用和须要
  5. androidのview游戏框架
  6. Android学习之ListView使用基础
  7. 【CMake】CMake 引入 ( Android(安卓)NDK
  8. Android如何使用注解进行代码检查
  9. Android下使用activation发送邮件
  10. DSL element ‘android.dataBinding.enab