Android 滑動衝突與解決策略

安卓開發過程中滑動衝突的情形主要有三類:

① 父view與子view的滑動方向不同,如:父view左右滑動,子view上下滑動或相反;

這種情形是比較簡單的,只需要根據不同的滑動動作進行相應的攔截與處理即可。

② 父view與子view的滑動方向相同,即,父view左右,子view也左右,父view上下,子view也是上下;

這種情形需要根據具體情況來進行攔截處理,比如父View在出現子View滑動到邊緣的情況才進行

攔截處理或者其他情況。

③ 以上兩種情形,多個View的嵌套。

這種情形雖比較複雜,但根據上面兩種情形的處理法則分開進行處理即可。


滑動衝突的解決策略的理論基礎爲安卓的事件分發機制,不甚瞭解的朋友可以去先補補這方面的知識。

針對滑動衝突的解決策略有以下兩種:

一種是外部攔截法:即是當事件滿足滑動條件,通過父View的onInterceptTouchEvent方法對其進行

攔截,攔截之後將直接進入父View的onTouchEvent進行事件消費,不會再傳入下級view;

二種是內部攔截法:即是通過子View的dispatchTouchEvent方法接收到down事件,然後獲取父View的requestDisallowInterceptTouchEvent方法禁止其onInterceptTouchEvent攔截,當滿足父View滑動條件的

時候才允許。

第二種方法需要父View不攔截down事件,一般情況下也是不攔截down事件的,因爲攔截了down事件,

所有子元素點擊事件都會失效。建議採用第一種方法,易於理解,不容易出錯。


針對第一種方法,下面

一個下拉刷新的SwipeRefreshLayout裏面嵌套了左右滑動、並且在pullToRefreshListView頭的viewPage

和上下滑動pullToRefreshListView(這裏解釋一下,pullToRefreshListView禁掉了它的下拉刷新功能,只用它

的上拉刷新),若不做解決事件衝突處理,就會出現只能父View下拉刷新,viewPager左右滑動無效,並且當

ListView往下滑動後不能再上滑,因爲上滑時候的下拉事件被父view攔截了,所以這裏有了兩種衝突情形。

首先先分析viewPage與父view SwipeRefreshLayout的滑動衝突,一個上下,一個左右,那好辦,只要重寫SwipeRefreshLayout的onInterceptTouchEvent對滑動事件進行事件判斷,如果是左右滑動的那麼返回false

進行攔截,事件將直接傳到子View,即傳給了viewPager,實現其左右滑動的操作。然後說說pullToRefreshListView

與父view SwipeRefreshLayout的滑動衝突,因爲兩者都要上滑即手勢下拉的操作,所以產生了衝突。而這裏我

只是想當pullToRefreshListView滑動到頂部才使能SwipeRefreshLayout的下拉刷新,所以思路就是當沒滑動到

頂部的時候SwipeRefreshLayout的onInterceptTouchEvent方法返回false 不進行攔截,把事件傳給他的子View pullToRefreshListView,從而實現其上滑操作,直到滑動到了頂部父View纔對事件進行攔截,如此便解決了

事件的衝突。

獲取子View pullToRefreshListView,並開啓滑動監聽,判斷是否處於頂部

<span style="font-size:14px;">                if(getChildAt(0).getId() == R.id.pullToRefreshListView){
                    ((PullToRefreshListView)getChildAt(0)).getRefreshableView().setOnScrollListener(new AbsListView.OnScrollListener() {
                        @Override
                        public void onScrollStateChanged(AbsListView view, int scrollState) {}
                        @Override
                        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                            if(firstVisibleItem==0){
                                isFirst = true;
                            }else{
                                isFirst = false;
                            }
                        }
                    });
                }</span>

重寫onInterceptTouchEvent進行條件判斷

<span style="font-size:14px;"> @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mPrevX = MotionEvent.obtain(event).getX();
                break;
            case MotionEvent.ACTION_MOVE:
                final float eventX = event.getX();
                float xDiff = Math.abs(eventX - mPrevX);
                if (xDiff > mTouchSlop) {
                    return false;
                }
                if(!isFirst){
                    return false;
                }
        }
        return super.onInterceptTouchEvent(event);
    }</span>

爲了精準判斷滑動的方式,我們在自定義viewgroup裏面重寫onInterceptTouchEvent時,需要對滑動

的xy軸距離判斷大小,若豎直方向距離大則認爲是豎直滑動,否則認爲是水平滑動。


而第二種方法一般代碼如下:

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                getParent().requestDisallowInterceptTouchEvent(true);
                break;
            case MotionEvent.ACTION_MOVE:
                if("滿足條件")
                    getParent().requestDisallowInterceptTouchEvent(false);
                break;
        }
        return super.dispatchTouchEvent(ev);
    }



發佈了33 篇原創文章 · 獲贊 36 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章