今天写东西遇到了滑动触发动画的问题,于是开始了Android监听手势之旅。然而并不是特别顺利,特记下一些心得体会与相关的理解。
Android中主要有两种手势行为,一是官方提供的特定手势,而是用户自定义的手势。本文主要讲关于android提供的手势交互。
手势交互顺序
Android中的手势交互可分解为几个小过程:
- 手指与屏幕接触,触发MotionEvent事件。
- MotionEvent事件中封装了一些手势动作的事件,以及x,y轴的坐标值。
- OnTouchListener监听MotionEvent事件,通过其onTouch()方法可获得该事件对象。
- MotionEvent事件可由GestureDetector转发给OnGestureListener,在OnGestureListener中获取事件对象,进行下一步操作。
- GestureDetector可以识别各种手势
- OnGestureListener用于监听各种的手势交互
GestureListener的回调方法
- onDown() : 按下触屏瞬间
- onFling() : 抛掷动作,即手指迅速移动后松开
- onLongPress() : 长按
- onScroll() : 滑动拖动
- onShowPress() : 按住,时间范围在长按之前
- onSingleTapUp : 手指离屏瞬间
手势监听
- 创建DestureDetector对象,传入OnGestureListener
- 将Activity或特定组件上的TouchEvent事件交给Gestureor处理
1 | // 主要代码 |
注:不要忘了转交手势事件。Demo中最好不要放其他组件,先简单感受一下手势交互,原因后边会讲。
由于回调方法将事件分解的十分细致,所以你一个动作可能会连续触发好几个动作。如一次点击就会触发onDown()和onSingleTapUp()方法。
SimpleOnGestureListener
第一次写以上demo时候就感觉有些蠢,有时候只是想要监听点击手势或滑动手势,却就要实现5,6个回调方法。官方也是这么想的,所以给你封装一个SimpleGestureListener类,只需要重写你想要监听的手势的方法就好了。
使用与OnGestureListener相同。
栗子:
以下是菜鸟上的一个简单的栗子,借用一下
1 | public class MainActivity extends AppCompatActivity { |
以下也是我初体验时比较坑的地方,我一上来就直接在ListView上识别滑动手势,却始终不能成功识别。原来我只传递了Activity上的TouchEvent,所以没能成功识别到。
所以这里以ListView为例了解一下其他控件的滑动事件监听。
ListView的手势监听
ListView主要有两种滑动事件监听方法,OnTouchListener与OnSrcollListener
OnTouchListener
该方法来自View中的监听事件,通过监听Action.DOWN,Action.MOVE,Action.UP以及其发生的位置坐标即可判断用户的滑动方向。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17mListView.setOnTouchListener(new View.OnTouchListener()){
public boolean onTouch(View v,MotionEvent event){
swicth(event.getAction()){
case:MotionEvent.ACTION_DOWN:
//触摸
break;
case:MotionEvent.ACTION_MOVE:
//移动
break;
case:MotionEvent.ACTION_MOVE:
//离开
break;
}
}
return false;
}
OnScrollListener
1 | listView.setOnScrollListener(new AbsListView.OnScrollListener() { |
OnScrollListener()中有两个回调方法onScrollStateChanged()与onScroll(),都会进行多次回调
onScrollStateChanged()
srrollState参数有三种情况,当滑动为抛掷动作时,该方法只会回调3次,无抛掷动作(可理解为拖动)则回调2次。即滑动时回调,停止时回调,若有惯性滑动,则多回调一次。
可以在这个方法中通过不同的状态来设置一些Flag,区分不同的滑动状态,供其他方法处理。
onScroll()
该方法在ListView滚动时会一直不断的回调,通过其参数可进行滑动的判断
如判断是否滚动到最后一行,即当前第一个ItemID + 当前Item和 == Item总数的时候,可以进行加载更多Item等操作。
1 | if(firstVisibleItem+visibleItemCount==totalItemCount&&totalItemCount>0){} |
然而到这里仍然没有解决我的需求。因为我是需要在一个SrcollView与WebView嵌套的结构中监听滑动,ScrollView和WebView都是可滑动控件,所以操作起来麻烦。还有就是滑动动作一直作用在SrcollView上,所以Activity一直无法获得手势事件,也就无法触发方法。
所以需要解决的就是ScrollView或WebView的滑动监听。
最后我是通过实现ScrollView的滑动监听解决了这个问题。
实现ScrollView的滑动监听
继承ScrollView
和WebView一样,ScrollView的滑动监听方法onScrollChanged()是一个受保护的方法,这意味着想要使用它必须实现一个自己的ScrollView重写原方法。关于这一点Google已经被黑惨了。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36// 自定义ScrollView
public class RLScrollView extends ScrollView {
private OnScrollChangedListener onScrollChangedListener;
public RLScrollView(Context context) {
super(context);
}
public RLScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public RLScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public interface OnScrollChangedListener{
public void onScrollChanged(int x, int y, int oldxX, int oldY);
}
public void setOnScrollListener(OnScrollChangedListener onScrollChangedListener){
this.onScrollChangedListener=onScrollChangedListener;
}
protected void onScrollChanged(int x, int y, int oldX, int oldY){
super.onScrollChanged(x, y, oldX, oldY);
if(onScrollChangedListener!=null){
onScrollChangedListener.onScrollChanged(x, y, oldX, oldY);
}
}
}
// 调用
scrollView.setOnScrollListener(new RLScrollView.OnScrollChangedListener() {
public void onScrollChanged(int x, int y, int oldxX, int oldY) {
// 实现通过判断先后Y值差获取有效滑动距离
if(Math.abs(y-oldY) < 20)
return ;
});
使用NestedScrollView类
后来发现v4包下有一个NestScrollView类,相当于一个封装好的ScrollView类,使用方式与自实现ScrollView相同。