Android ScaleGestureDetector实现可缩

2019-04-23  本文已影响0人  Mundy

今天我们来做一个可以根据手势进行缩放的图片查看器。
首先是需要用到的几个东西:
1.Matrix (用来实现图片的缩放)
Matrix可以实现对图形进行平移,缩放,旋转等操作。在这里,我们主要用到的是平移和缩放。
postTranslate(dx,dy) 对图形进行平移
postScale(sx,sy,px,py)对图形进行缩放
2.ScaleGestureDetector (对缩放手势的监听)

ScaleGestureDetector gestureDetector=new ScaleGestureDetector(mContext, new ScaleGestureDetector.OnScaleGestureListener() {
            @Override
            public boolean onScale(ScaleGestureDetector scaleGestureDetector) {

                float factor=scaleGestureDetector.getScaleFactor();//缩放因子,这个是根据两个手指来计算缩放的倍数


                return true;
            }

            @Override
            public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
                //缩放开始,这里返回true表示要接收这个事件,必须为true,onScale才能执行
                return true;
            }

            @Override
            public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {

                //缩放结束
             
            }
        });

以上就是实现本文功能的两个重点了。下面我们就开始写代码了
首先要先写一个view继承Imageview

public class PhotoView extends AppCompatImageView {

    private Context mContext;

    public PhotoView(Context context) {
        super(context);
        //在代码中new PhotoView()的时候会调用这个初始化

        mContext=context;
        init();
    }

    public PhotoView(Context context, AttributeSet attrs) {
        super(context, attrs);
        //在布局中使用会调用这个初始化
        mContext=context;
        init();
    }

    void init(){
        //初始化

        
    }
}

这样,自定义photoview的主结构就出来了。接下来就是对图形的处理了。
我们无法保证所加载的图片的宽高,所以我们第一步需要对图片进行放大或缩小。使图片适应view的大小,并将图片移动到屏幕中间。下面是代码实现

public class PhotoView extends AppCompatImageView implements ViewTreeObserver.OnGlobalLayoutListener {
//这里我们需要实现 ViewTreeObserver.OnGlobalLayoutListener,这个接口用于测量大小

...省略构造函数

void init(){
      mMatrix=new Matrix();//初始化
      setScaleType(ScaleType.MATRIX);//设置类型,使图片能支持Matrix

}
@Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
      //需要在这里添加监听。
        getViewTreeObserver().addOnGlobalLayoutListener(this);
    }
}

下面是实现

@Override
    public void onGlobalLayout() {
        if(isFristLoad){
            //因为onGlobalLayout可能会回调多次,所以这里要加个布尔值,控制下面的代码只执行一次,这里也可以改成把监听移除。
            isFristLoad=false;

            int width=getWidth();//获取view的宽度
            int height=getHeight();//获取view的高度
            Drawable drawable=getDrawable();//获取加载的图片
            if(drawable==null){
                return;
            }
            int imgWidth=drawable.getIntrinsicWidth();//获取图片的宽度
            int imgHeight=drawable.getIntrinsicHeight();//获取图片的宽度

      
            //下面是计算出图片的大小和view大小相差的倍数,然后对图片进行缩放,
            if(imgWidth>width||imgHeight>height){
                //当图片比view大的,需要缩小
                //图片宽或高超过view
                if(imgWidth>imgHeight){
                    //乘1f是为了不让scale变成int值
                    //mScale是一个全局的变量,存放初始的缩放倍数
                    mScale=width*1f/imgWidth*1f;
                }else {
                    mScale=height*1f/imgHeight*1f;
                }

            }else if(imgWidth<width&&imgHeight<height) {
                //当图片比view小,需要放大
                //图片宽高都小于view
                if(imgWidth>imgHeight){
                    mScale=width*1f/imgWidth*1f;
                }else {
                    mScale=height*1f/imgHeight*1f;
                }
            }

          
            //这个方法文章开头有介绍过,设置移动到view中间
            mMatrix.postTranslate(width/2-imgWidth/2,height/2-imgHeight/2);
            //根据前面计算出的倍数,对图片进行缩放,使图片适应view大小
            mMatrix.postScale(mScale,mScale,width/2,height/2);

            //将修改应用到图片上。
            setImageMatrix(mMatrix);


        }
    }

接下来就是对手势的监听了,上面我们已经说过ScaleGestureDetector得基础实现,下面我们就用它来实现手势缩放.

void init(){

        mMatrix=new Matrix();
        setScaleType(ScaleType.MATRIX);


        gestureDetector=new ScaleGestureDetector(mContext, new ScaleGestureDetector.OnScaleGestureListener() {
            @Override
            public boolean onScale(ScaleGestureDetector scaleGestureDetector) {

           
                float factor=scaleGestureDetector.getScaleFactor();//缩放因子
                float scale=getScale();//获取Matrix当前的缩放数值
                
                if(scale*factor>MAX_SCALE||scale*factor<=mScale*0.8){
                    //MAX_SCALE是一个全局变量,为了防止图片放大太大,我们设置一个值,图片放大到这个值就不能再放大。
                    //后面那个0.8的作用我们在下面的onScaleBegin里面做说明
                  
                }else {
                  //根据手指获取缩放中心,然后根据缩放因子进行缩放
                    //原理和我们上面的一样
                  mMatrix.postScale(factor,factor,scaleGestureDetector.getFocusX(),scaleGestureDetector.getFocusY());

                    setImageMatrix(mMatrix);
                }
                return true;
            }

            @Override
            public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
                return true;
            }

            @Override
            public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {
                //这里是缩放结束后做的操作,
              //上面onScale那个0.8就是配合下面这段代码的
              //下面是获取当前缩放值,mScale就是我们第一步里面图片适应了view时候的倍数
              //上面的0.8是我们允许在缩放过程中,图片缩小到原来80%的大小
              //然后再缩放结束后,回复到100%的大小。这样一来,图片就会又一个回弹的效果(参考微信)
                float scale=getScale();//获取当前缩放值
                if(scale<mScale){
                    //恢复到100%大小
                    float s=mScale/scale;
                    mMatrix.postScale(s,s,getWidth()/2,getHeight()/2);
                    setImageMatrix(mMatrix);
                }


            }
        });




    }

//需要在onTouchEvent里面改成调用gestureDetector.onTouchEvent(event);
@Override
    public boolean onTouchEvent(MotionEvent event) {
        return gestureDetector.onTouchEvent(event);
    }

这样,我们就实现了一个手势缩放的图片查看器。下面是完整代码

public class PhotoView extends AppCompatImageView implements ViewTreeObserver.OnGlobalLayoutListener {


    //最大缩放倍数
    private static int MAX_SCALE=5;

    //默认缩放倍数,初始化后会根据图片大小改变这个值
    private static float mScale=1f;

    private Context mContext;

    private ScaleGestureDetector gestureDetector;

    private Matrix mMatrix;

    //首次加载,避免onGlobalLayout多次执行
    private boolean isFristLoad=true;

    public PhotoView(Context context) {
        super(context);
        mContext=context;
        init();
    }

    public PhotoView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext=context;
        init();
    }


    void init(){

        mMatrix=new Matrix();
        setScaleType(ScaleType.MATRIX);
        gestureDetector=new ScaleGestureDetector(mContext, new ScaleGestureDetector.OnScaleGestureListener() {
            @Override
            public boolean onScale(ScaleGestureDetector scaleGestureDetector) {

              
                float factor=scaleGestureDetector.getScaleFactor();
                float scale=getScale();

                if(scale*factor>MAX_SCALE||scale*factor<=mScale*0.8){

                }else {

                    mMatrix.postScale(factor,factor,getWidth()/2,getHeight()/2);

                    setImageMatrix(mMatrix);
                }
                return true;
            }

            @Override
            public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
                return true;
            }

            @Override
            public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {

                float scale=getScale();
                if(scale<mScale){
                    float s=mScale/scale;
                    mMatrix.postScale(s,s,getWidth()/2,getHeight()/2);
                    setImageMatrix(mMatrix);
                }


            }
        });




    }


    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        getViewTreeObserver().addOnGlobalLayoutListener(this);
    }


    //获取当前缩放值
    private float getScale() {
        float[] values = new float[9];
        mMatrix.getValues(values);
        return values[Matrix.MSCALE_X];
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return gestureDetector.onTouchEvent(event);
    }

    @Override
    public void onGlobalLayout() {
        if(isFristLoad){
            isFristLoad=false;

            int width=getWidth();
            int height=getHeight();
            Drawable drawable=getDrawable();
            if(drawable==null){
                return;
            }
            int imgWidth=drawable.getIntrinsicWidth();
            int imgHeight=drawable.getIntrinsicHeight();



            if(imgWidth>width||imgHeight>height){
                //图片宽或高超过view
                if(imgWidth>imgHeight){
                    //乘1f是为了不让scale变成int值
                    mScale=width*1f/imgWidth*1f;
                }else {
                    mScale=height*1f/imgHeight*1f;
                }

            }else if(imgWidth<width&&imgHeight<height) {
                //图片宽高都小于view
                if(imgWidth>imgHeight){
                    mScale=width*1f/imgWidth*1f;
                }else {
                    mScale=height*1f/imgHeight*1f;
                }
            }

    
            mMatrix.postTranslate(width/2-imgWidth/2,height/2-imgHeight/2);
            mMatrix.postScale(mScale,mScale,width/2,height/2);


            setImageMatrix(mMatrix);


        }
    }
}
上一篇下一篇

猜你喜欢

热点阅读