相信大家都有接触过容器嵌套出现滑动冲突,尤其是轮播图和listView之间的滑动的不协调。那如何去解决呢?
在Android的View系列中分为两种ViewGroup和View,对于滑动事件的相关方法有:
- dispatchTouchEvent(ev);
- onInterceptTouchEvent(ev); --->ViewGroup及子类独有的
- onTouchEvent(ev);
这三个表示事件的分发,事件的拦截,事件的消费,一个触摸的事件一般分别先从dispathc->onIntercept->onTouch。当然是在默认的情况下。这三个方法返回值类型为boolean。返回值决定了这触摸事件的顺序和执行的结果。
触摸事件传递机制:
一般情况下,ViewGroup下包含子View,最近在做一个项目就是使用SwipeRefreshLayout+ViewPager以及常见的ListView,在滑动的时候,发现三个组件对滑动都有响应。。。体验很差。于是花了一个下午的时间把滑动事件又重新梳理了一下。废话少说,言归正传:
直接看图来的直观,画的好累,有错误还请指正哦,在黑色线条为人为不去干扰的传递机制,而在开发中我们通过onInterceprt()这个方法来改变这个传递的顺序。
onInterceprt():在返回true,表示拦截此次触摸事件,也就是不在往下传递触摸事件。默认传递。
dispatch():一般在开发中,不去动它。
onTouchEvent(): 事件的处理---->你要处理的逻辑代码。
注意:在dispatch()中不管你返回false还是true,对于触摸传递机制都不会往下传递。只有官方的return super事件才往下传递。
代码栗子:
自定义ViewGroup和自定义Button,在ViewGroup拦截action.Move事件,推理可得-->button的点击事件不受影响的。点击出现Toast
代码如下:
package com.example.huangzhibo.intercept;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.RelativeLayout;
import android.widget.ScrollView;
import static android.R.attr.y;
/**
* Created by HuangZhiBo on 2017/3/22.
*/
public class MyViewGroup extends RelativeLayout {
private int y;
public MyViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
}
private static final String TAG = "MyScrollView";
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean result = false;
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
//DOWN事件千万不能拦截
result = false;
y = (int) ev.getY();
Log.i(TAG, "onInterceptTouchEvent: ---ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.i(TAG, "onInterceptTouchEvent:------ACTION_MOVE ");
float v = ((int)ev.getY() - y);
/* Log.i(TAG, "onInterceptTouchEvent: "+v);
if (Math.abs(v)>10) {
result = true;
}else{
result = false;
}*/
result=true;
break;
case MotionEvent.ACTION_UP:
Log.i(TAG, "onInterceptTouchEvent: -----ACTION_UP");
result = false;
break;
}
return result;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
Log.i(TAG, "onTouchEvent: ---ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.i(TAG, "onTouchEvent:------ACTION_MOVE ");
break;
case MotionEvent.ACTION_UP:
Log.i(TAG, "onTouchEvent: -----ACTION_UP");
break;
}
return super.onTouchEvent(ev);
}
}
public class MyButton extends android.support.v7.widget.AppCompatButton { public MyButton(Context context, AttributeSet attrs) { super(context, attrs); } private static final String TAG = "MyButton"; @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: Log.i(TAG, "onTouchEvent: ---ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.i(TAG, "onTouchEvent:------ACTION_MOVE "); break; case MotionEvent.ACTION_UP: Log.i(TAG, "onTouchEvent: -----ACTION_UP"); break; } return super.onTouchEvent(event); } }
运行程序-> 点击button Toast并未出现,再看log信息,可以看出MOVE多次调用,只是点击为啥Move出现多次,猜测在onInterceptTouchEvent的MOVE直接返回true,虽然只是轻轻点击了一下,可系统认定为滑动事件(很微妙),此刻点击事件就无效了。
程序的log打印:
接着改善(ViewGroup):
package com.example.huangzhibo.intercept;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.RelativeLayout;
import android.widget.ScrollView;
import static android.R.attr.y;
/**
* Created by HuangZhiBo on 2017/3/22.
*/
public class MyViewGroup extends RelativeLayout {
private int y;
public MyViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
}
private static final String TAG = "MyScrollView";
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean result = false;
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
//DOWN事件千万不能拦截
result = false;
y = (int) ev.getY();
Log.i(TAG, "onInterceptTouchEvent: ---ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.i(TAG, "onInterceptTouchEvent:------ACTION_MOVE ");
float v = ((int)ev.getY() - y);
Log.i(TAG, "onInterceptTouchEvent: "+v);
if (Math.abs(v)>10) {
result = true;
}else{
result = false;
}
break;
case MotionEvent.ACTION_UP:
Log.i(TAG, "onInterceptTouchEvent: -----ACTION_UP");
result = false;
break;
}
return result;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
Log.i(TAG, "onTouchEvent: ---ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.i(TAG, "onTouchEvent:------ACTION_MOVE ");
break;
case MotionEvent.ACTION_UP:
Log.i(TAG, "onTouchEvent: -----ACTION_UP");
break;
}
return super.onTouchEvent(ev);
}
}
log打印:
可以看出,我们在Move的滑动事件中,给它设置了个滑动距离才拦截滑动事件。这样用户点击了button由于对于微观的滑动来说,并未拦截。响应点击事件。
结束语:
在onInterceptTouchEvent中如果返回return Super.onInterceptTouchEvent(ev)以上代码无效,至于原因的从源代码分析,对于要进行人为干预的onInterceptTouchEvent 最好不要return Super.onInterceptTouchEvent( ).