*****
**事件分發的使用**
[TOC=6]
# 1.解決事件滑動沖突的思路及方法
## 1.1常見的三種情況
第一種情況,滑動方向不同

第二種情況,滑動方向相同

第三種情況,上述兩種情況的嵌套

## 1.2 解決思路
看了上面三種情況,我們知道他們的共同特點是父View 和子View都想爭著響應我們的觸摸事件,但遺憾的是我們的觸摸事件 同一時刻只能被某一個View或者ViewGroup攔截消費,所以就產生了滑動沖突?那既然同一時刻只能由某一個View或者ViewGroup消費攔截,那我們就只需要 決定在某個時刻由這個 View 或者 ViewGroup 攔截事件,另外的 某個時刻由 另外一個 View 或者 ViewGroup 攔截事件,不就OK了嗎?綜上,正如 在?《Android開發藝術》?一書提出的,總共 有兩種解決方案
以下解決思路來自于?《Android開發藝術》?書籍
下面的兩種方法針對第一種情況(滑動方向不同),父View是上下滑動,子View是左右滑動的情況。
## 1.3 外部解決法
從父View著手,重寫onInterceptTouchEvent方法,在父View需要攔截的時候攔截,不要的時候返回false,為代碼大概 如下
~~~
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final float x = ev.getX();
final float y = ev.getY();
final int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
mDownPosX = x;
mDownPosY = y;
break;
case MotionEvent.ACTION_MOVE:
final float deltaX = Math.abs(x - mDownPosX);
final float deltaY = Math.abs(y - mDownPosY);
// 這里是夠攔截的判斷依據是左右滑動,讀者可根據自己的邏輯進行是否攔截
if (deltaX > deltaY) {
return false;
}
}
return super.onInterceptTouchEvent(ev);
}
~~~
## 1.4 內部解決法
從子View著手,父View先不要攔截任何事件,所有的事件傳遞給 子View,如果子View需要此事件就消費掉,不需要此事件的話就交給 父View處理。
實現思路 如下,重寫子 View的dispatchTouchEvent方法,在Action\_down 動作中通過方法 requestDisallowInterceptTouchEvent(true) 先請求 父 View不要攔截事件,這樣保證子 View 能夠接受到 Action\_move 事件,再在 Action\_move 動作中根據自己的邏輯是否要攔截事件,不需要攔截事件的話再交給 父 View 處理。
~~~
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
int x = (int) ev.getRawX();
int y = (int) ev.getRawY();
int dealtX = 0;
int dealtY = 0;
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
dealtX = 0;
dealtY = 0;
// 保證子View能夠接收到Action_move事件
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
dealtX += Math.abs(x - lastX);
dealtY += Math.abs(y - lastY);
Log.i(TAG, "dealtX:=" + dealtX);
Log.i(TAG, "dealtY:=" + dealtY);
// 這里是夠攔截的判斷依據是左右滑動,讀者可根據自己的邏輯進行是否攔截
if (dealtX >= dealtY) {
getParent().requestDisallowInterceptTouchEvent(true);
} else {
getParent().requestDisallowInterceptTouchEvent(false);
}
lastX = x;
lastY = y;
break;
case MotionEvent.ACTION_CANCEL:
break;
case MotionEvent.ACTION_UP:
break;
}
return super.dispatchTouchEvent(ev);
}
~~~
# 2. ScrollView嵌套ListView或GridView滑動沖突解決方案
開發過程中經常會遇到使用scrollview嵌套listview或gridview的情況,這時由于scrollview攔截消費了滑動事件,所以在listview或gridview區域滑動時該區域無法滑動,而是scrollview整體滑動。
正確的處理應該是當焦點在listview或gridview區域該區域滑動,在區域外則scrollview滑動。
想要解決這個問題,加上如下代碼即可:
~~~
listView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View arg0, MotionEvent arg1) {
scrollView.requestDisallowInterceptTouchEvent(true);
return false;
}
}
~~~
~~~
public class MyListView extends ListView {
public MyListView(Context context) {
super(context);
}
public MyListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
}
~~~
# 3. ViewPager嵌套ViewPager滑動沖突解決方案
## 內部解決法
從子View ViewPager著手,重寫 子View的 dispatchTouchEvent方法,在子 View需要攔截的時候進行攔截,否則交給父View處理,代碼如下
~~~
public class ChildViewPager extends ViewPager {
private static final String TAG = "xujun";
public ChildViewPager(Context context) {
super(context);
}
public ChildViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
int curPosition;
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
curPosition = this.getCurrentItem();
int count = this.getAdapter().getCount();
Log.i(TAG, "curPosition:=" +curPosition);
// 當當前頁面在最后一頁和第0頁的時候,由父親攔截觸摸事件
if (curPosition == count - 1|| curPosition==0) {
getParent().requestDisallowInterceptTouchEvent(false);
} else {//其他情況,由孩子攔截觸摸事件
getParent().requestDisallowInterceptTouchEvent(true);
}
}
return super.dispatchTouchEvent(ev);
}
}
~~~
# 4. ScrollView嵌套ViewPager滑動沖突解決方案(了解)
## 4.1 外部解決
從 父View ScrollView著手,重寫 OnInterceptTouchEvent方法,在上下滑動的時候攔截事件,在左右滑動的時候不攔截事件,返回 false,這樣確保子View 的dispatchTouchEvent方法會被調用,代碼 如下
~~~
/**
* @ explain:這個ScrlloView不攔截水平滑動事件,
* 是用來解決 ScrollView里面嵌套ViewPager使用的
*/
public class VerticalScrollView extends ScrollView {
public VerticalScrollView(Context context) {
super(context);
}
public VerticalScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public VerticalScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@TargetApi(21)
public VerticalScrollView(Context context, AttributeSet attrs, int defStyleAttr, int
defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
private float mDownPosX = 0;
private float mDownPosY = 0;
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final float x = ev.getX();
final float y = ev.getY();
final int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
mDownPosX = x;
mDownPosY = y;
break;
case MotionEvent.ACTION_MOVE:
final float deltaX = Math.abs(x - mDownPosX);
final float deltaY = Math.abs(y - mDownPosY);
// 這里是否攔截的判斷依據是左右滑動,讀者可根據自己的邏輯進行是否攔截
if (deltaX > deltaY) {// 左右滑動不攔截
return false;
}
}
return super.onInterceptTouchEvent(ev);
}
}
~~~
## 4.2 內部解決
通過requestDisallowInterceptTouchEvent(true)方法來影響父View是否攔截事件,我們通過重寫ViewPager的 dispatchTouchEvent()方法,在左右滑動的時候請求父View ScrollView不要攔截事件,其他的時候由子View 攔截事件
~~~
/**
* @ explain:這個 ViewPager是用來解決ScrollView里面嵌套ViewPager的 內部解決法的
*/
public class MyViewPager extends ViewPager {
private static final String TAG = "xujun";
int lastX = -1;
int lastY = -1;
public MyViewPager(Context context) {
super(context);
}
public MyViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
int x = (int) ev.getRawX();
int y = (int) ev.getRawY();
int dealtX = 0;
int dealtY = 0;
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
dealtX = 0;
dealtY = 0;
// 保證子View能夠接收到Action_move事件
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
dealtX += Math.abs(x - lastX);
dealtY += Math.abs(y - lastY);
Log.i(TAG, "dealtX:=" + dealtX);
Log.i(TAG, "dealtY:=" + dealtY);
// 這里是否攔截的判斷依據是左右滑動,讀者可根據自己的邏輯進行是否攔截
if (dealtX >= dealtY) { // 左右滑動請求父 View 不要攔截
getParent().requestDisallowInterceptTouchEvent(true);
} else {
getParent().requestDisallowInterceptTouchEvent(false);
}
lastX = x;
lastY = y;
break;
case MotionEvent.ACTION_CANCEL:
break;
case MotionEvent.ACTION_UP:
break;
}
return super.dispatchTouchEvent(ev);
}
}
~~~
- 咨詢項目實戰
- 第一單元 HTTP協議
- 1.1 OSI七層模型
- 1.2 HTTP協議(重點)
- 1.3 HTTPS協議(了解)
- 1.4 TCP/IP協議擴展
- 1.5 WebService簡介及實戰(無接口)
- 1.6 課后練習
- 第二單元 HTTPURLConnection
- 2.1 ANR
- 2.2 網絡判斷
- 2.3 HTTPURLConnection
- 2.4 課后練習
- 第三單元 AsyncTask
- 3.1 AsyncTask概述
- 3.2 AsyncTask基本使用
- 3.3 課后練習
- 第四單元 圖片異步加載
- 4.1 圖片異步加載概述
- 4.2 LruCache
- 4.3 DiskLRUCache
- 4.4 圖片三級緩存概述
- 4.5 封裝圖片加載緩存框架
- 第五單元 ListView多條目
- 5.1 ListView多條目概述
- 5.2 ListView多條目的使用
- 第六單元 ListView實現下拉刷新上拉加載
- 6.1 下拉刷新和上拉加載更多
- 6.2 XListView概述
- 6.3 XListView的使用
- 第七單元 封裝網絡框
- 7.1 封裝網絡框架概述
- 7.2 網絡框架的封裝
- 第八單元 項目介紹
- 8.1 公司項目團隊架構簡介
- 8.2 項目文檔及項目流程介紹
- 8.3 項目管理
- 8.4 項目開發
- 第九單元 項目框架搭建
- 9.1 基類封裝概述
- 9.2 Application中初始化配置
- 9.3 項目中的工具類
- 9.4 封裝網絡請求框架
- 9.5 封裝圖片異步緩存框架
- 第十單元 搭建UI框架1
- 10.1 側滑菜單概述
- 10.2 主界面框架搭建
- 第十一單元 搭建UI框架2
- 11.1 TabLayout的概述
- 11.2 TabLayout的使用
- 第十二單元 圖片上傳
- 12.1 圖片上傳概述
- 12.2 圖片上傳的實現
- 第十三單元 PullToRefresh
- 13.1 PullToRefresh概述
- 13.2 PullToRefresh的使用
- 13.3 緩存業務實現思路
- 第十四單元 事件分發及滑動沖突
- 14.1 事件分發概述
- 14.2 事件分發流程
- 14.3 事件分發的使用
- 第十五單元 傳感器的基本使用
- 15.1 傳感器概述
- 15.2 傳感器的使用
- 第十六單元 HTML與CSS復習
- 16.1 HTML
- 16.2 CSS
- 第十七單元 js復習
- 17.1 js基礎語法
- 17.2 js數組和內置對象
- 17.3 js常用事件
- 17.4 js對象模型
- 17.5 js 正則表達式
- 第十八單元 WebView
- 18.1 WebView 概述
- 18.2 WebView的使用
- 18.3 WebView與js交互
- 第十九單元 項目案例
- 項目概述
- 第二十單元 項目答辯
- 周考
- 第一周周考
- 第二周周考
- 第三種周考
- 月考
- 接口文檔