ViewGroup事件处理之动态切换ViewPagerIndicator页签

ViewGroup事件处理机制

在上一篇中实现了ViewPagerIndicator来绑定ViewPager动态转换小标题的功能,这一篇会通过事件的角度,去实现滑动ViewPager,转换切换不同的小标题,并在恰当的时候,拉出SlidingMenu。

Android系统中的每个ViewGroup的子类都具有下面三个和TouchEvent处理密切相关的方法:

1)public boolean dispatchTouchEvent(MotionEvent ev)          这个方法用来分发TouchEvent
2)public boolean onInterceptTouchEvent(MotionEvent ev)         这个方法用来拦截TouchEvent
3)public boolean onTouchEvent(MotionEvent ev)                 这个方法用来处理TouchEvent

他们之间的关系如图:


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

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

事件处理小结:

总之我们可以理解为父控件(out)包裹子空间(Middle)包裹孙子控件(center),在这个传递过程中一旦有一个调用了onTouchEvent,事件被处理,传递结束。在dispatchTouchEvent分派任务时,只要OnInterCeptTouchEvent不拦截,事件就会一直传递下去。

有了这个概念之后,我们可以总结出:

子控件处理事件角度

子控件不想让父控件和祖先控件拦截事件时,只需要在dispatchtouchEvent中通知父控件不需要拦截即可:

例如:

  1 在一个继承了ViewPager类中可使用子控件请求父控件不拦截事件:

/*
 * 事件分发,请求父控件以及祖先控件不阻拦事件
 */
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
	getParent().requestDisallowInterceptTouchEvent(true);	
	return super.dispatchTouchEvent(ev);
}


父控件处理事件角度

  2 同样在一个继承了ViewPager的父 控件不想拦截子控件的事件:

/*
	 * 设置为false,表示不拦截事件,事件会继续向下传递
	 * 
	 */
	@Override
	public boolean onInterceptTouchEvent(MotionEvent arg0) {
		// TODO Auto-generated method stub
		return false;
	}

通常在这种情况下,会将onTouchEvent设置返回false

首先触摸事件发生时(ACTION_DOWN),由系统调用Activity的dispatchTouchEvent方法,分发该事件。/*
	 * 重新ViewPager 使其不产生左右滑动事件
	 */
	
	@Override
	public boolean onTouchEvent(MotionEvent arg0) {
		// TODO Auto-generated method stub
		return false;
	}
	/*


智慧北京实例:

智慧北京的项目中,由于SlidingMenu和ViewPagerIndictor的使用,出现了多层的控件包裹关系,与其相关的ViewPager关系图:


其中ViewPagerIndicator与ViewPager2进行了绑定,通过填充Fragment嵌套在ViewPager1中,在应用中发现SlidingMenu是作为这些ViewPager的父控件存在的。

步骤 1:

1 整个项目进行编写时,是先添加了slidingmenu,再添加了ViewPager1 ,实现了如下左边的效果;

    

当向右滑动时,可以进行切换不同的ViewPager,但是向左滑动时经常将Slidingmenu画出。

分析:slidingmenu是一个父控件,当向右滑动时,拦截了ViewPager1的事件,因此从父控件的角度来讲,不去阻拦事件即可,但是这里需要更改SlidingMenu的源代码,一种更为简单的方式是使用RadionGroup进行监听,使用按钮切换不同的ViewPager界面,这时只需要在自定义一个ViewPager1,复写onTouchEvent即可。

@Override
	public boolean onTouchEvent(MotionEvent arg0) {
		// TODO Auto-generated method stub
		return false;
	}


步骤 2:

在添加了ViewpagerIndicator并与ViewPager2进行关联之后的效果如图:



问题 1 

到这里的第一个问题就是无法滑动ViewPager2,因为父控件ViewPager1已经将onTouchEvent返回false,并且控件默认是阻拦事件下发的.

解决方法:

在自定义父控件ViewPager1中允许向子控件传递事件:

/*
	 * 设置为false,表示不拦截事件,事件会继续向下传递
	 * 
	 */
	@Override
	public boolean onInterceptTouchEvent(MotionEvent arg0) {
		// TODO Auto-generated method stub
		return false;
	}

问题 2

这个问题解决之后,可以发现可以通过滑动ViewPager2向右来切换IndicatorViewPager标签了,向左滑动时,同样会出现拉出Slidngmenu的情况:

解决方法:(更改Slingmenu源码太麻烦了)

更改IndicatorViewPager的源码,在ViewPagerIndicator的源码中复写dispatchTouchEvent,使子控件(IndicatorViewPager)请求父控件(slidingmenu)不拦截事件。

/*
     * 事件分发,请求父控件以及祖先控件不拦截事件
     * 
     */
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
    	getParent().requestDisallowInterceptTouchEvent(true);
    	
    	return super.dispatchTouchEvent(ev);
    }


对于ViewPager2 的结局方法方式相同,自定义个ViewPager,复写一下dispatchTouchEvent,然后把源码粘进去就行了。

ViewPager2与ViewpagerIndicator都可以理解为ViewPager

问题 3: 

 我们现在解决了不能滑动ViewPager2 以及向左滑动ViewPager2和ViewPagerIndicator出现SlidingMenu的BUG,

现在新的BUG是除非手动点击ViewPagerIndicator标签切换ViewPager2,否则无法自动切换到下一个页面。

解决方法:

监听ViewPager2滑动事件,在以下三种情况时,需要父控件进行拦截:

1 当滑动到第一个ViewPager时,如果此时依旧向左滑动,则跳转到上一个标签;

2 当滑动到当前ViewPager的最后一个画面时,如果还是向右滑动,则切换到下一个标签;

3 上下进行滑动
 同样自定义一个ViewPager控件,复写dispatchTouchEvent:

 public boolean dispatchTouchEvent(MotionEvent ev) {		
		// startX,startY;
		int startx=0,starty=0;
		int endx,endy;
		switch (ev.getAction()) {
		case MotionEvent.ACTION_DOWN: //检测到手指按下
			getParent().requestDisallowInterceptTouchEvent(true); //子控件请求父控件不拦截事件 
			startx=(int) ev.getRawX();
			starty=(int) ev.getRawY();			  
			break;
		case MotionEvent.ACTION_MOVE: //检测到手指移动
			endx=(int) ev.getRawX();
			endy=(int) ev.getRawY();
			if(Math.abs(endx-startx) > Math.abs(endy-starty))//水平滑动
			{
				if(endx>startx) //向右滑动ViewPager
				{
					if(getCurrentItem()==0)//第一个页面,需要父控件进行拦截,
					{
						getParent().requestDisallowInterceptTouchEvent(false); //需要父控件拦截
					}
				}
				else //左滑动ViewPager
				{ 
					if(getCurrentItem()==(getAdapter().getCount()-1))
					{
						getParent().requestDisallowInterceptTouchEvent(false); //需要父控件拦截
					}			
				}
			} 	
			else//上下滑动
			{
				getParent().requestDisallowInterceptTouchEvent(false); //需要父控件拦截
			}
			break;
		default:
			break;
		}
		
    	return super.dispatchTouchEvent(ev);
    }


致此所有BUG完美解决,可以方便的通过滑动ViewPager2 进行页签转换了!!!!


搞明白的过程真的是好痛苦!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值