您的位置:首页 > 其它

关于解决多个viewpager嵌套所遇到的事件传递问题

2016-03-25 23:56 232 查看

    在开发时候我们会有一种情景,类似于新闻客户端的,就是在主页面的viewpager里面的view或者frament中再嵌套了一个viewpager,这时候如果不做任何处理的话,我们父view中的viewpager和子view的viewpager会产生冲突,比如滑动的卡顿等。

       所以为了解决这个情况我们需要了解一下安卓中的事件传递机制。如果有对安卓的事件传递机制的人不那么熟悉的话,那请自己去研究一下,在这里我只简单说一下时间传递机制的基本原理。



说明:

首先触摸事件发生时(ACTION_DOWN),由系统调用Activity的dispatchTouchEvent方法,分发该事件。根据触摸事件的坐标,将此事件传递给out的dispatchTouchEvent处理,out则调用onInterceptTouchEvent 判断事件是由自己处理,还是继续分发给子View。此处由于out不处理Touch事件,故根据事件发生坐标,将事件传递给out的直接子View(即middle)。

Middle及Center中事件处理过程同上。但是由于Center组件是clickable 表示其能处理Touch事件,故center中的onInterceptTouchEvent方法将事件传递给center自己的onTouchEvent方法处理。至此,此Touch事件已被处理,不继续进行传递。

关于事件传递自己有以下总结

1. 事件首先传递给父控件-->父控件最新接收到事件,`拥有事件处理的最高优先级`;

2. 关键方法
1. dispatchTouchEvent:是否派发,常见处理-->不做任何的处理.
2. onInterceptTouchEvent:是否拦截
1. 是(true)-->调用自己的onTouchEvent去处理事件,`如果你对MotionEvent进行了拆分(action_down,action_move,action_up,action_cancel),这个时候传递流程不能说的那么绝对`
2. 否(false)-->事件传递给孩子
1. 孩子是view-->ontouchEvent
2. 孩子是viewGroup-->和之前的情况一样

        3. onTouchEvent:是否消费
1. return true:消费事件
2. return false:不消费事件

 以下是在自己写的程序中打印的一些测试log

ScrollableViewGroup以及ViewHook事件传递流程分析

 ---ScrollableViewGroup(父亲)----onInterceptTouchEvent---MotionEvent.ACTION_DOWN--- return false
---viewGroudHook---onTouchEvent---ACTION_DOWN  return true
----ScrollableViewGroup(父亲)---onInterceptTouchEvent---MotionEvent.ACTION_MOVE--- return true,onInterceptTouchEvent返回true的时候.肯定回来自己的onTouchevent里面,但是可能会去孩子那里执行以下action_cancel
---viewGroudHook---onTouchEvent---ACTION_CANCEL--->这个地方很容易产生疑惑
---ScrollableViewGroup(父亲)---onTouchEvent-----MotionEvent.ACTION_MOVE------- 父亲在onInterceptTouchEvent里面的action_move分支下return true,所以我们进入的是ontouchEvent的Action_move
---ScrollableViewGroup(父亲)---onTouchEvent-----MotionEvent.ACTION_MOVE------- 这里我们用打断点的形式.没有看到action_move.所以我们常用打日志的形式进行了分析
---ScrollableViewGroup(父亲)---onTouchEvent-----MotionEvent.ACTION_MOVE-------
---ScrollableViewGroup(父亲)---onTouchEvent-----MotionEvent.ACTION_UP-------


以上就是个人对安卓的事件传递机制的一些小总结,可能也借鉴了一下别人的图,请见谅

大家看了上面事件传递机制,应该也基本有所了解,所以我就直接贴源码好了。

这里我重写了viewpager,实现的逻辑是当滑动到子view的viewpager的头或者尾的时候就请求父控件进行事件拦截,然后实现父view的viewpager的滑动事件。但是我看了网易新闻的客户端,貌似头条新闻并不是到尾它就切换到父viewpaer的下一个view,所以大家根据自己的需求改一下以下的代码吧

/**
* 头条新闻水平滑动的ViewPager
* @auth
a62c
or Administrator
*
*/
public class TopNewsViewPager extends ViewPager{

private int startX;
private int startY;

public TopNewsViewPager(Context context) {
super(context);
// TODO Auto-generated constructor stub
}

public TopNewsViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}

/**
* 事件分发,请求父控件及祖宗控件是否拦截事件
* 1.用户右滑,而且是第一个页面,就需要父控件拦截,显示上一个标签或者侧边栏
* 2.用户左滑,而且是最后一个页面,就需要父控件拦截,显示下一个标签
* 3.上下滑动,需要父控件拦截
*/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {

//    	//用getParent去请求,希望父控件不拦截这个OnToch事件
//        getParent().requestDisallowInterceptTouchEvent(true);

switch (ev.getAction()) {

case MotionEvent.ACTION_DOWN:
//用getParent去请求,希望父控件不拦截这个OnToch事件
//这样为了保证ACTION_MOVE调用
getParent().requestDisallowInterceptTouchEvent(true);

startX = (int) ev.getRawX();
startY = (int) ev.getRawY();

break;

case MotionEvent.ACTION_MOVE:

int endX=(int) ev.getRawX();
int endY=(int) ev.getRawY();

if(Math.abs(endX-startX)>Math.abs(endY-startY)){//左右滑动

if(endX>startX){//右滑

if(getCurrentItem()==0){//第一个页面,需要父控件拦截

getParent().requestDisallowInterceptTouchEvent(false);
}

}else{//左滑

if(getCurrentItem()==getAdapter().getCount()-1){//最后一个页面,需要拦截

getParent().requestDisallowInterceptTouchEvent(false);
}

}

}else{//上下滑动,需要父控件拦截
getParent().requestDisallowInterceptTouchEvent(false);
}

break;
}

return super.dispatchTouchEvent(ev);
}

}


以上大概就是我对多个viewpager嵌套的事件传递的解决吧。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: