[安卓自定义控件]可双指缩放和双击切换大小的ImageScaleView

来源:互联网 发布:淘宝宝贝描述模板psd 编辑:程序博客网 时间:2024/05/21 10:04

要求说明

仍然是最近做的项目中的一个需求,整个的需求是要实现一个可以滑动切换来浏览不同图片的图片展示页。具体的要求如下:

  • 默认进入全屏显示状态,图片宽度适应屏幕
  • 双指放大缩小图片,双击在100%显示与适应宽度显示之间切换
  • 浏览前一张,第一张往前翻时显示“已经是第一张图片”
  • 浏览后一张,最后一张往后翻时显示“已经是最后一张图片”
    实现思路上,滑动切换可以通过结合ViewPager来做,而双指缩放以及双击切换显示大小则需要定制化一个ImageScaleView来实现。因此本篇文章先针对后者的实现进行叙述。

实现要点

  • 为了利用ImageView本身已经在图片显示上提供的支持,ImageScaleView将继承自ImageView。
  • 缩放效果本身可以通过矩阵(Matrix)来实现,这方面的知识可以参考这里。
  • 对于双指缩放操作,可以覆盖父类的onTouchEvent(MotionEvent event)并实现相应的处理。
  • 对于双击操作的监听,则可以利用系统提供的GestureDetector类对onTouchEvent传入的event进行处理,减少了自行通过计算两次点击之间的时间间隔来判断是否为一次有效双击的麻烦。
  • 为了支持在首次进入时默认进入全屏显示状态,图片宽度适应屏幕,还需要实现ViewTreeObserver.OnGlobalLayoutListener接口并在onAttachedToWindow和onDetachedFromWindow中对自身对该事件的监听进行添加和移除。
  • 最后,为了支持可能的在修改图片内容(比如图片内容是通过网络异步加载的)后也自动进入全屏显示状态,图片宽度适应屏幕,再覆盖可能用于修改图片内容的setImageResource和setImageDrawable方法,在其中调用相应的用于进入全屏显示状态并使图片宽度适应屏幕的函数。

源码与相关注释说明

import android.content.Context;import android.graphics.Matrix;import android.graphics.PointF;import android.graphics.drawable.Drawable;import android.support.annotation.DrawableRes;import android.support.annotation.Nullable;import android.util.AttributeSet;import android.view.GestureDetector;import android.view.MotionEvent;import android.view.ViewTreeObserver;import android.widget.ImageView;/** * An ImageView which supports scaling the picture. */public class ImageScaleView extends ImageView implements ViewTreeObserver.OnGlobalLayoutListener{    //是否第一次查看该图片,用于实现默认自动将图片平移到控件中间并宽度适应屏幕    private boolean once = true;    //是否处于100%显示状态,用于在宽度适应屏幕与100%显示之间切换    private boolean inFull=false;    //图片的矩阵    private Matrix mMatrx;    //图片临时状态的矩阵    private Matrix mTempMatrix = new Matrix();    //两指按下的点    private PointF mStartPoint = new PointF();    //两点的中心点    private PointF mMidPoint = new PointF();    //两指按下的距离    private float oldDis = 1f;    //状态    private int state = NONE;    //默认状态    private static final int NONE = 0;    //拖拽状态    private static final int DRAG = 1;    //缩放状态    private static final int ZOOM = 2;    //缩放到宽度适应屏幕    private static final int SCALE_FIT_WIDTH=0;    //缩放到100%    private static final int SCALE_FULL=1;    private GestureDetector mGestureDetector;    public ImageScaleView(Context context) {        this(context, null);    }    public ImageScaleView(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public ImageScaleView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);//注意需要将ScaleType设置为ScaleType.MATRIX以支持通过变换使用的矩阵动态修改图片显示大小super.setScaleType(ScaleType.MATRIX);        init();    }    private void init() {        mMatrx = new Matrix();        mGestureDetector=new GestureDetector(getContext(),new GestureDetector.SimpleOnGestureListener(){            @Override            public boolean onDoubleTap(MotionEvent e){//进行双击时图片缩放状态切换                if(inFull){                    scalePicture(SCALE_FIT_WIDTH);                }                else{                    scalePicture(SCALE_FULL);                }                inFull=!inFull;                return true;            }        });    }//当view 被添加到window中,被绘制之前的回调。    @Override    protected void onAttachedToWindow() {        super.onAttachedToWindow();        getViewTreeObserver().addOnGlobalLayoutListener(this);    }//当view被从窗体中移除时调用。    @Override    protected void onDetachedFromWindow() {        super.onDetachedFromWindow();        getViewTreeObserver().removeGlobalOnLayoutListener(this);    }    @Override    public void onGlobalLayout() {        //第一次layout,使图片默认缩放到适应屏幕宽度        if (once) {            scalePicture(SCALE_FIT_WIDTH);            once = false;        }    }    private void scalePicture(int scaleType){    //在对矩阵进行操作前,需要先将矩阵归0,否则会受之前在矩阵中设置的值的影响        mMatrx.reset();        Drawable drawable = getDrawable();        if (drawable == null) {            return;        }        //图片本身的宽高        int dw = drawable.getIntrinsicWidth();        int dh = drawable.getIntrinsicHeight();        //控件的宽高,也即控件所占据的屏幕大小        int w = getWidth();        int h = getHeight();        float scale = 1.0f;        //显示宽度适应屏幕        if(scaleType==SCALE_FIT_WIDTH)        scale = (w * 1.0f) / dw;        //显示到100%        else if(scaleType==SCALE_FULL)            scale = 1.0f;        //平移到控件(也即屏幕)中间        mMatrx.postTranslate((w - dw) / 2, (h - dh) / 2);        //缩放        mMatrx.postScale(scale, scale, w / 2, h / 2);        //注意每次setImageMatrix都是将之前设置的矩阵效应清除后再使用当前设置的矩阵        setImageMatrix(mMatrx);    }    @Override    public void setImageResource(@DrawableRes int resId){        super.setImageResource(resId);        scalePicture(SCALE_FIT_WIDTH);    }    @Override    public void setImageDrawable(@Nullable Drawable drawable) {        super.setImageDrawable(drawable);        scalePicture(SCALE_FIT_WIDTH);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        switch (event.getAction() & MotionEvent.ACTION_MASK) {            case MotionEvent.ACTION_DOWN:                mTempMatrix.set(mMatrx);                mStartPoint.set(event.getX(), event.getY());                state = DRAG;                break;            case MotionEvent.ACTION_POINTER_DOWN://双指                oldDis = getDistance(event);                if (oldDis > 10f) {//缓存双指按压开始时的矩阵                    mTempMatrix.set(mMatrx);                    getMidPoint(mMidPoint, event);                    //只有双指才被认为是缩放状态                    state = ZOOM;                }                break;            case MotionEvent.ACTION_MOVE:               if (state == ZOOM) {                    float newDis = getDistance(event);                    if (newDis > 10f) {//恢复双指按压时缓存的矩阵mMatrx.set(mTempMatrix);                        float scale = newDis / oldDis;//缩放尺度计算为与缓存距离的比例//以控件中心为缩放中心                        mMatrx.postScale(scale, scale, getWidth()/2,                                getHeight()/2);                    }                }                break;            case MotionEvent.ACTION_UP:                state = NONE;                break;            case MotionEvent.ACTION_POINTER_UP:                state = NONE;                break;        }        setImageMatrix(mMatrx);        mGestureDetector.onTouchEvent(event);        //需要return true以使得控件能在收到down事件后继续监听move和up事件        return true;    }    /**     * 计算两点距离     *     * @param event     * @return     */    private float getDistance(MotionEvent event) {        float x = event.getX(0) - event.getX(1);        float y = event.getY(0) - event.getY(1);        return (float) Math.sqrt(x * x + y * y);    }    /**     * 计算中心点     *     * @param point     * @param event     */    private void getMidPoint(PointF point, MotionEvent event) {        float x = event.getX(0) + event.getX(1);        float y = event.getY(0) + event.getY(1);        point.set(x / 2, y / 2);    }}
0 0
原创粉丝点击