安卓的手势识别框架

昨天介绍了一个手势处理的框架http://jcodecraeer.com/a/opensource/2015/0211/2465.html  ,这个框架其实是将手势识别封装成了一个个的类,更好复用而已,其实是很久以前的项目了,但是其面向对象的思维还是值得学习的。

下面是相关文章的翻译,翻译的不是很认真哈。

从api android2.2开始安卓中出现了ScaleGestureDetector 。从此第一台多点触控的安卓手机出现了,不管是手机还是ScaleGestureDetector都非常了不起。但我疑惑不解的是,为什么ScaleGestureDetector是唯一一个多点触控手势识别api,为什么没有类似于旋转手势识别的api,识别两个手指(或者多个手指)配合而造成的旋转。难道是因为安卓和google之间的专利战争导致的吗?我不管那么多,反正我就是要用,因此我写了这个小项目,用于扩展原有的api,这篇文章将会讲解如何在app中使用这个项目。

为什么我要写这个小框架

当我用google搜索实现两个手指旋转的坚决方案的时候,我找到了很多解决此问题的办法,这些办法来自于不同的开发者,但是它们都有一个缺点,就是少了点面向对象的味道,不好重用,非常难以扩展。

同时我幻想google有一天会将RotationGestureDetector(或者类似的名字)加入到api中(这一天终究没有到来),就像现在已经有了的ScaleGestureDetector 一样。到那时我只需将import 的声明改下,而不必去修改activity。这就是我为什么要写这个手势识别框架的原因。你可以在github上找到该项目。

如何使用安卓自带的ScaleGestureDetector

从如何在activity中使用ScaleGestureDetector开始讲吧

基本代码结构

在activity中你需要实现android.view.View.OnTouchListener以及其onTouch(...)方法,这样安卓系统就会在android.view.MotionEvent发生的时候通知它。

public class TouchActivity extends Activity implements OnTouchListener {
	public void onCreate(Bundle savedInstanceState) {
		// Init stuff here
		...
	}
	public boolean onTouch(View v, MotionEvent event) {
		// Handle touch events here
		...
	}
	...
}

现在你可以通过onTouch(...)来获得所有的多点触控事件了,但是相信我,你绝对会写出一堆杂乱无章的代码来,所以还是用android.view.ScaleGestureDetector来优雅的解决这个问题吧。

public class TouchActivity extends Activity implements OnTouchListener {
	private ScaleGestureDetector mScaleDetector;
	public void onCreate(Bundle savedInstanceState) {
		mScaleDetector = new ScaleGestureDetector(getApplicationContext(), new ScaleListener());
		...
	}
	public boolean onTouch(View v, MotionEvent event) {
		mScaleDetector.onTouchEvent(event);
	}
	...
}

首先当然是创建ScaleGestureDetecto 的对象,在这里这个对象是mScaleDetector ,我们将MotionEvent 转交给mScaleDetector.onTouchEvent(event),这样mScaleDetector 就能计算出是否发生了缩放事件。但是我们如何处理用户缩放操作的呢?

使用Listener来处理事件

你可能注意到了在初始化ScaleGestureDetector的时候也新建了一个ScaleListener。这个Listener将在scale事件发生的时候通知我们,并传递相关的数据。ScaleListener是我们定义的一个内部类:

public class TouchActivity extends Activity implements OnTouchListener
	...
	private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
		@Override
		public boolean onScale(ScaleGestureDetector detector) {
			mScaleSpan = detector.getCurrentSpan(); // average distance between fingers
			return true;
		}
	}
}

ScaleListener实现了ScaleGestureDetector的内部接口SimpleOnScaleGestureListener。这样ScaleGestureDetector就知道如何在scale事件发生的时候去调用我们的这个实现。在onScale(...)方法中,我们可以读取出需要的来自ScaleGestureDetector的数据。

使用scale的相关数据

既然ScaleListener是一个内部类,因此我们可以使用activity中的变量来保存数据,以便用于绘制(渲染等)缩放效果,这些操作可以在onTouch(...)完成。

public class TouchActivity extends Activity implements OnTouchListener
	private float mScaleSpan = 1.0f;
	private ScaleGestureDetector mScaleDetector;
	...
	public boolean onTouch(View v, MotionEvent event) {
                mScaleDetector.onTouchEvent(event);
		// 在这里通过mScaleSpan 的值来完成你想要的操作
		// Perform your magic with mScaleSpan now!
		...
		return true; // indicate event was handled
	}
	private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
		@Override
		public boolean onScale(ScaleGestureDetector detector) {
			mScaleSpan = detector.getCurrentSpan(); // average distance between fingers
			return true; // indicate event was handled
		}
	}
}

保存两个手势之间的状态

detector.getCurrentSpan() 返回的始终是两个手指之间的距离。因此我们使用ScaleGestureDetector.getScaleFactor()同时将=变成*=。

public class TouchActivity extends Activity implements OnTouchListener
	private float mScaleFactor = 1.0f;
	...
	private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
		@Override
		public boolean onScale(ScaleGestureDetector detector) {
			mScaleFactor *= detector.getScaleFactor(); // scale change since previous event
			return true; // indicate event was handled
		}
	}
}

Almeros(作者自己)的手势检测类

ok,现在你学会了以面向对象的方式来处理安卓中标准的多点触摸手势了。我所要介绍的手势处理类也是相同的使用方法。我将首先介绍两个我自定义的手势处理类。

RotateGestureDetector

只有两只以上的手指触摸屏幕才会产生旋转事件,下面是RotateGestureDetector的使用

public class TouchActivity extends Activity implements OnTouchListener {
    private float mRotationDegrees = 0.f;
	...
	private class RotateListener extends RotateGestureDetector.SimpleOnRotateGestureListener {
		@Override
		public boolean onRotate(RotateGestureDetector detector) {
			mRotationDegrees -= detector.getRotationDegreesDelta();
			return true;
		}
	}	
	...
}

MoveGestureDetector的用法

其实这是一个简化了操作流程的手势识别类,即便没有这个你也可以处理还,但是使用MoveGestureDetector可以让代码变得更简单。

public class TouchActivity extends Activity implements OnTouchListener {
    private float mFocusX = 0.f;
	private float mFocusY = 0.f;
	...
	private class MoveListener extends MoveGestureDetector.SimpleOnMoveGestureListener {
		@Override
		public boolean onMove(MoveGestureDetector detector) {
			PointF d = detector.getFocusDelta();
			mFocusX += d.x;
			mFocusY += d.y;		
			// mFocusX = detector.getFocusX();
			// mFocusY = detector.getFocusY();
			return true;
		}
	}	
	...
}

所有的手势识别类结合在一起

...
import com.almeros.android.multitouch.gesturedetectors.MoveGestureDetector;
import com.almeros.android.multitouch.gesturedetectors.RotateGestureDetector;
public class TouchActivity extends Activity implements OnTouchListener {
	...
    private float mScaleFactor = 1.0f;
    private float mRotationDegrees = 0.f;
    private float mFocusX = 0.f;
    private float mFocusY = 0.f;  
    private ScaleGestureDetector mScaleDetector;
    private RotateGestureDetector mRotateDetector;
    private MoveGestureDetector mMoveDetector;
	@Override
	public void onCreate(Bundle savedInstanceState) {
		...
		// Setup Gesture Detectors
		mScaleDetector = new ScaleGestureDetector(getApplicationContext(), new ScaleListener());
		mRotateDetector = new RotateGestureDetector(getApplicationContext(), new RotateListener());
		mMoveDetector = new MoveGestureDetector(getApplicationContext(), new MoveListener());
	}
	public boolean onTouch(View v, MotionEvent event) {
	        mScaleDetector.onTouchEvent(event);
        	mRotateDetector.onTouchEvent(event);
	        mMoveDetector.onTouchEvent(event);
        	// Mmmmmhhhagic!!!
        	//  with: mScaleFactor, mRotationDegrees, mFocusX and mFocusY 
		...
		return true; // indicate event was handled
	}
	private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
		@Override
		public boolean onScale(ScaleGestureDetector detector) {
			mScaleFactor *= detector.getScaleFactor(); // scale change since previous event
			return true;
		}
	}
	private class RotateListener extends RotateGestureDetector.SimpleOnRotateGestureListener {
		@Override
		public boolean onRotate(RotateGestureDetector detector) {
			mRotationDegrees -= detector.getRotationDegreesDelta();
			return true;
		}
	}	
	private class MoveListener extends MoveGestureDetector.SimpleOnMoveGestureListener {
		@Override
		public boolean onMove(MoveGestureDetector detector) {
			PointF d = detector.getFocusDelta();
			mFocusX += d.x;
			mFocusY += d.y;		
			return true;
		}
	}		
}

翻译自 http://code.almeros.com/android-multitouch-gesture-detectors#.VNrg7_mSwTx