Android 滑动冲突以及如何解决

今天针对一个滑动冲突的其中一类----内外两层的滑动方向一致。

遇到的问题是:
外层是一个自定义LinearLayout,我在它上面重写了onToucheEvent,设置了滑动事件,让它内部的子View能够在手指下滑的时候,组件跟着下移,手指松开,组件回到原来位置。
内层是一个ScrollView,它本身就可滑动。
那么当我手指在屏幕上滑动的时候,系统并不明确我想让哪一层滑动,可能系统给出的结果就不是我预期的。所以,这里我们必须写程序去控制这个滑动效果。

 

我们想要达到的效果是:
当我下滑:
在ScrollView滑动最上面的时候(即 ScrollView的顶端已经显示出来),此时如果我下滑,那么就触发外层自定义LinearLayout的滑动事件。
在ScrollView并不是滑动到最上面的时候,此时如果我下滑,就触发scrollView本身的scroll效果。

当我上滑:
无论ScrollView处于何种情况,都不要触发外层自定义LinearLayout的滑动事件。

 

最终效果如下图:

这是刚刚加载的样子:

  

 

这是scrollView处于顶端但是向下滑动的时候:触发了外层layout的下拉事件,松开手则会回弹

 

这是ScrollView不处于顶端而向下滑的时候:并不会触发外层的下拉事件,因为在自定义ScrollView中,重写了onTouchEvent,定义了外层getParent何时可以拦截,而何时不能拦截。(scroll到顶的时候可以拦截下拉事件,没到顶的时候则不能拦截下拉事件)


-----------------------------------------------------------------------
解决此问题的前提是 :熟练掌握 事件分发机制的原理。
解决此问题的方法步骤为:
1)外部拦截:在自定义LinearLayout中,重写onInterceptTouchEvent,拦截所有下滑的事件,释放所有下滑的事件。
2)内部拦截:在子组件,也就是自定义ScrollView中,重写OnTouchEvent,判定是不是当前scroll到了顶部,如果是到了顶部,那么就允许父组件进行拦截。如果是滑到中间位置,不是顶部,就不允许父组件进行拦截,事件就会在子组件这里消耗掉,父组件就不会执行onTouchEvent.

最终就达成了上面说的预期效果;

 

全部代码如下:

MyParentView.java 即 外围的自定义LinearLayout,由它进行外部拦截

 1 import android.content.Context;
 2 import android.util.AttributeSet;
 3 import android.util.Log;
 4 import android.view.MotionEvent;
 5 import android.widget.LinearLayout;
 6 
 7 public class MyParentView extends LinearLayout {
 8 
 9     private int mMove;
10     private int yDown, yMove;
11     private int i = 0;
12 
13     public MyParentView(Context context, AttributeSet attrs) {
14         super(context, attrs);
15     }
16 
17     @Override
18     public boolean onInterceptTouchEvent(MotionEvent ev) {
19         int y = (int) ev.getY();
20         boolean isIntercept = false;// 默认不拦截,这个变量只能放在方法内部作为局部变量,因为如果作为全局变量的话,子组件内部有可能划不动;至于是啥原因,我还没想明白
21         switch (ev.getAction()) {
22             case MotionEvent.ACTION_DOWN:
23                 yDown = y;
24                 break;
25             case MotionEvent.ACTION_MOVE:
26                 yMove = y;
27                 if (yMove - yDown < 0) {// 上滑动作直接放行
28                     isIntercept = false;
29                 } else if (yMove - yDown > 0) { // 下滑动作拦截住,不往下发
30                     isIntercept = true;
31                 }
32                 break;
33             case MotionEvent.ACTION_UP:
34                 break;
35         }
36         Log.d("onInterceptTouchEvent", "isIntercept:" + isIntercept);
37 
38         return isIntercept;
39     }
40 
41     /**
42      * 重写onTouchEvent获取屏幕事件
43      *
44      * @param event
45      * @return
46      */
47     @Override
48     public boolean onTouchEvent(MotionEvent event) {
49         int y = (int) event.getY();// 取得Y轴坐标值
50         switch (event.getAction()) {
51             case MotionEvent.ACTION_DOWN:
52                 yDown = y;
53                 break;
54             case MotionEvent.ACTION_MOVE:
55                 yMove = y;
56                 if ((yMove - yDown) > 0) {// 如果是向下拉,因为向下拉的话,yMove总是比yDown要大
57                     mMove = yMove - yDown;// 计算出拖动的距离
58                     i += mMove;//记录一共拖动了多长距离,累加的
59                     layout(getLeft(), getTop() + mMove, getRight(), getBottom() + mMove);// 调用layout进行重新布局,只是改变布局的相对于父组件的位置
60                 }
61                 break;
62             case MotionEvent.ACTION_UP:
63                 layout(getLeft(), getTop() - i, getRight(), getBottom() - i);
64                 i = 0;
65                 break;
66         }
67         return true;
68     }
69 }

MyScrollView.java 内层的自定义ScrollView,由它进行内部拦截

 1 import android.content.Context;
 2 import android.util.AttributeSet;
 3 import android.util.Log;
 4 import android.view.MotionEvent;
 5 import android.widget.ScrollView;
 6 
 7 public class MyScrollView extends ScrollView {
 8 
 9 
10     public MyScrollView(Context context) {
11         this(context, null);
12     }
13 
14     public MyScrollView(Context context, AttributeSet attrs) {
15         this(context, attrs, 0);
16     }
17 
18     public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
19         super(context, attrs, defStyleAttr);
20     }
21 
22     @Override
23     public boolean onTouchEvent(MotionEvent ev) {
24         Log.d("onInterceptTouchEvent", "MyScrollView:onTouchEvent");
25         switch (ev.getAction()) {
26             case MotionEvent.ACTION_MOVE:
27                 int scrollY = getScrollY();//纵向滑动的顶端Y轴坐标值
28                 if (scrollY == 0) {//如果已经scroll到了顶端
29                     //允许父View进行事件拦截
30                     getParent().requestDisallowInterceptTouchEvent(false);//是否禁止父组件拦截事件. false表示不禁止,也就是允许
31                 } else {
32                     //禁止父View进行事件拦截
33                     getParent().requestDisallowInterceptTouchEvent(true);//true表示禁止,不允许
34                 }
35                 break;
36         }
37         return super.onTouchEvent(ev);
38 
39     }
40 }

 

布局文件 activity_scroll_conflict.xml

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <com.example.administrator.technologystackapp.activities.custom.scroll_confict.MyParentView xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:id="@+id/parent_view"
 4     android:layout_width="match_parent"
 5     android:layout_height="match_parent"
 6     android:orientation="vertical">
 7 
 8     <com.example.administrator.technologystackapp.activities.custom.scroll_confict.MyScrollView
 9         android:layout_width="match_parent"
10         android:layout_height="match_parent">
11 
12         <LinearLayout
13             android:layout_width="match_parent"
14             android:layout_height="match_parent"
15             android:gravity="center"
16             android:orientation="vertical"
17             android:padding="10dp">
18 
19             <TextView
20                 android:layout_width="match_parent"
21                 android:layout_height="100dp"
22                 android:layout_margin="5dp"
23                 android:background="@drawable/textview_background2"
24                 android:gravity="center"
25                 android:text="content1" />
26 
27             <TextView
28                 android:layout_width="match_parent"
29                 android:layout_height="100dp"
30                 android:layout_margin="5dp"
31                 android:background="@drawable/textview_background2"
32                 android:gravity="center"
33                 android:text="content2" />
34 
35             <TextView
36                 android:layout_width="match_parent"
37                 android:layout_height="100dp"
38                 android:layout_margin="5dp"
39                 android:background="@drawable/textview_background2"
40                 android:gravity="center"
41                 android:text="content3" />
42 
43             <TextView
44                 android:layout_width="match_parent"
45                 android:layout_height="100dp"
46                 android:layout_margin="5dp"
47                 android:background="@drawable/textview_background2"
48                 android:gravity="center"
49                 android:text="content4" />
50 
51             <TextView
52                 android:layout_width="match_parent"
53                 android:layout_height="100dp"
54                 android:layout_margin="5dp"
55                 android:background="@drawable/textview_background2"
56                 android:gravity="center"
57                 android:text="content5" />
58 
59             <TextView
60                 android:layout_width="match_parent"
61                 android:layout_height="100dp"
62                 android:layout_margin="5dp"
63                 android:background="@drawable/textview_background2"
64                 android:gravity="center"
65                 android:text="content6" />
66 
67         </LinearLayout>
68 
69     </com.example.administrator.technologystackapp.activities.custom.scroll_confict.MyScrollView>
70 
71 </com.example.administrator.technologystackapp.activities.custom.scroll_confict.MyParentView>

最后是Activity:

 1 import android.os.Bundle;
 2 
 3 import com.example.administrator.technologystackapp.R;
 4 import com.example.administrator.technologystackapp.activities.activity.manager.BaseActivity;
 5 
 6 public class ActivityScrollConflict extends BaseActivity {
 7 
 8     @Override
 9     protected void onCreate(Bundle savedInstanceState) {
10         super.onCreate(savedInstanceState);
11         setContentView(R.layout.activity_scroll_conflict);
12     }
13 }

 

 一个辅助的drawable

textview_background2.xml:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <shape xmlns:android="http://schemas.android.com/apk/res/android">
 3     <!-- 实心 -->
 4     <solid android:color="@android:color/holo_blue_light" />
 5     <!-- 边框 -->
 6     <stroke
 7         android:width="2dp"
 8         android:color="@android:color/holo_blue_dark" />
 9     <!-- 圆角 -->
10     <corners android:radius="3dp" />
11     <!-- 边距 -->
12     <padding
13         android:bottom="5dp"
14         android:left="5dp"
15         android:right="5dp"
16         android:top="5dp" />
17 </shape>

 

 

 

 

 

 

 

 

原文地址:https://www.cnblogs.com/hankzhouAndroid/p/9005977.html