1. 程式人生 > >通過事件分發機制處理ListView與ScrollView滑動衝突

通過事件分發機制處理ListView與ScrollView滑動衝突

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數量去請求父類是否攔截事件。


Activity
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 事件分發機制 原始碼解析 (上)