1. 内部拦截法:

父容器不拦截事件,所有的事件全部都传递给子元素,如果子元素需要此事件就直接消耗掉,否则就交给父容器进行处理。

这种方法和Android中的事件分发机制不一样,需要配合requestDisallowInterceptTouchEvent方法才能正常工作,使用起来较外部拦截法稍显负责一点。

我们需要重写子元素的dispatchTouchEvent方法。

这种方法的伪代码是:

 1  @Override
 2     public boolean dispatchTouchEvent(MotionEvent event) {
 3         int x = (int) event.getX();
 4         int y = (int) event.getY();
 5
 6         switch (event.getAction()) {
 7         case MotionEvent.ACTION_DOWN: {
 8             parent.requestDisallowInterceptTouchEvent(true);
 9             break;
10         }
11         case MotionEvent.ACTION_MOVE: {
12             int deltaX = x - mLastX;
13             int deltaY = y - mLastY;
14             if (父容器需要此类点击事件) {
15                 parent.requestDisallowInterceptTouchEvent(false);
16             }
17             break;
18         }
19         case MotionEvent.ACTION_UP: {
20             break;
21         }
22         default:
23             break;
24         }
25
26         mLastX = x;
27         mLastY = y;
28         return super.dispatchTouchEvent(event);
29     }

上面重写的子元素的dispatchTouchEvent方法,这里同时需要重写父容器的onInterceptTouchEvent方法,为什么呢?

那是因为ACTION_DOWN事件并不受FLAG_DISALLOW_INTERCEPT这个标记位的控制,所以一旦父容器拦截ACTION_DOWN事件,那么所有的事件都无法传递到子元素之中,这样内部拦截法就无法起作用了。

父容器所做的修改如下:

 1 @Override
 2     public boolean onInterceptTouchEvent(MotionEvent event) {
 3
 4         int action = event.getAction();
 5         if (action == MotionEvent.ACTION_DOWN) {
 6             return false;
 7         } else {
 8             return true;
 9         }
10     }

2. 下面通过一个Demo示例说明:

(1)首先我们创建一个Android工程,如下:

(2)首先我们来到主布局activity_main.xml,如下:

 1 <com.himi.viewconflict1.ui.RevealLayout
 2     xmlns:android="http://schemas.android.com/apk/res/android"
 3     xmlns:tools="http://schemas.android.com/tools"
 4     android:layout_width="match_parent"
 5     android:layout_height="match_parent"
 6     android:orientation="vertical"
 7     android:padding="12dp"
 8     tools:context="${relativePackage}.${activityClass}" >
 9
10     <Button
11         android:id="@+id/button1"
12         style="@style/AppTheme.Button.Green"
13         android:onClick="onButtonClick"
14         android:text="滑动冲突场景1-内部拦截" />
15
16 </com.himi.viewconflict1.ui.RevealLayout>

(3)接下来来到MainActivity,如下:

 1 package com.himi.viewconflict;
 2
 3 import android.app.Activity;
 4 import android.content.Intent;
 5 import android.os.Bundle;
 6 import android.view.View;
 7
 8 public class MainActivity extends Activity {
 9
10     @Override
11     protected void onCreate(Bundle savedInstanceState) {
12         super.onCreate(savedInstanceState);
13         setContentView(R.layout.activity_main);
14     }
15
16
17
18     public void onButtonClick(View view) {
19          Intent intent = new Intent(this, DemoActivity_1.class);
20          startActivity(intent);
21     }
22 }

(4)上面很自然地跳转到DemoActivity_2之中,如下:

 1 package com.himi.viewconflict1;
 2
 3 import java.util.ArrayList;
 4
 5 import com.himi.viewconflict1.ui.HorizontalScrollViewEx2;
 6 import com.himi.viewconflict1.ui.ListViewEx;
 7 import com.himi.viewconflict1.utils.MyUtils;
 8
 9 import android.app.Activity;
10 import android.graphics.Color;
11 import android.os.Bundle;
12 import android.util.Log;
13 import android.view.LayoutInflater;
14 import android.view.MotionEvent;
15 import android.view.View;
16 import android.view.ViewGroup;
17 import android.widget.AdapterView;
18 import android.widget.AdapterView.OnItemClickListener;
19 import android.widget.ArrayAdapter;
20 import android.widget.TextView;
21 import android.widget.Toast;
22
23 public class DemoActivity_2 extends Activity {
24     private static final String TAG = "DemoActivity_2";
25
26     private HorizontalScrollViewEx2 mListContainer;
27
28     @Override
29     protected void onCreate(Bundle savedInstanceState) {
30         super.onCreate(savedInstanceState);
31         setContentView(R.layout.demo_2);
32         Log.d(TAG, "onCreate");
33         initView();
34     }
35
36     private void initView() {
37         LayoutInflater inflater = getLayoutInflater();
38         mListContainer = (HorizontalScrollViewEx2) findViewById(R.id.container);
39         final int screenWidth = MyUtils.getScreenMetrics(this).widthPixels;
40         final int screenHeight = MyUtils.getScreenMetrics(this).heightPixels;
41         for (int i = 0; i < 3; i++) {
42             ViewGroup layout = (ViewGroup) inflater.inflate(
43                     R.layout.content_layout2, mListContainer, false);
44             layout.getLayoutParams().width = screenWidth;
45             TextView textView = (TextView) layout.findViewById(R.id.title);
46             textView.setText("page " + (i + 1));
47             layout.setBackgroundColor(Color
48                     .rgb(255 / (i + 1), 255 / (i + 1), 0));
49             createList(layout);
50             mListContainer.addView(layout);
51         }
52     }
53
54     private void createList(ViewGroup layout) {
55         ListViewEx listView = (ListViewEx) layout.findViewById(R.id.list);
56         ArrayList<String> datas = new ArrayList<String>();
57         for (int i = 0; i < 50; i++) {
58             datas.add("name " + i);
59         }
60         ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
61                 R.layout.content_list_item, R.id.name, datas);
62         listView.setAdapter(adapter);
63         listView.setHorizontalScrollViewEx2(mListContainer);
64         listView.setOnItemClickListener(new OnItemClickListener() {
65             @Override
66             public void onItemClick(AdapterView<?> parent, View view,
67                     int position, long id) {
68                 Toast.makeText(DemoActivity_2.this, "click item "+position,
69                         Toast.LENGTH_SHORT).show();
70
71             }
72         });
73     }
74
75     @Override
76     public boolean dispatchTouchEvent(MotionEvent ev) {
77         Log.d(TAG, "dispatchTouchEvent action:" + ev.getAction());
78         return super.dispatchTouchEvent(ev);
79     }
80
81     @Override
82     public boolean onTouchEvent(MotionEvent event) {
83         Log.d(TAG, "onTouchEvent action:" + event.getAction());
84         return super.onTouchEvent(event);
85     }
86 }

上面的DemoActivity_2主布局demo_2.xml,如下:

 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     xmlns:tools="http://schemas.android.com/tools"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:background="#ffffff"
 6     android:orientation="vertical" >
 7
 8     <com.himi.viewconflict1.ui.HorizontalScrollViewEx2
 9         android:id="@+id/container"
10         android:layout_width="wrap_content"
11         android:layout_height="match_parent" />
12
13
14 </LinearLayout>

上面使用到HorizontalScrollViewEx2是自定义控件(继承自ViewGroup),如下:

HorizontalScrollViewEx2是父容器,这里需要重写它的onInterceptTouchEvent方法,让父容器不拦截ACTION_DOWN事件。

  1 package com.himi.viewconflict1.ui;
  2
  3 import android.content.Context;
  4 import android.util.AttributeSet;
  5 import android.util.Log;
  6 import android.view.MotionEvent;
  7 import android.view.VelocityTracker;
  8 import android.view.View;
  9 import android.view.ViewGroup;
 10 import android.widget.Scroller;
 11
 12 public class HorizontalScrollViewEx2 extends ViewGroup {
 13     private static final String TAG = "HorizontalScrollViewEx2";
 14
 15     private int mChildrenSize;
 16     private int mChildWidth;
 17     private int mChildIndex;
 18     // 分别记录上次滑动的坐标
 19     private int mLastX = 0;
 20     private int mLastY = 0;
 21
 22     // 分别记录上次滑动的坐标(onInterceptTouchEvent)
 23     private int mLastXIntercept = 0;
 24     private int mLastYIntercept = 0;
 25
 26     private Scroller mScroller;
 27     private VelocityTracker mVelocityTracker;
 28
 29     public HorizontalScrollViewEx2(Context context) {
 30         super(context);
 31         init();
 32     }
 33
 34     public HorizontalScrollViewEx2(Context context, AttributeSet attrs) {
 35         super(context, attrs);
 36         init();
 37     }
 38
 39     public HorizontalScrollViewEx2(Context context, AttributeSet attrs,
 40             int defStyle) {
 41         super(context, attrs, defStyle);
 42         init();
 43     }
 44
 45     private void init() {
 46         mScroller = new Scroller(getContext());
 47         mVelocityTracker = VelocityTracker.obtain();
 48     }
 49
 50     @Override
 51     public boolean onInterceptTouchEvent(MotionEvent event) {
 52         int x = (int) event.getX();
 53         int y = (int) event.getY();
 54         int action = event.getAction();
 55         if (action == MotionEvent.ACTION_DOWN) {
 56             mLastX = x;
 57             mLastY = y;
 58             if (!mScroller.isFinished()) {
 59                 mScroller.abortAnimation();
 60                 return true;
 61             }
 62             return false;
 63         } else {
 64             return true;
 65         }
 66     }
 67
 68     @Override
 69     public boolean onTouchEvent(MotionEvent event) {
 70         Log.d(TAG, "onTouchEvent action:" + event.getAction());
 71         mVelocityTracker.addMovement(event);
 72         int x = (int) event.getX();
 73         int y = (int) event.getY();
 74         switch (event.getAction()) {
 75         case MotionEvent.ACTION_DOWN: {
 76             if (!mScroller.isFinished()) {
 77                 mScroller.abortAnimation();
 78             }
 79             break;
 80         }
 81         case MotionEvent.ACTION_MOVE: {
 82             int deltaX = x - mLastX;
 83             int deltaY = y - mLastY;
 84             Log.d(TAG, "move, deltaX:" + deltaX + " deltaY:" + deltaY);
 85             scrollBy(-deltaX, 0);
 86             break;
 87         }
 88         case MotionEvent.ACTION_UP: {
 89             int scrollX = getScrollX();
 90             int scrollToChildIndex = scrollX / mChildWidth;
 91             Log.d(TAG, "current index:" + scrollToChildIndex);
 92             mVelocityTracker.computeCurrentVelocity(1000);
 93             float xVelocity = mVelocityTracker.getXVelocity();
 94             if (Math.abs(xVelocity) >= 50) {
 95                 mChildIndex = xVelocity > 0 ? mChildIndex - 1 : mChildIndex + 1;
 96             } else {
 97                 mChildIndex = (scrollX + mChildWidth / 2) / mChildWidth;
 98             }
 99             mChildIndex = Math.max(0, Math.min(mChildIndex, mChildrenSize - 1));
100             int dx = mChildIndex * mChildWidth - scrollX;
101             smoothScrollBy(dx, 0);
102             mVelocityTracker.clear();
103             Log.d(TAG, "index:" + scrollToChildIndex + " dx:" + dx);
104             break;
105         }
106         default:
107             break;
108         }
109
110         mLastX = x;
111         mLastY = y;
112         return true;
113     }
114
115     @Override
116     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
117         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
118         int measuredWidth = 0;
119         int measuredHeight = 0;
120         final int childCount = getChildCount();
121         measureChildren(widthMeasureSpec, heightMeasureSpec);
122
123         int widthSpaceSize = MeasureSpec.getSize(widthMeasureSpec);
124         int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
125         int heightSpaceSize = MeasureSpec.getSize(heightMeasureSpec);
126         int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
127         if (childCount == 0) {
128             setMeasuredDimension(0, 0);
129         } else if (heightSpecMode == MeasureSpec.AT_MOST) {
130             final View childView = getChildAt(0);
131             measuredHeight = childView.getMeasuredHeight();
132             setMeasuredDimension(widthSpaceSize, childView.getMeasuredHeight());
133         } else if (widthSpecMode == MeasureSpec.AT_MOST) {
134             final View childView = getChildAt(0);
135             measuredWidth = childView.getMeasuredWidth() * childCount;
136             setMeasuredDimension(measuredWidth, heightSpaceSize);
137         } else {
138             final View childView = getChildAt(0);
139             measuredWidth = childView.getMeasuredWidth() * childCount;
140             measuredHeight = childView.getMeasuredHeight();
141             setMeasuredDimension(measuredWidth, measuredHeight);
142         }
143     }
144
145     @Override
146     protected void onLayout(boolean changed, int l, int t, int r, int b) {
147         Log.d(TAG, "width:" + getWidth());
148         int childLeft = 0;
149         final int childCount = getChildCount();
150         mChildrenSize = childCount;
151
152         for (int i = 0; i < childCount; i++) {
153             final View childView = getChildAt(i);
154             if (childView.getVisibility() != View.GONE) {
155                 final int childWidth = childView.getMeasuredWidth();
156                 mChildWidth = childWidth;
157                 childView.layout(childLeft, 0, childLeft + childWidth,
158                         childView.getMeasuredHeight());
159                 childLeft += childWidth;
160             }
161         }
162     }
163
164     private void smoothScrollBy(int dx, int dy) {
165         mScroller.startScroll(getScrollX(), 0, dx, 0, 500);
166         invalidate();
167     }
168
169     @Override
170     public void computeScroll() {
171         if (mScroller.computeScrollOffset()) {
172             scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
173             postInvalidate();
174         }
175     }
176
177     @Override
178     protected void onDetachedFromWindow() {
179         mVelocityTracker.recycle();
180         super.onDetachedFromWindow();
181     }
182 }

(5)来到主布局之中,在HorizontalScrollViewEx2之中包含一个子布局content_layout2.xml,如下:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:orientation="vertical" >
 6
 7     <TextView
 8         android:id="@+id/title"
 9         android:layout_width="wrap_content"
10         android:layout_height="wrap_content"
11         android:layout_marginTop="5dp"
12         android:layout_marginBottom="5dp"
13         android:text="TextView" />
14
15     <com.himi.viewconflict1.ui.ListViewEx
16         android:id="@+id/list"
17         android:layout_width="match_parent"
18         android:layout_height="match_parent"
19         android:background="#fff4f7f9"
20         android:cacheColorHint="#00000000"
21         android:divider="#dddbdb"
22         android:dividerHeight="1.0px"
23         android:listSelector="@android:color/transparent" />
24
25 </LinearLayout>

上面的ListViewEx是自定义的控件(继承自ListView),在ListViewEx里面实现了内部拦截法的逻辑,如下:

 1 package com.himi.viewconflict1.ui;
 2
 3 import android.content.Context;
 4 import android.util.AttributeSet;
 5 import android.util.Log;
 6 import android.view.MotionEvent;
 7 import android.widget.ListView;
 8
 9 public class ListViewEx extends ListView {
10     private static final String TAG = "ListViewEx";
11
12     private HorizontalScrollViewEx2 mHorizontalScrollViewEx2;
13
14     // 分别记录上次滑动的坐标
15     private int mLastX = 0;
16     private int mLastY = 0;
17
18     public ListViewEx(Context context) {
19         super(context);
20     }
21
22     public ListViewEx(Context context, AttributeSet attrs) {
23         super(context, attrs);
24     }
25
26     public ListViewEx(Context context, AttributeSet attrs, int defStyle) {
27         super(context, attrs, defStyle);
28     }
29
30     public void setHorizontalScrollViewEx2(
31             HorizontalScrollViewEx2 horizontalScrollViewEx2) {
32         mHorizontalScrollViewEx2 = horizontalScrollViewEx2;
33     }
34
35     @Override
36     public boolean dispatchTouchEvent(MotionEvent event) {
37         int x = (int) event.getX();
38         int y = (int) event.getY();
39
40         switch (event.getAction()) {
41         case MotionEvent.ACTION_DOWN: {
42             mHorizontalScrollViewEx2.requestDisallowInterceptTouchEvent(true);
43             break;
44         }
45         case MotionEvent.ACTION_MOVE: {
46             int deltaX = x - mLastX;
47             int deltaY = y - mLastY;
48             Log.d(TAG, "dx:" + deltaX + " dy:" + deltaY);
49             if (Math.abs(deltaX) > Math.abs(deltaY)) {
50                 mHorizontalScrollViewEx2.requestDisallowInterceptTouchEvent(false);
51             }
52             break;
53         }
54         case MotionEvent.ACTION_UP: {
55             break;
56         }
57         default:
58             break;
59         }
60
61         mLastX = x;
62         mLastY = y;
63         return super.dispatchTouchEvent(event);
64     }
65
66 }

void  requestDisallowInterceptTouchEvent(boolean  disallowIntercept):

这个方法的入参一个boolean 变量,用来表示是否需要调用onInterceptTouchEvent来判断是否拦截.

该标记如果为True,就如它的字面意思一样---不允许调用onInterceptTouchEvent(),结果就是,所有的父类方法都不会进行拦截,而把事件传递给子View. 该方法属于ViewGroup ,并且是个递归方法,也就是说一旦调用后,所有父类的disallowIntercept都会设置成True。即当前View的所有父类View,都不会调用自身的onInterceptTouchEvent()进行拦截。

该标记如果为False,就如它的字面意思一样---允许调用onInterceptTouchEvent(),结果就是,父类可以拦截事件。

接下来,来到Listview的Item布局content_list_item.xml,如下:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:layout_width="match_parent"
 4     android:layout_height="50dp"
 5     android:gravity="center_vertical"
 6     android:orientation="vertical" >
 7
 8     <TextView
 9         android:id="@+id/name"
10         android:layout_width="wrap_content"
11         android:layout_height="wrap_content"
12         android:text="TextView" />
13
14 </LinearLayout>

(6)最终项目如下:

(7)部署程序到手机上,如下:

3. 示例源码下载

转载于:https://www.cnblogs.com/hebao0514/p/5700730.html

自定义控件(视图)2期笔记13:View的滑动冲突之 内部拦截法相关推荐

  1. android中view手势滑动冲突的两种解决方法

    android中view手势滑动冲突的解决方法,主要解决方法有两种,外部和内部拦截.有需要的可以参考下. Android手势事件的冲突跟点击事件的分发过程息息相关,由三个重要的方法来共同完成,分别是: ...

  2. 安卓基础知识之View篇(四):View 事件滑动冲突解决方案

    安卓基础知识系列旨在简明扼要地提供面试或工作中常用的基础知识,让对安卓还不太熟悉的小伙伴更快地入门.同时自己在工作中,也没法完全记住所有的基础细节,写这样的系列文章,可以让自己形成一个更完备的知识体系 ...

  3. 一个Demo带你彻底掌握View的滑动冲突

    本文已授权微信公众号:鸿洋(hongyangAndroid)在微信公众号平台原创首发. 近期在又一次学习Android自己定义View这一块的内容.遇到了平时开发中常常碰到的一个棘手问题:View的滑 ...

  4. android自定义控件中文乱码,Android笔记--自定义View之组合控件

    Android-自定义View 分享是最好的记忆-- 如需转发请注明出处 [强调]:共同学习 共同进步 不喜勿喷 内容简介 前言 实现 总结 1. 前言 这次更新有2个目的 1. 复用控件,而不是每次 ...

  5. Git 中的对象模型和文件的详细视图 —— Git 学习笔记 13

    Git 中的对象模型和文件的详细视图 文章目录 Git 中的对象模型和文件的详细视图 初始状态 编辑一个文件之后 暂存文件后 提交之后 参考资料 本文将用几幅图来可视化一个叫 file1的文件从修改到 ...

  6. Android自定义View,滑动,事件传递小结

    本文只总结知识点 欢迎补充,欢迎纠正.谢谢! #预备知识 Android控件框架 ####1. View树状图 Android的View树结构总是以一个ViewGroup开始,包含多个View或Vie ...

  7. Android View的事件分发机制和滑动冲突解决方案

    这篇文章会先讲Android中View的事件分发机制,然后再介绍Android滑动冲突的形成原因并给出解决方案.因水平有限,讲的不会太过深入,只希望各位看了之后对事件分发机制的流程有个大概的概念,并且 ...

  8. 自定义View(二)--表层浅析View的事件分发机制和滑动冲突

    转载请注明出处:From李诗雨:http://blog.csdn.net/cjm2484836553/article/details/54387722 不诗意的女程序猿不是好厨师~ 这篇文章来得有些曲 ...

  9. 兔八哥笔记13:我的2003

    兔八哥笔记13:我的2003                                     邮箱:ltf_ty@163.net 又要到年底了,感觉时间过得飞快,不知不觉中自己又老了一岁,去年 ...

最新文章

  1. 创建物化视图commit_oracle 中物化视图讲解
  2. 20家单位共同倡议发起成立上海人工智能发展联盟
  3. iOS开发OC基础:Xcode中常见英文总结,OC常见英文错误
  4. 02 判断某个字符串是否由一个子字符串重复组成
  5. oracle 11g 大量废连接占满数据库连接问题处理
  6. Python configparser模块
  7. react-native项目打包速度优化
  8. HDU2546_用01背包做
  9. C++编译原理 (转载)
  10. mysql数据库需求分析工具_一份全面的“数据库设计需求分析”是怎样的?
  11. 今天终于搞懂了:为什么Java的main方法必须是public static void?
  12. 并查集【算法笔记/晴神笔记】
  13. PHP经典面试题——数据库优化
  14. 孙空空的Vue之路-Day07-事件处理
  15. 通俗的解释卡尔曼滤波(Kalman Filter)以及其Python的实现
  16. Android手机多媒体
  17. python socket实现实时通信
  18. ACPI的介绍和概念
  19. 免费分享kali-linux-2019.2-vmware-amd64.7z
  20. 电子信息工程专业,大学要学什么

热门文章

  1. cordova 5.0版本说明
  2. 自定义配置节与配置节的读取
  3. 一起Polyfill系列:Function.prototype.bind的四个阶段
  4. Android Intent.FLAG_NEW_TASK详解,包括其他的标记的一些解释
  5. 分享一个前后端分离的web项目(vue+spring boot)
  6. RUNOOB python练习题13 水仙花数
  7. 3.13 判读是否是对称素数
  8. 数据结构05数组和广义表
  9. turtle 20秒画完小猪佩奇“社会人”
  10. JVM(2)--一文读懂垃圾回收