PhotoView 源码解读

本文深入解读Android开源库PhotoView,用于实现ImageView的缩放、旋转等操作。文章详细解析了PhotoViewAttacher的构造、Touch事件监听、双击缩放和双指缩放的实现原理,以及如何处理图片显示边界。通过源码分析,阐述了PhotoView如何通过Matrix实现图片变换,并提供了使用建议。
摘要由CSDN通过智能技术生成

转载请注明本文出自maplejaw的博客(http://blog.csdn.net/maplejaw_

开源库地址:https://github.com/chrisbanes/PhotoView
PhotoView是一个用来帮助开发者轻松实现ImageView缩放的库。开发者可以轻易控制对图片的缩放旋等等操作。
image_1akkgdkg01saq1l01jdvaaqeeh9.png-11.6kB
PhotoView的使用极其简单,而且提供了两种方案。可以使用普通的ImageView,也可以使用该库中提供的ImageView(PhotoView)。

  • 使用PhotoView
    只需如下引用该库中的ImageView,无需关心其它实现细节,你的ImageView便可拥有缩放效果。
<uk.co.senab.photoview.PhotoView
            android:id="@+id/iv_photo"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent" />
  • 针对普通ImageView
    有的时候,可能因为一些历史原因,使得你不得不用原来的ImageView。幸运的是该库也提供了一种解决方案。只需用PhotoViewAttacher包装即可。
PhotoViewAttacher mAttacher=new PhotoViewAttacher(mImageView);//用PhotoViewAttacher包装

mAttacher.update();//当图片改变时需调用update();

mAttacher.cleanup();//当ImageView不再使用时回收资源(可在onDestory中 调用)。PhotoView已经实现了这个功能不需要自己管理。

PhotoView真的很神奇,接下来我们去源码里一探究竟吧。顺便多说一句,图片的缩放大量运用到了Matrix相关知识,不了解的务必要先查阅相关资料哦。强烈推荐Android Matrix 这篇文章,当然也可以看我的这篇Android Matrix矩阵详解

源码解读

这次源码解读我们从使用普通ImageView入手,普通的ImageView如果想缩放,必须依赖于PhotoViewAttacher,而PhotoViewAttacher又实现了IPhotoView接口。IPhotoView主要定义了一些常用的操作和默认值,由于方法实在太多了,就不一一列举了,直接上图。
IPhotoView定义的所有抽象方法如下。
image_1am7q83srm9n1nb5odsarv17sm9.png-98.5kB
IPhotoView的部分源码如下。

public interface IPhotoView {
   
    float DEFAULT_MAX_SCALE = 3.0f;//默认最大缩放倍数为3倍
    float DEFAULT_MID_SCALE = 1.75f;//默认中间缩放倍数为1.75倍
    float DEFAULT_MIN_SCALE = 1.0f;//默认最小缩放倍数为1倍
    int DEFAULT_ZOOM_DURATION = 200;//默认的缩放间隔为200ms
    boolean canZoom();//可以缩放
    RectF getDisplayRect();//获取显示矩形
    boolean setDisplayMatrix(Matrix finalMatrix);//设置显示矩阵
    Matrix getDisplayMatrix();//获取显示矩阵
    //..
    //省略了部分源码

介绍完IPhotoView接口后,现在改来看看PhotoViewAttacher了,PhotoViewAttacher的属性也比较多,如下:

    private Interpolator mInterpolator = new AccelerateDecelerateInterpolator();//插值器,用于缩放动画
    int ZOOM_DURATION = DEFAULT_ZOOM_DURATION;//默认的缩放间隔

    static final int EDGE_NONE = -1;//图片两边都不在边缘内
    static final int EDGE_LEFT = 0;//图片左边显示在View的左边缘内
    static final int EDGE_RIGHT = 1;//图片右边显示在View的右边缘内
    static final int EDGE_BOTH = 2;//图片两边都在边缘内

    static int SINGLE_TOUCH = 1;//单指

    private float mMinScale = DEFAULT_MIN_SCALE;//最小缩放倍数
    private float mMidScale = DEFAULT_MID_SCALE;//中间缩放倍数
    private float mMaxScale = DEFAULT_MAX_SCALE;//最大缩放倍数

    private boolean mAllowParentInterceptOnEdge = true;//当在边缘操作时,允许父布局拦截事件。
    private boolean mBlockParentIntercept = false;//阻止父布局拦截事件


    private WeakReference<ImageView> mImageView;//弱引用

    //手势探测器
    private GestureDetector mGestureDetector;//单击,长按,Fling
    private uk.co.senab.photoview.gestures.GestureDetector mScaleDragDetector;//缩放和拖拽

    private final Matrix mBaseMatrix = new Matrix();//基础矩阵,用来保存初始的显示矩阵
    private final Matrix mDrawMatrix = new Matrix();//绘画矩阵,用来计算最后显示区域的矩阵,是在mBaseMatrix和mSuppMatrix的基础上计算出来的。
    private final Matrix mSuppMatrix = new Matrix();//这个矩阵我也不知道怎么称呼,也不知道是不是Supply的意思,暂且叫作供应矩阵吧,用来保存旋转平移和缩放的矩阵。
    private final RectF mDisplayRect = new RectF();//显示矩形
    private final float[] mMatrixValues = new float[9];//用来保存矩阵的值。3*3

    // 各类监听
    private OnMatrixChangedListener mMatrixChangeListener;
    private OnPhotoTapListener mPhotoTapListener;
    private OnViewTapListener mViewTapListener;
    private OnLongClickListener mLongClickListener;
    private OnScaleChangeListener mScaleChangeListener;
    private OnSingleFlingListener mSingleFlingListener;

    //保存ImageView的top,right,bottom,left
    private int mIvTop, mIvRight, mIvBottom, mIvLeft;
    //Fling时的Runable
    private FlingRunnable mCurrentFlingRunnable;
    private int mScrollEdge = EDGE_BOTH;//两边边缘
    private float mBaseRotation;//基础旋转角度
    private boolean mZoomEnabled;//是否可以缩放
    private ScaleType mScaleType = ScaleType.FIT_CENTER;//默认缩放类型

此外PhotoViewAttacher中还定义了以下几个接口。

    public interface OnMatrixChangedListener {
   
        /**
         * 当用来显示Drawable的Matrix改变时回调
         * @param rect - 显示Drawable的新边界
         */
        void onMatrixChanged(RectF rect);
    }


    public interface OnScaleChangeListener {
   
        /**
         * 当ImageView改变缩放时回调
         *
         * @param scaleFactor 小于1表示缩小,大于1表示放大
         * @param focusX     缩放焦点X
         * @param focusY     缩放焦点Y
         */
        void onScaleChange(float scaleFactor, float focusX, float focusY);
    }

    public interface OnPhotoTapListener {
   

        /**
         * 
         *当用户敲击在照片上时回调,如果在空白区域不会回调
         * @param view - ImageView
         * @param x    -用户敲击的位置(在图片中从左往右的位置)占图片宽度的百分比
         * @param y    -用户敲击的位置(在图片中从上往下的位置)占图片高度的百分比
         */
        void onPhotoTap(View view, float x, float y);

        /**
         * 在图片外部的空白区域敲击回调
         * */
        void onOutsidePhotoTap();
    }

    public interface OnViewTapListener {
   

        /**
         * 只要用户敲击ImageView就会回调,不管是不是在图片上。
         * @param view - View the user tapped.
         * @param x    -敲击View的x坐标
         * @param y    -敲击View的y坐标
         */
        void onViewTap(View view, float x, float y);
    }

    public interface OnSingleFlingListener {
   

        /**
         * 用户使用单指在ImageView上快速滑动时回调,不管是不是在图片上。
         * @param e1        - 第一次触摸事件
         * @param e2        - 第二次触摸事件
         * @param velocityX - 水平滑过的速度.
         * @param velocityY - 竖直滑过的素组.
         */
        boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);
    }

在看完PhotoViewAttacher的一些属性和接口外,现在就来看PhotoViewAttacher的构造方法。即new PhotoViewAttacher(mImageView)这一句。

  public PhotoViewAttacher(ImageView imageView) {
        this(imageView, true);
    }

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

        imageView.setDrawingCacheEnabled(true);//开启绘制缓存区,用于获取可见区的bitmap
        imageView.setOnTouchListener(this);//设置Touch监听,用于添加手势监听

        ViewTreeObserver observer = imageView.getViewTreeObserver();
        if (null != observer)
            observer.addOnGlobalLayoutListener(this);//用于监听ImageView的大小

        // 确保ImageView的ScaleType为Matrix
        setImageViewScaleTypeMatrix(imageView);

        if (imageView.isInEditMode()) {
            return;
        }

       //初始化多指缩放/拖拽手势探测器
        mScaleDragDetector = VersionedGestureDetector.newInstance(
                imageView.getContext(), this);

        //初始化其它手势监听(长按,Fling)
        mGestureDetector = new GestureDetector(imageView.getContext(),
                new GestureDetector.SimpleOnGestureListener() {

                    //长按
                    @Override
                    public void onLongPress(MotionEvent e) {
                        if (null != mLongClickListener) {
                            mLongClickListener.onLongClick(getImageView());
                        }
                    }

                    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值