Android ViewPager + View 使用及其事件分发一点经验

根据Developer中关于viewPager的描述,ViewPager常和Fragment搭配使用,而其最简单的使用方法,可以直接往里面添加view,甚至是ImageView,做成gallery的样子。对于添加View的简单用法,一下几篇博客有比较详细的说明。
【1】http://blog.csdn.net/alangdangjia/article/details/9054921
【2】http://blog.csdn.net/eyu8874521/article/details/7838525

简单来说是这样。

  1. 在界面的布局里接入一个viewpager的控件,注意这个控件实在v4的包里
  2. 在代码中取出这个ViewPager
  3. 把要添加到ViewPager里的view Inflate出来,即把他们构造好
  4. 新建一个PagerAdapter,具体到使用Fragment的话,就使用该类的派生类FragmentPagerAdapter 和 FragmentStatePagerAdapter
    5.写好PagerAdapter里面的重载方法,主要的有getCount,destroyItem,instantiateItem(ViewGroup container, int position) 。注意这些方法中container的类属为view的方法已经过时,现在推荐使用ViewGroup。必须要在instantiateItem中往container里添加所需要的view
  5. 给viewpager添加adapter,并且是指onPageSelectedListener等

借用百度地图某一个版本的样式截图,请看上方的控件

当我尝试复现这种效果时,我一开始为了方便,每个view使用8个TextView,然后给Textview设置DrawableTop,和上图的样式打到几乎一样,此时问题来了,当我直接添加view进去后,viewpager能够正常滑动,而一旦添加了OnClick事件以后,无论在什么位置添加这个事件,有Textview可见的位置,都不在相应手势滑动。

这显然是个奇怪的情况,按说滑动和点击事件是可以同时相应,android的launcher就是一个典型的例子,而我尝试了各种事件的dispatch中返回true 、 false, 都没有用。

问题描述完了,不绕圈了,直接先说解决方法。
解决方法
最简单的解决方法,控件全部换成ImageButton,事件冲突的问题就解决了,ImageButton和Button和Textview的不同之处在于,其listener在系统层面就添加过了,他们对于onTouch事件是会分发的(分发模型中,事件首先以隧道方式传递到最内层的控件,本例中即icon们先得到事件,如果textview没有添加onClick,则会把事件直接返回给viewpager,一旦添加了onclick,改方法默认会拦截手势事件,所以上层的viewpager就没有ontouch了,而imageButton本来就有onClick,并且设置了不拦截事件,这样就实现了既可以点击,也可以滑动的效果)。

当然,重写所有textview的onTouch事件经试验也是可以的,不过要自己判断是否为点击手势。

同理的,在我做的小游戏,《拼方块》中,所用的方块控件正是ImageButton的组合,事实也证明,ImageView无法实现拖动的效果。和这里类似

安卓中的事件分发机制参考博客:
http://www.cnblogs.com/sunzn/archive/2013/05/10/3064129.html
http://blog.csdn.net/sunnyfans/article/details/8221505

下面这篇博客所讲授的原理,很好的说明本文所说的现象,如果觉得本文解释的太简单,请详细阅读下文
http://blog.csdn.net/lzqjfly/article/details/11981611
为了表示对博客详细分析的感谢和崇拜,在此摘录一部分:

当我们通过调用setOnClickListener方法来给控件注册一个点击事件时,就会给mOnClickListener赋值。然后每当控件被点击时,都会在performClick()方法里回调被点击控件的onClick方法。

这样View的整个事件分发的流程就让我们搞清楚了!不过别高兴的太早,现在还没结束,还有一个很重要的知识点需要说明,就是touch事件的层级传递。我们都知道如果给一个控件注册了touch事件,每次点击它的时候都会触发一系列的ACTION_DOWN,ACTION_MOVE,ACTION_UP等事件。这里需要注意,如果你在执行ACTION_DOWN的时候返回了false,后面一系列其它的action就不会再得到执行了。简单的说,就是当dispatchTouchEvent在进行事件分发的时候,只有前一个action返回true,才会触发后一个action。

说到这里,很多的朋友肯定要有巨大的疑问了。这不是在自相矛盾吗?前面的例子中,明明在onTouch事件里面返回了false,ACTION_DOWN和ACTION_UP不是都得到执行了吗?其实你只是被假象所迷惑了,让我们仔细分析一下,在前面的例子当中,我们到底返回的是什么。

参考着我们前面分析的源码,首先在onTouch事件里返回了false,就一定会进入到onTouchEvent方法中,然后我们来看一下onTouchEvent方法的细节。由于我们点击了按钮,就会进入到第14行这个if判断的内部,然后你会发现,不管当前的action是什么,最终都一定会走到第89行,返回一个true。

是不是有一种被欺骗的感觉?明明在onTouch事件里返回了false,系统还是在onTouchEvent方法中帮你返回了true。就因为这个原因,才使得前面的例子中ACTION_UP可以得到执行。

那我们可以换一个控件,将按钮替换成ImageView,然后给它也注册一个touch事件,并返回false。在ACTION_DOWN执行完后,后面的一系列action都不会得到执行了。这又是为什么呢?因为ImageView和按钮不同,它是默认不可点击的,因此在onTouchEvent的第14行判断时无法进入到if的内部,直接跳到第91行返回了false,也就导致后面其它的action都无法执行了。


文章为原创,转载请注明出处。

你可能感兴趣的:(Android ViewPager + View 使用及其事件分发一点经验)