ViewPager,ScrollView 嵌套ViewPager滑动冲突解决
来源:互联网 发布:红蜘蛛软件一年多少钱 编辑:程序博客网 时间:2024/05/21 09:42
http://blog.csdn.net/gdutxiaoxu/article/details/52939127
版权声明:本文为博主原创文章,未经博主允许不得转载。
目录(?)[+]
- ViewPagerScrollView 嵌套ViewPager滑动冲突解决
- 本篇文章已授权微信公众号 guolin_blog 郭霖独家发布
- 先看一下效果图
- ScrollView里面嵌套ViewPager
- ViewPager里面嵌套ViewPager
- View的 事件分发机制
- 下面引用图解 Android 事件分发机制这一篇博客的内容
- 总结
- 解决事件滑动冲突的思路及方法
- 常见的三种情况
- 解决思路
- 外部解决法
- 内部解决法
- ScrollView 里面嵌套ViewPager导致的滑动冲突
- 外部解决法
- 内部解决法
- 注意事项坑
- 在Activity中的相关解决方法
- 在Fragment中的相关解决方法
- 个人疑点
- ViewPager里面嵌套ViewPager导致的滑动冲突
- 内部解决法
- 外部解决法
- 讨论
- 总结
- 题外话
ViewPager,ScrollView 嵌套ViewPager滑动冲突解决
本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布
这篇博客主要讲解一下几个问题
- 粗略地介绍一下View的事件分发机制
- 解决事件滑动冲突的思路及方法
- ScrollView 里面嵌套ViewPager导致的滑动冲突
- ViewPager里面嵌套ViewPager 导致的滑动冲突
- 轮播图的几种实现方式
文章首发地址CSDN:http://blog.csdn.NET/gdutxiaoxu/article/details/52939127
先看一下效果图
ScrollView里面嵌套ViewPager
ViewPager里面嵌套ViewPager
View的 事件分发机制
这篇博客大打算详细讲解View的事件分发机制,因为网上已经出现了一系列的好 文章,我自己的水平也有限,目前肯定写得不咋的。
先啰嗦一下,View 的事件分发机制主要涉及到一下三个 方法
- dispatchTouchEvent ,这个方法主要是用来分发事件的
- onInterceptTouchEvent,这个方法主要是用来拦截事件的(需要注意的是ViewGroup才有这个方法,View没有onInterceptTouchEvent这个方法
- onTouchEvent这个方法主要是用来处理事件的
- requestDisallowInterceptTouchEvent(true),这个方法能够影响父View是否拦截事件,true表示 不拦截事件,false表示拦截事件
下面引用图解 Android 事件分发机制这一篇博客的内容
- 仔细看的话,图分为3层,从上往下依次是Activity、ViewGroup、View
- 事件从左上角那个白色箭头开始,由Activity的dispatchTouchEvent做分发
- 箭头的上面字代表方法返回值,(return true、return false、return super.xxxxx(),super 的意思是调用父类实现。
- dispatchTouchEvent和 onTouchEvent的框里有个【true—->消费】的字,表示的意思是如果方法返回true,那么代表事件就此消费,不会继续往别的地方传了,事件终止。
- 目前所有的图的事件是针对ACTION_DOWN的,对于ACTION_MOVE和ACTION_UP我们最后做分析。
- 之前图中的Activity 的dispatchTouchEvent 有误(图已修复),只有return super.dispatchTouchEvent(ev) 才是往下走,返回true 或者 false 事件就被消费了(终止传递)。
总结
当TouchEvent发生时,首先Activity将TouchEvent传递给最顶层的View,TouchEvent最先到达最顶层 view 的 dispatchTouchEvent ,然后由 dispatchTouchEvent 方法进行分发,
- 如果dispatchTouchEvent返回true 消费事件,事件终结。
如果dispatchTouchEvent返回 false ,则回传给父View的onTouchEvent事件处理;
onTouchEvent事件返回true,事件终结,返回false,交给父View的OnTouchEvent方法处理
如果dispatchTouchEvent返回super的话,默认会调用自己的onInterceptTouchEvent方法
默认的情况下interceptTouchEvent回调用super方法,super方法默认返回false,所以会交给子View的onDispatchTouchEvent方法处理
如果 interceptTouchEvent 返回 true ,也就是拦截掉了,则交给它的 onTouchEvent 来处理,
如果 interceptTouchEvent 返回 false ,那么就传递给子 view ,由子 view 的 dispatchTouchEvent 再来开始这个事件的分发。
关于更多详细分析,请查看原博客图解 Android 事件分发机制,真心推荐,写得很好。
解决事件滑动冲突的思路及方法
常见的三种情况
第一种情况,滑动方向不同
第二种情况,滑动方向相同
第三种情况,上述两种情况的嵌套
解决思路
看了上面三种情况,我们知道他们的共同特点是父View 和子View都想争着响应我们的触摸事件,但遗憾的是我们的触摸事件 同一时刻只能被某一个View或者ViewGroup拦截消费,所以就产生了滑动冲突?那既然同一时刻只能由某一个View或者ViewGroup消费拦截,那我们就只需要 决定在某个时刻由这个View或者ViewGroup拦截事件,另外的 某个时刻由 另外一个View或者ViewGroup拦截事件不就OK了吗?综上,正如 在《Android开发艺术》 一书提出的,总共 有两种解决方案
以下解决思路来自于 《Android开发艺术》 书籍
下面的两种方法针对第一种情况(滑动方向不同),父View是上下滑动,子View是左右滑动的情况。
外部解决法
从父View着手,重写onInterceptTouchEvent方法,在父View需要拦截的时候拦截,不要的时候返回false,为代码大概 如下
<code class="hljs java has-numbering"><span class="hljs-annotation">@Override</span><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">onInterceptTouchEvent</span>(MotionEvent ev) { <span class="hljs-keyword">final</span> <span class="hljs-keyword">float</span> x = ev.getX(); <span class="hljs-keyword">final</span> <span class="hljs-keyword">float</span> y = ev.getY(); <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> action = ev.getAction(); <span class="hljs-keyword">switch</span> (action) { <span class="hljs-keyword">case</span> MotionEvent.ACTION_DOWN: mDownPosX = x; mDownPosY = y; <span class="hljs-keyword">break</span>; <span class="hljs-keyword">case</span> MotionEvent.ACTION_MOVE: <span class="hljs-keyword">final</span> <span class="hljs-keyword">float</span> deltaX = Math.abs(x - mDownPosX); <span class="hljs-keyword">final</span> <span class="hljs-keyword">float</span> deltaY = Math.abs(y - mDownPosY); <span class="hljs-comment">// 这里是够拦截的判断依据是左右滑动,读者可根据自己的逻辑进行是否拦截</span> <span class="hljs-keyword">if</span> (deltaX > deltaY) { <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>; } } <span class="hljs-keyword">return</span> <span class="hljs-keyword">super</span>.onInterceptTouchEvent(ev);}</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li></ul><div class="save_code tracking-ad" data-mod="popu_249"><a target=_blank target="_blank"><img src="http://static.blog.csdn.net/images/save_snippets.png" alt="" /></a></div><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li></ul>
内部解决法
从子View着手,父View先不要拦截任何事件,所有的 事件传递给 子View,如果子View需要此事件就消费掉,不需要此事件的话就交给 父View处理。
实现思路 如下,重写子 View的dispatchTouchEvent方法,在Action_down 动作中通过方法 requestDisallowInterceptTouchEvent(true) 先请求 父 View不要拦截事件,这样保证 子View能够 接受到Action_move事件,再在Action_move动作中根据 自己的逻辑是否要拦截事件,不要的 话再交给 父View处理
<code class="language-java hljs has-numbering"><span class="hljs-annotation">@Override</span><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">dispatchTouchEvent</span>(MotionEvent ev) { <span class="hljs-keyword">int</span> x = (<span class="hljs-keyword">int</span>) ev.getRawX(); <span class="hljs-keyword">int</span> y = (<span class="hljs-keyword">int</span>) ev.getRawY(); <span class="hljs-keyword">int</span> dealtX = <span class="hljs-number">0</span>; <span class="hljs-keyword">int</span> dealtY = <span class="hljs-number">0</span>; <span class="hljs-keyword">switch</span> (ev.getAction()) { <span class="hljs-keyword">case</span> MotionEvent.ACTION_DOWN: dealtX = <span class="hljs-number">0</span>; dealtY = <span class="hljs-number">0</span>; <span class="hljs-comment">// 保证子View能够接收到Action_move事件</span> getParent().requestDisallowInterceptTouchEvent(<span class="hljs-keyword">true</span>); <span class="hljs-keyword">break</span>; <span class="hljs-keyword">case</span> MotionEvent.ACTION_MOVE: dealtX += Math.abs(x - lastX); dealtY += Math.abs(y - lastY); Log.i(TAG, <span class="hljs-string">"dealtX:="</span> + dealtX); Log.i(TAG, <span class="hljs-string">"dealtY:="</span> + dealtY); <span class="hljs-comment">// 这里是够拦截的判断依据是左右滑动,读者可根据自己的逻辑进行是否拦截</span> <span class="hljs-keyword">if</span> (dealtX >= dealtY) { getParent().requestDisallowInterceptTouchEvent(<span class="hljs-keyword">true</span>); } <span class="hljs-keyword">else</span> { getParent().requestDisallowInterceptTouchEvent(<span class="hljs-keyword">false</span>); } lastX = x; lastY = y; <span class="hljs-keyword">break</span>; <span class="hljs-keyword">case</span> MotionEvent.ACTION_CANCEL: <span class="hljs-keyword">break</span>; <span class="hljs-keyword">case</span> MotionEvent.ACTION_UP: <span class="hljs-keyword">break</span>; } <span class="hljs-keyword">return</span> <span class="hljs-keyword">super</span>.dispatchTouchEvent(ev);}</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li></ul><div class="save_code tracking-ad" data-mod="popu_249"><a target=_blank target="_blank"><img src="http://static.blog.csdn.net/images/save_snippets.png" alt="" /></a></div><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li></ul>
ScrollView 里面嵌套ViewPager导致的滑动冲突
外部解决法
如上面所述,从 父View ScrollView着手,重写 OnInterceptTouchEvent方法,在上下滑动的时候拦截事件,在左右滑动的时候不拦截事件,返回 false,这样确保子View 的dispatchTouchEvent方法会被调用,代码 如下
<code class="hljs java has-numbering"><span class="hljs-javadoc">/** * @ explain:这个ScrlloView不拦截水平滑动事件, * 是用来解决 ScrollView里面嵌套ViewPager使用的 * @ author:xujun on 2016/10/25 15:28 * @ email:gdutxiaoxu@163.com */</span><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">VerticalScrollView</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ScrollView</span> {</span> <span class="hljs-keyword">public</span> <span class="hljs-title">VerticalScrollView</span>(Context context) { <span class="hljs-keyword">super</span>(context); } <span class="hljs-keyword">public</span> <span class="hljs-title">VerticalScrollView</span>(Context context, AttributeSet attrs) { <span class="hljs-keyword">super</span>(context, attrs); } <span class="hljs-keyword">public</span> <span class="hljs-title">VerticalScrollView</span>(Context context, AttributeSet attrs, <span class="hljs-keyword">int</span> defStyleAttr) { <span class="hljs-keyword">super</span>(context, attrs, defStyleAttr); } <span class="hljs-annotation">@TargetApi</span>(<span class="hljs-number">21</span>) <span class="hljs-keyword">public</span> <span class="hljs-title">VerticalScrollView</span>(Context context, AttributeSet attrs, <span class="hljs-keyword">int</span> defStyleAttr, <span class="hljs-keyword">int</span> defStyleRes) { <span class="hljs-keyword">super</span>(context, attrs, defStyleAttr, defStyleRes); } <span class="hljs-keyword">private</span> <span class="hljs-keyword">float</span> mDownPosX = <span class="hljs-number">0</span>; <span class="hljs-keyword">private</span> <span class="hljs-keyword">float</span> mDownPosY = <span class="hljs-number">0</span>; <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">onInterceptTouchEvent</span>(MotionEvent ev) { <span class="hljs-keyword">final</span> <span class="hljs-keyword">float</span> x = ev.getX(); <span class="hljs-keyword">final</span> <span class="hljs-keyword">float</span> y = ev.getY(); <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> action = ev.getAction(); <span class="hljs-keyword">switch</span> (action) { <span class="hljs-keyword">case</span> MotionEvent.ACTION_DOWN: mDownPosX = x; mDownPosY = y; <span class="hljs-keyword">break</span>; <span class="hljs-keyword">case</span> MotionEvent.ACTION_MOVE: <span class="hljs-keyword">final</span> <span class="hljs-keyword">float</span> deltaX = Math.abs(x - mDownPosX); <span class="hljs-keyword">final</span> <span class="hljs-keyword">float</span> deltaY = Math.abs(y - mDownPosY); <span class="hljs-comment">// 这里是够拦截的判断依据是左右滑动,读者可根据自己的逻辑进行是否拦截</span> <span class="hljs-keyword">if</span> (deltaX > deltaY) { <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>; } } <span class="hljs-keyword">return</span> <span class="hljs-keyword">super</span>.onInterceptTouchEvent(ev); }}</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li></ul><div class="save_code tracking-ad" data-mod="popu_249"><a target=_blank target="_blank"><img src="http://static.blog.csdn.net/images/save_snippets.png" alt="" /></a></div><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li></ul>
内部解决法
如上面上述,通过requestDisallowInterceptTouchEvent(true)方法来影响父View是否拦截事件,我们通过重写ViewPager的 dispatchTouchEvent()方法,在左右滑动的时候请求父View ScrollView不要拦截事件,其他的时候由子View 拦截事件
<code class="language-java hljs has-numbering"><span class="hljs-javadoc">/** * @ explain:这个 ViewPager是用来解决ScrollView里面嵌套ViewPager的 内部解决法的 * @ author:xujun on 2016/10/25 16:38 * @ email:gdutxiaoxu@163.com */</span><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyViewPager</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ViewPager</span> {</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> String TAG = <span class="hljs-string">"xujun"</span>; <span class="hljs-keyword">int</span> lastX = -<span class="hljs-number">1</span>; <span class="hljs-keyword">int</span> lastY = -<span class="hljs-number">1</span>; <span class="hljs-keyword">public</span> <span class="hljs-title">MyViewPager</span>(Context context) { <span class="hljs-keyword">super</span>(context); } <span class="hljs-keyword">public</span> <span class="hljs-title">MyViewPager</span>(Context context, AttributeSet attrs) { <span class="hljs-keyword">super</span>(context, attrs); } <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">dispatchTouchEvent</span>(MotionEvent ev) { <span class="hljs-keyword">int</span> x = (<span class="hljs-keyword">int</span>) ev.getRawX(); <span class="hljs-keyword">int</span> y = (<span class="hljs-keyword">int</span>) ev.getRawY(); <span class="hljs-keyword">int</span> dealtX = <span class="hljs-number">0</span>; <span class="hljs-keyword">int</span> dealtY = <span class="hljs-number">0</span>; <span class="hljs-keyword">switch</span> (ev.getAction()) { <span class="hljs-keyword">case</span> MotionEvent.ACTION_DOWN: dealtX = <span class="hljs-number">0</span>; dealtY = <span class="hljs-number">0</span>; <span class="hljs-comment">// 保证子View能够接收到Action_move事件</span> getParent().requestDisallowInterceptTouchEvent(<span class="hljs-keyword">true</span>); <span class="hljs-keyword">break</span>; <span class="hljs-keyword">case</span> MotionEvent.ACTION_MOVE: dealtX += Math.abs(x - lastX); dealtY += Math.abs(y - lastY); Log.i(TAG, <span class="hljs-string">"dealtX:="</span> + dealtX); Log.i(TAG, <span class="hljs-string">"dealtY:="</span> + dealtY); <span class="hljs-comment">// 这里是够拦截的判断依据是左右滑动,读者可根据自己的逻辑进行是否拦截</span> <span class="hljs-keyword">if</span> (dealtX >= dealtY) { getParent().requestDisallowInterceptTouchEvent(<span class="hljs-keyword">true</span>); } <span class="hljs-keyword">else</span> { getParent().requestDisallowInterceptTouchEvent(<span class="hljs-keyword">false</span>); } lastX = x; lastY = y; <span class="hljs-keyword">break</span>; <span class="hljs-keyword">case</span> MotionEvent.ACTION_CANCEL: <span class="hljs-keyword">break</span>; <span class="hljs-keyword">case</span> MotionEvent.ACTION_UP: <span class="hljs-keyword">break</span>; } <span class="hljs-keyword">return</span> <span class="hljs-keyword">super</span>.dispatchTouchEvent(ev); }}</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li><li>54</li><li>55</li><li>56</li><li>57</li><li>58</li></ul><div class="save_code tracking-ad" data-mod="popu_249"><a target=_blank target="_blank"><img src="http://static.blog.csdn.net/images/save_snippets.png" alt="" /></a></div><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li><li>54</li><li>55</li><li>56</li><li>57</li><li>58</li></ul>
注意事项(坑)
当我们ScrollView的最上层的Layout里面多多个孩子的时候,当下面一个孩子是RecyclerView或者ListView的时候,往往会自动滑动到ListView或者RecyclerView 的第一个item,导致进入界面的时候会导致RecyclerView 上面的 View被滑动到界面之外,看不见,这时候的用户体验是比较差的
即结构如下面的时候
在Activity中的相关解决方法
于是我查找了相关的资料,在Activity中完美解决,主要要一下两种方法
第一种方法,重写Activity的onWindowFocusChanged()方法,在里面调用mNoHorizontalScrollView.scrollTo(0,0);方法,滑动到顶部,因为onWindowFocusChanged是在所有View绘制完毕的时候才会回调的,不熟悉的话建议先回去看一下Activity的生命周期的相关介绍
<code class="hljs java has-numbering"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">scroll</span>() { mNoHorizontalScrollView.scrollTo(<span class="hljs-number">0</span>,<span class="hljs-number">0</span>);}<span class="hljs-annotation">@Override</span><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onWindowFocusChanged</span>(<span class="hljs-keyword">boolean</span> hasFocus) { <span class="hljs-keyword">super</span>.onWindowFocusChanged(hasFocus); <span class="hljs-keyword">if</span>(hasFocus && first){ first=<span class="hljs-keyword">false</span>; scroll(); }}</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li></ul><div class="save_code tracking-ad" data-mod="popu_249"><a target=_blank target="_blank"><img src="http://static.blog.csdn.net/images/save_snippets.png" alt="" /></a></div><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li></ul>
第二种解决方法,调用RecyclerView上面的View的一下方法,让其获取焦点
<code class="hljs avrasm has-numbering">view<span class="hljs-preprocessor">.setFocusable</span>(true)<span class="hljs-comment">; </span>view<span class="hljs-preprocessor">.setFocusableInTouchMode</span>(true)<span class="hljs-comment">; </span>view<span class="hljs-preprocessor">.requestFocus</span>()<span class="hljs-comment">;</span></code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li></ul><div class="save_code tracking-ad" data-mod="popu_249"><a target=_blank target="_blank"><img src="http://static.blog.csdn.net/images/save_snippets.png" alt="" /></a></div><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li></ul>
这段代码在初始化的时候就让该界面的顶部的某一个控件获得焦点,滚动条自然就显示到顶部了。
在Fragment中的相关解决方法
同样是调用第二种方法,调用RecyclerView上面的View的一下方法,让其获取焦点
<code class="hljs avrasm has-numbering">view<span class="hljs-preprocessor">.setFocusable</span>(true)<span class="hljs-comment">; </span>view<span class="hljs-preprocessor">.setFocusableInTouchMode</span>(true)<span class="hljs-comment">; </span>view<span class="hljs-preprocessor">.requestFocus</span>()<span class="hljs-comment">;</span></code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li></ul><div class="save_code tracking-ad" style="display: none;" data-mod="popu_249"><a target=_blank target="_blank"><img src="http://static.blog.csdn.net/images/save_snippets_01.png" alt="" /></a></div><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li></ul>
这段代码在初始化的时候就让该界面的顶部的某一个控件获得焦点,滚动条自然就显示到顶部了。但是该方法存在缺点,就是当我们上面的view如果滑动到一半的时候,切换到下一个Fragment,在切换回来的时候,RecyclerView的第一个item会自动滑动到顶部。目前我还没有找到相对比较好的解决这个问题的方法,大家知道相关解决方法的话也欢迎联系我,可以加我微信或者在留言区评论,谢谢
个人疑点
借鉴于解决Activity的方法,目前我还没有找到一个方法是在Fragemnt界面完全绘制完毕以后回调的方法,如果大家知道怎样处理的 话,欢迎大家提出来
ViewPager里面嵌套ViewPager导致的滑动冲突
内部解决法
从子View ViewPager着手,重写 子View的 dispatchTouchEvent方法,在子 View需要拦截的时候进行拦截,否则交给父View处理,代码如下
<code class="language-java hljs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ChildViewPager</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ViewPager</span> {</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> String TAG = <span class="hljs-string">"xujun"</span>; <span class="hljs-keyword">public</span> <span class="hljs-title">ChildViewPager</span>(Context context) { <span class="hljs-keyword">super</span>(context); } <span class="hljs-keyword">public</span> <span class="hljs-title">ChildViewPager</span>(Context context, AttributeSet attrs) { <span class="hljs-keyword">super</span>(context, attrs); } <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">dispatchTouchEvent</span>(MotionEvent ev) { <span class="hljs-keyword">int</span> curPosition; <span class="hljs-keyword">switch</span> (ev.getAction()) { <span class="hljs-keyword">case</span> MotionEvent.ACTION_DOWN: getParent().requestDisallowInterceptTouchEvent(<span class="hljs-keyword">true</span>); <span class="hljs-keyword">break</span>; <span class="hljs-keyword">case</span> MotionEvent.ACTION_MOVE: curPosition = <span class="hljs-keyword">this</span>.getCurrentItem(); <span class="hljs-keyword">int</span> count = <span class="hljs-keyword">this</span>.getAdapter().getCount(); Log.i(TAG, <span class="hljs-string">"curPosition:="</span> +curPosition); <span class="hljs-comment">// 当当前页面在最后一页和第0页的时候,由父亲拦截触摸事件</span> <span class="hljs-keyword">if</span> (curPosition == count - <span class="hljs-number">1</span>|| curPosition==<span class="hljs-number">0</span>) { getParent().requestDisallowInterceptTouchEvent(<span class="hljs-keyword">false</span>); } <span class="hljs-keyword">else</span> {<span class="hljs-comment">//其他情况,由孩子拦截触摸事件</span> getParent().requestDisallowInterceptTouchEvent(<span class="hljs-keyword">true</span>); } } <span class="hljs-keyword">return</span> <span class="hljs-keyword">super</span>.dispatchTouchEvent(ev); }}</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li></ul><div class="save_code tracking-ad" data-mod="popu_249"><a target=_blank target="_blank"><img src="http://static.blog.csdn.net/images/save_snippets.png" alt="" /></a></div><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li></ul>
外部解决法
这个如果要采用外部解决法来解决的话想,相对很麻烦,我提一下自己的个人思路,我们可以先测量子View在哪个区域,然后我们在根据我们按下的点是否在区域以内,如果是的话,在根据子View时候需要拦截进行处理
讨论
对于这种效果,上面是轮播图的,下面是RecyclerView或者ListView的,一般有一下几种实现方式
- 使用我们上述提高的ScrollView里面嵌套ViewPager和RecyclerView,这种实现方式需要自己解决View滑动事件的冲突,同时还有我在上述提高的在Fragment中存在的问题
- 使用listView的addHeaderView来实现,或者是通过多种不同的item来实现
- 使用RecyclerView添加headerView来实现,或者复用多种不同的item来实现。关于RecyclerView如何添加headerView可以参考鸿洋大神的这一篇博客Android 优雅的为RecyclerView添加HeaderView和FooterView
- 使用SupportLibrary中的CoordinatorLayout等控件
其布局文件如下,Activity代码见项目中的SixActivity
<code class="language-java hljs has-numbering"><?xml version=<span class="hljs-string">"1.0"</span> encoding=<span class="hljs-string">"utf-8"</span>?><android.support.design.widget.CoordinatorLayout xmlns:android=<span class="hljs-string">"http://schemas.android.com/apk/res/android"</span> xmlns:app=<span class="hljs-string">"http://schemas.android.com/apk/res-auto"</span> android:layout_width=<span class="hljs-string">"match_parent"</span> android:layout_height=<span class="hljs-string">"match_parent"</span> android:background=<span class="hljs-string">"@android:color/background_light"</span> android:fitsSystemWindows=<span class="hljs-string">"true"</span>> <android.support.design.widget.AppBarLayout android:layout_width=<span class="hljs-string">"match_parent"</span> android:layout_height=<span class="hljs-string">"300dp"</span> android:fitsSystemWindows=<span class="hljs-string">"true"</span> android:theme=<span class="hljs-string">"@style/ThemeOverlay.AppCompat.Dark.ActionBar"</span> > <android.support.design.widget.CollapsingToolbarLayout android:layout_width=<span class="hljs-string">"match_parent"</span> android:layout_height=<span class="hljs-string">"match_parent"</span> app:layout_scrollFlags=<span class="hljs-string">"scroll|snap"</span>> <android.support.v4.view.ViewPager android:id=<span class="hljs-string">"@+id/viewPager"</span> android:layout_width=<span class="hljs-string">"match_parent"</span> android:layout_height=<span class="hljs-string">"match_parent"</span> > </android.support.v4.view.ViewPager> <TextView android:id=<span class="hljs-string">"@+id/tv_page"</span> android:layout_width=<span class="hljs-string">"match_parent"</span> android:layout_height=<span class="hljs-string">"wrap_content"</span> android:layout_gravity=<span class="hljs-string">"bottom"</span> android:gravity=<span class="hljs-string">"right"</span> android:text=<span class="hljs-string">"1/10"</span> android:textColor=<span class="hljs-string">"#000"</span>/> </android.support.design.widget.CollapsingToolbarLayout> </android.support.design.widget.AppBarLayout> <android.support.v7.widget.RecyclerView android:id=<span class="hljs-string">"@+id/recyclerView"</span> android:layout_width=<span class="hljs-string">"match_parent"</span> android:layout_height=<span class="hljs-string">"match_parent"</span> app:layout_behavior=<span class="hljs-string">"@string/appbar_scrolling_view_behavior"</span>> </android.support.v7.widget.RecyclerView></android.support.design.widget.CoordinatorLayout></code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li><li>54</li><li>55</li><li>56</li><li>57</li><li>58</li><li>59</li><li>60</li></ul><div class="save_code tracking-ad" data-mod="popu_249"><a target=_blank target="_blank"><img src="http://static.blog.csdn.net/images/save_snippets.png" alt="" /></a></div><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li><li>54</li><li>55</li><li>56</li><li>57</li><li>58</li><li>59</li><li>60</li></ul>
关于CoordinatorLayout的更多用法,可以参考我的这一篇博客使用CoordinatorLayout打造各种炫酷的效果
总结
- 当我们滑动方向不同的时候,采用外部解决法和内部解决法,复杂度差不多。
- 当我们滑动的方向相同的话,建议采用内部解决法来解决,因为采用外部解决法复杂度比较高。而且有时候我们是采用别人的开源控件,这时候去修改别人的源码可能会发生一些意想不到的bug。
题外话
- 在这篇博客的最后提高的实现轮播图+list列表的几种实现形式,刚开始是不想写的,后面因为ScrollView里面嵌套ViewPager和RecyclerView在fragment中RecyclerView抢占焦点,在某些情况下用户体验不好,才写出来的,跟这篇博客要讲解的View滑动事件冲突没有多大关系,只是给读者提供多种思路而已
- 至于CoordinatorLayout,是google IO 2015中提出来的,功能很强大,可以说是专门为了解决嵌套导滑动而产生的,极大地方便了开发者,对于初学者,可以暂时不必掌握它,先把其他的基础学好就好
- 同时卖一下广告,欢迎大家到我的github上面star或者fork,谢谢
参考文章:图解 Android 事件分发机制
文章首发地址CSDN:http://blog.csdn.Net/gdutxiaoxu/article/details/52939127
源码下载地址:https://github.com/gdutxiaoxu/TouchDemo.Git
- ViewPager,ScrollView 嵌套ViewPager滑动冲突解决
- ViewPager,ScrollView 嵌套ViewPager滑动冲突解决
- ViewPager,ScrollView 嵌套ViewPager滑动冲突解决
- ViewPager,ScrollView 嵌套ViewPager滑动冲突解决
- ViewPager,ScrollView 嵌套ViewPager滑动冲突解决
- ViewPager,ScrollView 嵌套ViewPager滑动冲突解决
- ViewPager,ScrollView 嵌套ViewPager滑动冲突解决
- ViewPager,ScrollView 嵌套ViewPager滑动冲突解决
- ViewPager,ScrollView 嵌套ViewPager滑动冲突解决
- ViewPager,ScrollView嵌套ViewPager滑动冲突解决
- ScrollView嵌套ViewPager滑动冲突的解决
- 解决ScrollView嵌套viewpager滑动冲突
- Android中Viewpager,ScrollView嵌套ViewPager滑动冲突解决
- scrollview嵌套viewpager 滑动冲突
- 解决ScrollView嵌套ViewPager出现的滑动冲突问题
- 解决ScrollView嵌套ViewPager出现的滑动冲突问题
- 解决ViewPager嵌套,滑动冲突
- 解决ViewPager嵌套ViewPager滑动冲突问题
- 神经网络训练时精度问题
- MFC中给程序添加图标
- Linux C——数据库的基本知识
- mysql更改用户名和密码及mysql常用命令
- OpenCV实现朴素贝叶斯分类器诊断病情
- ViewPager,ScrollView 嵌套ViewPager滑动冲突解决
- XML选择器的使用
- 优化sql语句
- 4.编写一个C函数,将”I am from shanghai ”倒置为”shanghai from am I”,及将句子中的单词位置倒置,而不改变单词内部结构.
- atitit 点播系统 概览 v2 qb1.docx
- java的super关键字
- 搭建git for windows服务器(100%可以成功)
- Json?OR FlatBuffers?FlatBuffers
- maven 仓库推荐