Touch事件分发的常见应用就是解决滑动冲突。常见的滑动冲突有父view左右滑动,子view也左右滑动这样,下面从重写父view的onInterceptTouchEvent方法和在子类中调用父类requestDisallowInterceptTouchEvent方法两种方式来举例。
应用场景1,viewpager嵌套webview,而webview中含有可以左右滑动的选项卡,这样viewpager的左右滑动势必会和webview左右滑动的选项卡冲突。解决思路是,重写viewpager的onInterceptTouchEvent方法,在move事件时判断,当在界面的边缘滑动,并且是左右滑动时(x的滑动绝对值大于y的滑动绝对值),将滑动事件给父view,否则滑动事件还交给子view处理。实现代码如下:
/**
* Created by lifengmei on 2016/6/20 18:00.
*/
public class EdgeSwitchPageViewPager extends ViewPager{
private float startX;
private float startY;
public EdgeSwitchPageViewPager(Context context) {
super(context);
}
public EdgeSwitchPageViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
startX = ev.getX();
startY = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
float nowX = ev.getX();
float nowY = ev.getY();
int screenWidth = DeviceUtils.getScreenWidth(getContext());
//当按下时距离左边框或者右边框小于100px,且横向滑动幅度大于纵向滑动幅度时,将滑动焦点还给viewpager用于整个界面的切换
//当在内部滑动时将滑动焦点给子类,用于子类滑动操作
if((startX<100 && Math.abs(nowX - startX)>Math.abs(nowY - startY))
|| ((screenWidth - startX) <100 && Math.abs(nowX - startX)>Math.abs(nowY - startY))){
return true;
}else{
return false;
}
case MotionEvent.ACTION_UP:
break;
}
return super.onInterceptTouchEvent(ev);
// return false;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
return super.onTouchEvent(ev);
}
}
运行,即可看到滑动冲突被解决了。
当然也可以在子view的内部拦截,思路是重写子view的dispatchTouchEvent方法,在down的时候调用父view的requestDisallowInterceptTouchEvent(true)不允许父类拦截,在move和up时在恰当的时机调用父view的getparent.requestDisallowInterceptTouchEvent(false)还给父view,同时还需要重写父view的onInterceptTouchEvent方法,默认拦截除了down事件以外的事件。down不能拦截,如果拦截了以后的任何事件就都不能到达子view了。
应用场景2,scrollview嵌套可滑动的Edittext。实现思路:在Edittext的onTouch方法中判断拦截,当Edittext中的内容实际高度大于控件的固定高度时,表明Edittext中的内容是可以滑动的,此时调用父view的requestDisallowInterceptTouchEvent(true),不允许父view抢占事件,那么事件交给Edittext处理滑动;否则还把事件交给父view处理。在Edittext的onTouch方法中拦截的好处是,确定滑动事件是在Edittext中的,不用再计算滑动事件的位置是否符合条件了,比较快捷省事。实现代码如下:布局文件activity_scroller_and_big_et.xml
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<ImageView
android:layout_width="match_parent"
android:layout_height="400dp"
android:scaleType="fitCenter"
android:src="@drawable/xueyou"
/>
<EditText
android:id="@+id/editText"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_margin="10dp"
android:gravity="top"
android:background="@drawable/shape_semicircle_gray" />
</LinearLayout>
</ScrollView>
activity文件:
public class ScrollerAndBigEtActivity extends AppCompatActivity {
private EditText et;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scroller_and_big_et);
et = (EditText) findViewById(R.id.editText);
et.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {//在onTouch方法中判断的好处是此时触摸事件一定是在edittext中了,不用计算触摸事件的位置是否符合条件了
if(canEtScroll(et)){//Scrollview和Edittext中内容滑动冲突,Edittext内容滑不动,说明滑动事件被Scrollview抢占了;
// down事件肯定没有拦截,此时判断在Edittext滑动时不允许父view拦截事件
et.getParent().requestDisallowInterceptTouchEvent(true);
}else{
et.getParent().requestDisallowInterceptTouchEvent(false);
}
return false;//不消费事件,还交给onTouchEvent处理滑动事件
}
});
}
/**
* 判断edittext中的内容是否可滚动,当内容高度大于控件固定高度时,edittext内容是可以滑动的,返回true
* */
private boolean canEtScroll(EditText et){
//et控件的固定高度
int etHeight = et.getHeight()-et.getCompoundPaddingTop()-et.getCompoundPaddingBottom();
//et内容的高度
int etContentHeight = et.getLayout().getHeight();
//二者之差
int dValue = etContentHeight - etHeight;
return dValue > 0;
}
}