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