通過事件分發機制處理ListView與ScrollView滑動衝突
阿新 • • 發佈:2019-01-08
ListView與ScrollView滑動衝突處理,是一個很經典的案例,網路上有各種各樣的解決方案,比如使用LinearLayout取代ListView、重寫ListView的onMeasure方法都能很好的解決這個問題。
但是本次採用的是通過重寫ListView的dispatchTouchEvent來處理滑動衝突,並以此加深對Android事件分發機制的理解。
在此之前先了解下面這三個方法:
1. dispatchTouchEvent(MotionEvent event)
如果事件傳遞給View那麼此方法一定能夠被呼叫,其返回結果表示是否消費當前的事件。
2. onIntercepteTouchEvent(MotionEvent event)
表示是否攔截當前事件,如果當前View攔截了某個事件,那麼在同一事件序列當中,此方法不會再次被呼叫,其返回結果表示是否攔截
3. onTouchEvent(MotionEvent event)
在dispatchTouchEvent(MotionEvent event)方法中呼叫,用於處理點選事件,返回結果表示是否消費當前事件,如果不消費則在同一事件序列中,當前View無法再次接收到事件。
熟悉了這三個方法再說說最關鍵的內容了,就是如何重寫dispatchTouchEvent(MotionEvent event),下面上程式碼:
View:
public class InsideInterceptListView extends ListView { private float lasty; public InsideInterceptListView(Context context) { super(context); } public InsideInterceptListView(Context context, AttributeSet attrs) { super(context, attrs); } public InsideInterceptListView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { int action = ev.getAction(); float y = ev.getY(); switch (action) { case MotionEvent.ACTION_DOWN: //此處必須讓父View不攔截事件,否則後面的事件無法獲取 getParent().requestDisallowInterceptTouchEvent(true); break; case MotionEvent.ACTION_MOVE: //獲取ListView當前的第一個可見item int frist = getFirstVisiblePosition(); //獲取ListView當前的最後一個可見item int last = getLastVisiblePosition(); //獲取item總數 int child = getCount(); Log.i("child", "child: " + child); if (y > lasty && frist == 0) { //ListView的第一個可見View的position == 0 並且向下滑動時,請求父View攔截事件 getParent().requestDisallowInterceptTouchEvent(false); } else if (y < lasty && last == child - 1) { //ListView的最後一個個可見View的position是最後一個item, 並且向上滑動時,請求父View攔截事件 getParent().requestDisallowInterceptTouchEvent(false); } else { //其它情況,事件由本View消費 getParent().requestDisallowInterceptTouchEvent(true); } break; case MotionEvent.ACTION_UP: getParent().requestDisallowInterceptTouchEvent(true); break; default: break; } lasty = y; return super.dispatchTouchEvent(ev); } }
View中主要是通過重寫dispatchTouchEvent方法,在方法內根據當前滑動的方向和當前view的item數量去請求父類是否攔截事件。
public class InsideInterceptActivity extends Activity {
private InsideInterceptListView lvTest;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_inside_intercept);
initView();
}
/**
* 初始化控制元件
*/
@SuppressLint("CommitTransaction")
private void initView() {
lvTest = (InsideInterceptListView) findViewById(R.id.test_lv);
List<String> data = new ArrayList<String>();
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, data);
for (int i = 0; i < 50; i++) {
data.add("ITEM:" + i);
}
lvTest.setAdapter(adapter);
}
}
Activity裡面沒技術含量,基本的實現而已
XML:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="${relativePackage}.${activityClass}" >
<ScrollView
android:id="@+id/scrollView1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="@+id/titil_tv"
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="標題:內部攔截"
android:textSize="16sp"
android:textColor="#6C080A"/>
<com.xuzhenhao.demo.views.InsideInterceptListView
android:id="@+id/test_lv"
android:layout_width="match_parent"
android:layout_height="400dp"
android:scrollbars="none"/>
<TextView
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="標題:內部攔截"
android:textSize="16sp"
android:textColor="#6C080A"/>
<TextView
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="標題:內部攔截"
android:textSize="16sp"
android:textColor="#6C080A"/>
<TextView
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="標題:內部攔截"
android:textSize="16sp"
android:textColor="#6C080A"/>
<TextView
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="標題:內部攔截"
android:textSize="16sp"
android:textColor="#6C080A"/>
<TextView
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="標題:內部攔截"
android:textSize="16sp"
android:textColor="#6C080A"/>
<TextView
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="標題:內部攔截"
android:textSize="16sp"
android:textColor="#6C080A"/>
<TextView
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="標題:內部攔截"
android:textSize="16sp"
android:textColor="#6C080A"/>
<TextView
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="標題:內部攔截"
android:textSize="16sp"
android:textColor="#6C080A"/>
<TextView
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="標題:內部攔截"
android:textSize="16sp"
android:textColor="#6C080A"/>
<TextView
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="標題:內部攔截"
android:textSize="16sp"
android:textColor="#6C080A"/>
<TextView
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="標題:內部攔截"
android:textSize="16sp"
android:textColor="#6C080A"/>
<TextView
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="標題:內部攔截"
android:textSize="16sp"
android:textColor="#6C080A"/>
<TextView
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="標題:內部攔截"
android:textSize="16sp"
android:textColor="#6C080A"/>
<TextView
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="標題:內部攔截"
android:textSize="16sp"
android:textColor="#6C080A"/>
<TextView
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="標題:內部攔截"
android:textSize="16sp"
android:textColor="#6C080A"/>
<TextView
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="標題:內部攔截"
android:textSize="16sp"
android:textColor="#6C080A"/>
<TextView
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center"
android:text="標題:內部攔截"
android:textSize="16sp"
android:textColor="#6C080A"/>
</LinearLayout>
</ScrollView>
</RelativeLayout>
xml裡面只是簡單的佈局。
下面看一看最終的效果!!
通過這個效果主要是為了加深對Android事件分發機制的瞭解,想更深入瞭解可以檢視下面這些博文,說不定會有意想不到的收穫!
View的事件分發機制學習筆記
Android View 事件分發機制 原始碼解析 (上)