0%

Android手势监听入门

今天写东西遇到了滑动触发动画的问题,于是开始了Android监听手势之旅。然而并不是特别顺利,特记下一些心得体会与相关的理解。

Android中主要有两种手势行为,一是官方提供的特定手势,而是用户自定义的手势。本文主要讲关于android提供的手势交互。

手势交互顺序

Android中的手势交互可分解为几个小过程:

  1. 手指与屏幕接触,触发MotionEvent事件
    • MotionEvent事件中封装了一些手势动作的事件,以及x,y轴的坐标值。
  2. OnTouchListener监听MotionEvent事件,通过其onTouch()方法可获得该事件对象。
  3. MotionEvent事件可由GestureDetector转发给OnGestureListener,在OnGestureListener中获取事件对象,进行下一步操作。
    • GestureDetector可以识别各种手势
    • OnGestureListener用于监听各种的手势交互

GestureListener的回调方法

  • onDown() : 按下触屏瞬间
  • onFling() : 抛掷动作,即手指迅速移动后松开
  • onLongPress() : 长按
  • onScroll() : 滑动拖动
  • onShowPress() : 按住,时间范围在长按之前
  • onSingleTapUp : 手指离屏瞬间

手势监听

  • 创建DestureDetector对象,传入OnGestureListener
  • 将Activity或特定组件上的TouchEvent事件交给Gestureor处理
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
// 主要代码
class Activity extends AppCompatActivity{
GestureDetect detector = new GestureDetector(this, new GestureDetector.OnGestureListener() {
@Override
public boolean onDown(MotionEvent e) {
return false;
}
@Override
public void onShowPress(MotionEvent e) {

}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return false;
}
@Override
public void onLongPress(MotionEvent e) {

}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
return false;
}
});
@Override
public boolean onTouchEvent(MotionEvent e){
return detector.onTouchEvent(e);
}
}

注:不要忘了转交手势事件。Demo中最好不要放其他组件,先简单感受一下手势交互,原因后边会讲。
由于回调方法将事件分解的十分细致,所以你一个动作可能会连续触发好几个动作。如一次点击就会触发onDown()和onSingleTapUp()方法。

SimpleOnGestureListener

第一次写以上demo时候就感觉有些蠢,有时候只是想要监听点击手势或滑动手势,却就要实现5,6个回调方法。官方也是这么想的,所以给你封装一个SimpleGestureListener类,只需要重写你想要监听的手势的方法就好了。
使用与OnGestureListener相同。

栗子:

以下是菜鸟上的一个简单的栗子,借用一下

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
public class MainActivity extends AppCompatActivity {

private GestureDetector mDetector;
private final static int MIN_MOVE = 200; //最小距离
private MyGestureListener mgListener;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//实例化SimpleOnGestureListener与GestureDetector对象
mgListener = new MyGestureListener();
mDetector = new GestureDetector(this, mgListener);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return mDetector.onTouchEvent(event);
}
private class MyGestureListener extends GestureDetector.SimpleOnGestureListener {

@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float v, float v1) {
if(e1.getY() - e2.getY() > MIN_MOVE){
startActivity(new Intent(MainActivity.this, MainActivity.class));
Toast.makeText(MainActivity.this, "通过手势启动Activity", Toast.LENGTH_SHORT).show();
}else if(e1.getY() - e2.getY() < MIN_MOVE){
finish();
Toast.makeText(MainActivity.this,"通过手势关闭Activity",Toast.LENGTH_SHORT).show();
}
return true;
}
}
}

以下也是我初体验时比较坑的地方,我一上来就直接在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
17
mListView.setOnTouchListener(new View.OnTouchListener()){
@Override
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
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
listView.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
switch (scrollState){
case SCROLL_STATE_IDLE:
// 滑动停止时调用
Toast.makeText(TestActivity.this, "停啦...", Toast.LENGTH_SHORT).show();
break;
case SCROLL_STATE_TOUCH_SCROLL:
// 滚动时调用
Toast.makeText(TestActivity.this, "正在滑动...", Toast.LENGTH_SHORT).show();
break;
case SCROLL_STATE_FLING:
// 离屏后ListView惯性滑动时
Toast.makeText(TestActivity.this, "停不下来...", Toast.LENGTH_SHORT).show();
break;
}
}
/**
* @param firstVisibleItem 当前能看到的第一个Item的id,包括未显示完整的Item,即Item的序号,从0开始
* @param visibleItemCount 当前能看到的Item数
* @param totalItemCount ListView的Item总数
*/
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
Toast.makeText(TestActivity.this, "第 " + firstVisibleItem, Toast.LENGTH_SHORT).show();
}
});

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;
}
@Override
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() {
@Override
public void onScrollChanged(int x, int y, int oldxX, int oldY) {

// 实现通过判断先后Y值差获取有效滑动距离
if(Math.abs(y-oldY) < 20)
return ;
});

使用NestedScrollView类

后来发现v4包下有一个NestScrollView类,相当于一个封装好的ScrollView类,使用方式与自实现ScrollView相同。



完结 撒花 ฅ>ω<*ฅ