今天针对一个滑动冲突的其中一类----内外两层的滑动方向一致。
遇到的问题是:
外层是一个自定义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>