1. 程式人生 > >ViewPager結合TabLayout --- 禁止滑動(點選切換)

ViewPager結合TabLayout --- 禁止滑動(點選切換)

【記錄】菜鳥記錄點滴

場景: ViewPager結合TabLayout,兩個Tab項(Tab1, Tab2),需要先在Tab1執行某些操作後,才能滑動ViewPager(點選Tab)切換到Tab2

1. 首先自定義CustomViewPager,可以設定允許/禁止滑動ViewPager。

繼承ViewPager,根據事件的分發機制,修改onInterceptTouchEvent()

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if(canScroll){
            return super.onInterceptTouchEvent(ev);
        } else {
            return false;
        }
    }

ViewPager繼承ViewGroup,禁止滑動時,canScroll = false,此時自定義的CustomViewPager不會攔截Touch事件及消費,繼續分發給子View

PS: 通過viewpager.setOnTouchListener()來消費事件,無法真正禁止滑動,此時ViewPager仍可以有小幅度的變化。

補充: 簡單查看了ViewGroup與View的dispatchTouchEvent方法,並通過Log確認,設定onTouchListener消費事件後,不會觸發onTouchEvent。再次重寫響應Scroller的computeScroll方法,ViewPager仍然會移動。

補充:ViewPager沒有重寫dispatchTouchEvent但是重寫了onInterceptTouchEvent,在重寫的onInterceptTouchEvent中,move事件是關鍵。onInterceptTouchEvent最終返回mIsBeingDragged,如果x軸上move的距離滿足條件,那麼mIsBeingDragged = true,畫面會滑動。

if (mIsBeingDragged) {
    // Scroll to follow the motion event
    if (performDrag(x)) {
        ViewCompat.postInvalidateOnAnimation(this);
    }
}

至於為什麼只有第一次move會發生滑動,由於事件分發機制的很多細節理解的還不是很透徹,猜測 : dispatchTouchEvent根據target標誌決定後續處理,而第一次onInterceptTouchEvent攔截事件交給onTouchEvent消費後會標記target,此後就不會再觸發這部分邏輯。

2. 設定TabLayout不可點選切換

參照其他部落格的思路,從頭開始分析,從層次中看出需要設定TabView,再檢視原始碼。從addTab入手,發現TabView對應Tab類中的mView欄位,最終被新增到SlidingTabStrip中(design-27.1.1)。因此利用反射,獲取TabView物件,並實現禁止點選。

    TabLayout.Tab tab = tabLayout.getTabAt(1);
    Class clazz = tab.getClass();
    try {
        Field field = clazz.getDeclaredField("mView");
        field.setAccessible(true);
        View view = (View) field.get(tab);
        view.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if(canScroll){
                    return false;
                } else {
                    Log.e("lxy", "提示或者幹什麼...");
                    return true;
                }
            }
        });
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }

PS: 類似的,應該也可以反射獲得SlidingTabStrip,通過獲得子View來設定

補充: 按照上述的分析,實現程式碼如下,同樣可以實現

    Class clazz2 = tabLayout.getClass();
    try {
        Field field = clazz2.getDeclaredField("mTabStrip");
        field.setAccessible(true);
        ViewGroup viewGroup = (ViewGroup) field.get(tabLayout);
        View v = viewGroup.getChildAt(1);
        v.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                ToastUtils.show("不可點選");
                return true;
            }
        });
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }