[Android] 解決 View 滑動衝突法則

前言

這是 demo 地址

上面 demo 用外部攔截法實現了滑動方向一致和不一致的兩種衝突。

滑動衝突場景

  • 場景 1————外部滑動方向和內部滑動方向不一致
  • 場景 2————外部滑動方向和內部滑動方向一致
  • 場景 3————上面兩種情況的嵌套

場景 1,主要是 ViewPager 和 Fragment 配合使用組成的頁面橫向滑動效果,而 Fragment 裏又包含了 ListView 豎直滑動的控件。由於 ViewPager 裏做了滑動衝突的處理,所以使用 ViewPager 時沒有出現滑動衝突的現象。

場景 2,內外兩層都在同一個方向可以滑動。比如,使用 ViewPager 和 Fragment 配合做頁面橫向滑動。在 Fragment 中使用 RecyclerView 做橫向滑動效果。

場景 3,是場景 1 和場景 2 的結合。比如,使用 ViewPager 和 Fragment 配合做頁面橫向滑動,Fragment 中使用 ListView 做豎直方向的滑動,而 ListView 中的 item 中包含橫向滑動的 RecyclerView。這種場景有些複雜,但有時在實際工作也會遇到,比如我公司項目鋒繪動漫的首頁就是這種場景。

滑動衝突的解決方式

外部攔截法

外部攔截法是指觸摸事件先經過父容器的攔截處理,如果父容器需要此事件就攔截,否則不攔截。外部攔截法需要重寫父容器的 onInterceptTouchEvent 方法,在此方法中做相應的攔截即可,僞代碼如下:

    @Override public boolean onInterceptTouchEvent(MotionEvent ev) {

    boolean intercept = false;
    int x = (int) ev.getX();
    int y = (int) ev.getY();
    switch (ev.getAction()) {
      case MotionEvent.ACTION_DOWN:
        intercept = false;
        break;
      case MotionEvent.ACTION_MOVE:
        int dealtX = x - mInterceptX;
        int dealtY = y - mInterceptY;
        if(父容器需要當前事件){
          intercept = true;
        }else{
          intercept = false;
        }
        break;
      case MotionEvent.ACTION_UP:
        intercept = false;
        mInterceptX = mInterceptY = 0;

        break;
    }

    return intercept;  }

在 onInterceTouchEvent 方法中,ACTION_DOWN 事件,父容器必須返回 false,因爲一旦父容器攔截了 ACTION_DOWN,那麼後續的 ACTION_MOVE 和 ACTION_UP 都會直接交由父容器處理,事件無法再傳遞給子元素。ACTION_UP 也必須返回 false,因爲如果父容器 ACTION_UP 返回 true,就導致子元素無法接受 ACTION_UP,子元素中的 onClick 事件也就無法觸發了。

內部攔截

內部攔截是指父容器不攔截任何事件,所有的事件都傳遞給子元素,如果子元素需要此事件就直接消耗掉,否則就交給父容器處理。這種方法和 Android 的事件分發機制不一致,需要配合 requestDisallowInterceTouchEvent 方法才能正常工作。我麼需要重寫子元素的 dispatchTouchEven 方法,僞代碼如下:

    @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;
      case MotionEvent.ACTION_UP:

        break;
    }
    return super.dispatchTouchEvent(ev); }

上述代碼是內部攔截的典型代碼,當面對不同的互動策略時只需要修改裏面的條件即可。除了子元素需要做處理父元素也要重寫 onInterceptTouchEvent 方法,攔截除了 ACTION_DOWN 以外的其他事件,這樣當子元素調用 getParent().requestDisallowInterceptTouchEvent(false) 時,父元素才能攔截所需要的事件。

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