1. 程式人生 > >Android ScaleGestureDetector縮放手勢識別

Android ScaleGestureDetector縮放手勢識別

ScaleGestureDetector

在Android中對於手勢識別操作可以使用GestureDetector類來進行處理,對於縮放手勢,則提供了對應的ScaleGestureDetector類。

public class ScaleGestureDetector
extends Object

ScaleGestureDetector 使用類內建的MotionEvents來識別探測縮放轉換手勢。當一個特定的縮放手勢事件發生時,使用者可以設定ScaleGestureDetector.OnScaleGestureListener 回撥來得到通知。這個類只能使用於通過觸控產生的MotionEvents事件。

ScaleGestureDetector.OnScaleGestureListener

ScaleGestureDetector.OnScaleGestureListener監聽器介面是用來接收處理縮放手勢事件的。

public static interface ScaleGestureDetector.OnScaleGestureListener

public interface OnScaleGestureListener {
        //縮放操作進行中
        public boolean onScale(ScaleGestureDetector detector);
        //縮放操作開始前
public boolean onScaleBegin(ScaleGestureDetector detector); //縮放操作結束後 public void onScaleEnd(ScaleGestureDetector detector); }

OnScaleGestureListener介面中包含了3個方法,分別對應縮放操作開始前,進行中和結束後對應時刻的事件處理方法。

  • onScaleBegin
    當一個縮放手勢開始時觸發該事件,即雙指按下螢幕時。該方法的返回值為boolean,主要用來判斷探測器是否應該繼續識別處理這個手勢。例如,如果在螢幕上的某個區域內不需要識別縮放手勢,當手勢的焦點在該區域內時,onScaleBegin()可以返回false忽略不處理該手勢的接下來的一系列事件。
  • onScale
    當一個縮放手勢正在進行中時持續觸發該事件,即雙指正在螢幕上滑動。該方法的返回值為boolean,主要用來判斷是否結束當前縮放手勢事件,即本次縮放事件是否已被處理。如果已被處理,那麼detector就會重置縮放事件;如果未被處理,detector會繼續進行計算,修改getScaleFactor()的返回值,直到被處理為止。當返回true時,則結束當前縮放手勢的一系列事件,縮放因子將會初始化為1.0,當返回false時,當前縮放手勢的一系列事件繼續進行,縮放因子不會被初始化,而是根據手勢不斷的持續變化。返回值可以用來限制縮放值的最大比例上限和最小比例下限。
  • onScaleEnd
    當一個縮放手勢結束後觸發該事件,即雙指擡起離開螢幕時。
  • 上述3個監聽方法中都帶有ScaleGestureDetector 引數,裡面主要包含了縮放手勢事件的一些相關資訊。簡單介紹幾個核心方法:

  • float getScaleFactor()
    返回從之前的縮放手勢和當前的縮放手勢兩者的比例因子,即縮放值,預設1.0。當手指做縮小操作時,該值小於1.0,當手指做放大操作時,該值大於1.0。在每一個縮放事件中都會從1.0開始變化,如果需要做持續性操作,則需要儲存上一次的伸縮值,然後當前的伸縮值*上一次的伸縮值,得到連續變化的伸縮值,例如有3次事件發生,沒連續性則是縮小0.9->縮小0.9->縮小0.9,如果做了連續性處理即儲存上一次的伸縮值,則是縮小0.9->縮小0.9*0.9=0.81->縮小0.81*0.9=0.72,有了逐漸變小的連續性。
  • float getFocusX()
    返回當前縮放手勢的焦點X座標,焦點即兩手指的中心點。
  • float getFocusY()
    返回當前縮放手勢的焦點Y座標,即兩手指的中心點。
  • float getCurrentSpan()
    返回每個縮放手勢的兩個手指之間的距離值。
  • 使用很簡單,只需要在Activity,Fragment或者自定義View中實現OnScaleGestureListener 介面,然後建立一個ScaleGestureDetector 例項,重寫onTouchEvent方法, 返回scaleGestureDetector.onTouchEvent(event)。

    public class MainActivity extends BaseActivity implements ScaleGestureDetector.OnScaleGestureListener{
        private static final String DEBUG_TAG="Gestures";
        private ScaleGestureDetector scaleGestureDetector;
        private ImageView img;
        private RelativeLayout container;
    
        @Override
        protected void initView() {
            setContentView(R.layout.activity_main);
            img= (ImageView) findViewById(R.id.Img);
            container= (RelativeLayout) findViewById(R.id.Container);
        }
    
        @Override
        protected void initData() {
            scaleGestureDetector=new ScaleGestureDetector(this,this);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event){
            return scaleGestureDetector.onTouchEvent(event);
        }
    
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            return false;
        }
    
        @Override
        public boolean onScaleBegin(ScaleGestureDetector detector) {
            return false;
        }
    
        @Override
        public void onScaleEnd(ScaleGestureDetector detector) {
    
        }
    }

    SimpleOnScaleGestureListener

    當使用OnScaleGestureListener 介面時,需要實現裡面所有的介面方法,如果只需要處理某幾個步驟時,該方式就不太方便了。Android還內建SimpleOnScaleGestureListener 來簡化處理縮放手勢事件,可以重寫裡面的某幾個監聽方法來實現對特定手勢事件的處理。

    public static class ScaleGestureDetector.SimpleOnScaleGestureListener
    extends Object implements ScaleGestureDetector.OnScaleGestureListener

    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
            }
        }

    SimpleOnScaleGestureListener 是ScaleGestureDetector裡面的一個靜態內部類,實現了OnScaleGestureListener 介面,當只需要使用某幾個介面方法時,可以繼承SimpleOnScaleGestureListener 新建一個子類,重寫裡面需要處理的監聽方法,然後建立該子類的例項來使用。

    下面使用SimpleOnScaleGestureListener 和Matrix矩陣實現一個圖片縮放操作的例子:
    這裡寫圖片描述

    public class MainActivity extends BaseActivity {
        private static final String DEBUG_TAG="Gestures";
        private ScaleGestureDetector scaleGestureDetector;
        private ImageView img;
        private Matrix matrix;
        private float preScale;//之前的伸縮值
        private float curScale;//當前的伸縮值
    
        private Bitmap sourceBitmap;
        private int[] screenSize;//螢幕尺寸資訊
        private float translateX;//平移到螢幕中心的X軸距離
        private float translateY;//平移到螢幕中心的Y軸距離
        private boolean isChangeScaleType;//是否轉換為Matrix模式
    
        @Override
        protected void initView() {
            setContentView(R.layout.activity_main);
            img= (ImageView) findViewById(R.id.Img);
        }
    
        @Override
        protected void initData() {
            curScale=1.0f;
            scaleGestureDetector=new ScaleGestureDetector(this,new MyScaleGestureDetector());
    
            sourceBitmap=BitmapFactory.decodeResource(getResources(),R.drawable.headimg);
            screenSize=getScreenSize();
    
            translateX=screenSize[0]/2-sourceBitmap.getWidth()/2;//使圖片顯示在中心
            translateY=sourceBitmap.getHeight()/2-screenSize[1]/2;
    
            matrix=new Matrix();
            preScale=screenSize[0]*1.0f/sourceBitmap.getWidth();//圖片完全顯示的伸縮值
            img.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
            img.setImageBitmap(sourceBitmap);
            isChangeScaleType=true;
        }
    
        @Override
        protected void initListener() {
            img.setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    return scaleGestureDetector.onTouchEvent(event);
                }
            });
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event){
            return super.onTouchEvent(event);
        }
    
    
        private class MyScaleGestureDetector extends ScaleGestureDetector.SimpleOnScaleGestureListener{
    
            @Override
            public boolean onScale(ScaleGestureDetector detector){
                if(isChangeScaleType){
                    img.setScaleType(ImageView.ScaleType.MATRIX);
                    isChangeScaleType=false;
                }
                curScale=detector.getScaleFactor()*preScale;//當前的伸縮值*之前的伸縮值 保持連續性
    
                if(curScale>5||curScale<0.1){//當放大倍數大於5或者縮小倍數小於0.1倍 就不伸縮圖片 返回true取消處理伸縮手勢事件
                    preScale=curScale;
                    return true;
                }
    
                matrix.setScale(curScale,curScale,img.getWidth()/2,img.getHeight()/2);//在螢幕中心伸縮
                matrix.preTranslate(translateX,translateY);//使圖片平移到螢幕中心顯示
    
                img.setImageMatrix(matrix);//改變矩陣值顯示圖片
                preScale=curScale;//儲存上一次的伸縮值
                return false;
            }
        }
    
        //獲取螢幕尺寸
        private int[] getScreenSize(){
            DisplayMetrics displayMetrics=new DisplayMetrics();
            WindowManager windowManager= (WindowManager) getSystemService(Context.WINDOW_SERVICE);
            windowManager.getDefaultDisplay().getMetrics(displayMetrics);
            int[] screenSize=new int[2];
            screenSize[0]=displayMetrics.widthPixels;
            screenSize[1]=displayMetrics.heightPixels;
            return screenSize;
        }
    
    
    }