Best Practices for User Input(第九章) 对用户输入的最佳实践
参考文献:http://developer.android.com/training/best-user-input.html
章节目录:
第一节:使用触摸手势(Using Touch Gestures)
1. Detecting Common Gestures(侦测通用手势)
当用户将一个手指或多个手指放置在触摸屏上时,手势(touch gesture)事件就发生了,你的应用将这个触摸图案解译(interpret)为特殊的手势。相应地,对手势侦测有两个阶段:
1) 收集与触摸事件相关的数据。
2) 解译(interpreting)该数据看看是否符合应用所支持手势的条件。
①Gather Data(收集数据)
当用户将一个或多个手指放置在触摸屏上时,就触发了接收触摸事件的视图上的回调函数 onTouchEvent(),对于一个触摸事件(position, pressure, size, addition of another finger, etc.)序列最终被鉴别为一个手势,而onTouchEvent()函数会被触发多次。
手势起始于用户首次触摸屏幕,持续于系统跟踪用户手指的位置,结束于捕捉最终的用户手指离开屏幕的事件。通过这个交互,被传递给onTouchEvent()函数的MotionEvent事件对象提供了每一个交互的细节。应用则可以根据MotionEvent对象提供的数据决定它所关注的手势事件是否发生。
②Capturing touch events for an Activity or View(为Activity或View捕获触摸事件)
要拦截在Activity或View中的触摸事件,需要重写onTouchEvent()方法。
下面的代码片段使用了getActionMasked()方法来从event parameter事件参数中提取用户执行的动作action。这给了你所需要的原始数据来决定你所关注的手势事件是否要发生。
public class MainActivity extends Activity {
...
// This example shows an Activity, but you would use the same approach if
// you were subclassing a View.
@Override
public boolean onTouchEvent(MotionEvent event){
int action = MotionEventCompat.getActionMasked(event);
switch(action) {
case (MotionEvent.ACTION_DOWN) :
Log.d(DEBUG_TAG,"Action was DOWN");
return true;
case (MotionEvent.ACTION_MOVE) :
Log.d(DEBUG_TAG,"Action was MOVE");
return true;
case (MotionEvent.ACTION_UP) :
Log.d(DEBUG_TAG,"Action was UP");
return true;
case (MotionEvent.ACTION_CANCEL) :
Log.d(DEBUG_TAG,"Action was CANCEL");
return true;
case (MotionEvent.ACTION_OUTSIDE) :
Log.d(DEBUG_TAG,"Movement occurred outside bounds " +
"of current screen element");
return true;
default :
return super.onTouchEvent(event);
}
}
然后你可以在这些事件上做你自己的处理加工来决定手势事件是否发生。这是一种对于你自定义手势必须要做的加工处理方式。但是,如果你的应用使用通用手势如双击(double tap)、长按(long press)、甩屏(fling)等,你就可以利用GestureDetector 类。该类可以让你更容易地侦测通用手势而不用加工处理你自己个人触摸事件。这会在”侦测手势“小节讨论。
③Capturing touch events for a single view (为单个视图捕获触摸事件)
作为一种对onTouchEvent()的替代方案,你可以通过使用setOnTouchListener()方法给任意的View附加一个View.onTouchEventListener触摸事件监听器对象。这使得不用子类化(subclassing)一个存在的View就可以对触摸事件进行监听。例如:
View myView = findViewById(R.id.my_view);
myView.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
// ... Respond to touch events
return true;
}
});
注意创建对ACTION_DOWN 事件监听而返回false的监听器Listener的情况,如何你这样做,那这个监听器对于随后的ACTION_MOVE和ACTION_UP事件将不会被调用.这是因为ACTION_DOWN是对所有触摸事件的起点。
如果你想创建一个自定义的View,那么你就可以如上所述的重写onToucheEvent()方法。
④Detect Gestures (侦测手势)
Android 提供了手势侦测类GestrueDetector 用来侦测通用手势(common gesture),它所支持的一些手势包括onDown()、onLongPress()、onFling()等,你可以结合前面阐述的onTouchEvent()方法使用GestureDetector。
⑤Detecting All Supported Gestures(侦测所有支持的手势)
当你实例化一个GestureDetectorCompat对象时,它所需要的一个参数是一个实现了GestureDetector.OnGestureListener接口的类对象。当一个特殊的触摸事件发生的售后这个接口就会通知给用户。为了让GestureDetector对象能够接受事件,返回值false则会往下通过视图堆栈传递事件,直到这个触摸事件被成功处理。
运行如下代码可以对 当你与触摸屏交互的时动作action是如何触发的以及对应于每一个触摸事件的MotionEvent对象的内容是什么 有一个初步感觉,你将会认识到仅仅对于一个简单的交互事件将会生成多少数据。
public class MainActivity extends Activity implements
GestureDetector.OnGestureListener,
GestureDetector.OnDoubleTapListener{
private static final String DEBUG_TAG = "Gestures";
private GestureDetectorCompat mDetector;
// Called when the activity is first created.
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Instantiate the gesture detector with the
// application context and an implementation of
// GestureDetector.OnGestureListener
mDetector = new GestureDetectorCompat(this,this);
// Set the gesture detector as the double tap
// listener.
mDetector.setOnDoubleTapListener(this);
}
@Override
public boolean onTouchEvent(MotionEvent event){
this.mDetector.onTouchEvent(event);
// Be sure to call the superclass implementation
return super.onTouchEvent(event);
}
@Override
public boolean onDown(MotionEvent event) {
Log.d(DEBUG_TAG,"onDown: " + event.toString());
return true;
}
@Override
public boolean onFling(MotionEvent event1, MotionEvent event2,
float velocityX, float velocityY) {
Log.d(DEBUG_TAG, "onFling: " + event1.toString()+event2.toString());
return true;
}
@Override
public void onLongPress(MotionEvent event) {
Log.d(DEBUG_TAG, "onLongPress: " + event.toString());
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
Log.d(DEBUG_TAG, "onScroll: " + e1.toString()+e2.toString());
return true;
}
@Override
public void onShowPress(MotionEvent event) {
Log.d(DEBUG_TAG, "onShowPress: " + event.toString());
}
@Override
public boolean onSingleTapUp(MotionEvent event) {
Log.d(DEBUG_TAG, "onSingleTapUp: " + event.toString());
return true;
}
@Override
public boolean onDoubleTap(MotionEvent event) {
Log.d(DEBUG_TAG, "onDoubleTap: " + event.toString());
return true;
}
@Override
public boolean onDoubleTapEvent(MotionEvent event) {
Log.d(DEBUG_TAG, "onDoubleTapEvent: " + event.toString());
return true;
}
@Override
public boolean onSingleTapConfirmed(MotionEvent event) {
Log.d(DEBUG_TAG, "onSingleTapConfirmed: " + event.toString());
return true;
}
}
⑥ Detecting a Subset of Supported Gestures(侦测一个支持手势的子集)
如果你只想加工处理少数几个手势,你可以继承GestureDector.SimpleOnGestureListener类而不是去实现GestureDetor.OnGestureListener接口。
GestureDector.SimpleOnGestureListener对所有的on<TouchEvent>方法通过返回false的方式提供了简单的实现。这样你就可以只重写你所关注的方法。例如下面的代码片段创建了一个继承了GestureDector.SimpleOnGestureListener并且重写了onFling()和onDown()方法的子类。
无论你使用上面的方式还是使用GestureDetor.OnGestureListener,最佳实践方式是去实现返回true的onDown()方法。这是因为所有的手势都是起始于onDown() message。如果onDown()方法返回false,GestureDector.SimpleOnGestureListener默认返回false,那么系统就会认为你将要忽略余下其他的手势,对应的其他方法将不会被调用,这将在你的应用中有可能会导致异常问题。在onDown()方法中返回false的唯一时间是你确确实实地想忽略所有的手势。
public class MainActivity extends Activity {
private GestureDetectorCompat mDetector;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDetector = new GestureDetectorCompat(this, new MyGestureListener());
}
@Override
public boolean onTouchEvent(MotionEvent event){
this.mDetector.onTouchEvent(event);
return super.onTouchEvent(event);
}
class MyGestureListener extends GestureDetector.SimpleOnGestureListener {
private static final String DEBUG_TAG = "Gestures";
@Override
public boolean onDown(MotionEvent event) {
Log.d(DEBUG_TAG,"onDown: " + event.toString());
return true;
}
@Override
public boolean onFling(MotionEvent event1, MotionEvent event2,
float velocityX, float velocityY) {
Log.d(DEBUG_TAG, "onFling: " + event1.toString()+event2.toString());
return true;
}
}
}
参考文献:
http://developer.android.com/training/best-user-input.html