ViewPager2 嵌套ViewPager2 滑動沖突解決

不知道大家是否有這個疑問,在 剛開始做Android的時候 ViewPager 占卧,ViewPager 嵌套 ViewPager 并沒有出現(xiàn)過滑動沖突遗菠×可是為什么在 ViewPager 的升級版 ViewPager2 中卻出現(xiàn)了滑動沖突呢?

  1. 首先我們看下ViewPager的源碼
    滑動沖突是需要在 onInterceptTouchEvent()方法中進行處理的辙纬,根據(jù)自身條件豁遭,來決定是否要攔截事件。在 ViewPager 的源碼中看到以下代碼:
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
 
  final int action = ev.getAction() & MotionEvent.ACTION_MASK;
  if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
    // 在事件取消或者抬起手指后重置狀態(tài)
      resetTouch();
      return false;
  }
 
 
  switch (action) {
    case MotionEvent.ACTION_MOVE: {
      // 這里判斷在水平方向上的滑動距離大于豎直方向的2倍贺拣,則認為是有效的切換頁面的滑動
      if (xDiff > mTouchSlop && xDiff * 0.5f > yDiff) { 
        mIsBeingDragged = true;
        // 禁止Parent View攔截事件蓖谢,即事件要能夠傳遞到ViewPager
        requestParentDisallowInterceptTouchEvent(true);
        setScrollState(SCROLL_STATE_DRAGGING);
      } else if (yDiff > mTouchSlop) {
        mIsUnableToDrag = true;
      }
      break;
    }
 
    case MotionEvent.ACTION_DOWN: {     
      if (mScrollState == SCROLL_STATE_SETTLING
              && Math.abs(mScroller.getFinalX() - mScroller.getCurrX()) > mCloseEnough) {
        // 在Down事件中禁止Parent View攔截事件,是為了事件序列能夠傳遞到ViewPager
        requestParentDisallowInterceptTouchEvent(true);
        setScrollState(SCROLL_STATE_DRAGGING);
      } else {
        completeScroll(false);
        mIsBeingDragged = false;
      }
      break;
    }
 
    case MotionEvent.ACTION_POINTER_UP:
      onSecondaryPointerUp(ev);
      break;
  }
  return mIsBeingDragged;
}

  1. 可以看 在 ACTION_DOWNACTION_MOVE 中根據(jù)一些判斷條件譬涡,調(diào)用了 requestParentDisallowInterceptTouchEvent(true) 方法來禁止Parent View 攔截事件闪幽。也就是說,ViewPager已經(jīng)幫我們處理了滑動沖突涡匀,所以我們只管用即可盯腌,無需擔心滑動沖突問題。
  2. 現(xiàn)在陨瘩,我們看 ViewPager2 翻閱源碼發(fā)現(xiàn)腕够,只有在 RecyclerView 的實現(xiàn)類中有 onInterceptTouchEvent() 的相關(guān)方法,而且這句代碼僅僅是處理禁用了用戶輸入的邏輯
private class RecyclerViewImpl extends RecyclerView {
  .... // 省略部分代碼
  @Override
  public boolean onInterceptTouchEvent(MotionEvent ev) {
    return isUserInputEnabled() && super.onInterceptTouchEvent(ev);
  }
}
  1. ViewPager2其實并沒有幫我們處理滑動沖突舌劳!
    ViewPager2 被聲明了 final帚湘,意味著我們不能像繼承 ViewPager一樣,來修改 ViewPager2甚淡。如果官方在 ViewPager2 內(nèi)部自行處理了滑動沖突大诸,那么如果有特殊的需求,需要根據(jù)我們自己的情況材诽,來處理 ViewPager2的滑動底挫,那么官方寫的處理滑動沖突的代碼,是不是會影響到我們自己的需求?
  2. 由于 ViewPager2被設(shè)置成了 final脸侥,我們無法通過繼承的方式來處理建邓,因此就需要我們在 ViewPager2 外部加一層自定義的 Layout。這層 Layout其實相當于夾在了內(nèi)層 View 和外層 View 的中間睁枕,其實就是這層 Layout 就變成了內(nèi)層.
    詳情代碼實現(xiàn):
/**
 * @Desciption :ViewPager2 嵌套后的滑動沖突解決方案
 */
class ViewPager2Container @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : RelativeLayout(context, attrs, defStyleAttr) {
 
  private var mViewPager2: ViewPager2? = null
  private var disallowParentInterceptDownEvent = true
  private var startX = 0
  private var startY = 0
 
//遍歷ViewPager2Container 的所有子 View官边,如果沒有找到 ViewPager2 就拋出異常
  override fun onFinishInflate() {
    super.onFinishInflate()
    for (i in 0 until childCount) {
      val childView = getChildAt(i)
      if (childView is ViewPager2) {
        mViewPager2 = childView
        break
      }
    }
    if (mViewPager2 == null) {
      throw IllegalStateException("The root child of ViewPager2Container must contains a ViewPager2")
    }
  }
 
  override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
    val doNotNeedIntercept = (!mViewPager2!!.isUserInputEnabled
            || (mViewPager2?.adapter != null
            && mViewPager2?.adapter!!.itemCount <= 1))
    if (doNotNeedIntercept) {
      return super.onInterceptTouchEvent(ev)
    }
    when (ev.action) {
      MotionEvent.ACTION_DOWN -> {
        startX = ev.x.toInt()
        startY = ev.y.toInt()
        parent.requestDisallowInterceptTouchEvent(!disallowParentInterceptDownEvent)
      }
      MotionEvent.ACTION_MOVE -> {
        val endX = ev.x.toInt()
        val endY = ev.y.toInt()
        val disX = abs(endX - startX)
        val disY = abs(endY - startY)
        if (mViewPager2!!.orientation == ViewPager2.ORIENTATION_VERTICAL) {
          onVerticalActionMove(endY, disX, disY)
        } else if (mViewPager2!!.orientation == ViewPager2.ORIENTATION_HORIZONTAL) {
          onHorizontalActionMove(endX, disX, disY)
        }
      }
      MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> parent.requestDisallowInterceptTouchEvent(false)
    }
    return super.onInterceptTouchEvent(ev)
  }
 
  private fun onHorizontalActionMove(endX: Int, disX: Int, disY: Int) {
    if (mViewPager2?.adapter == null) {
      return
    }
    if (disX > disY) {
      val currentItem = mViewPager2?.currentItem
      val itemCount = mViewPager2?.adapter!!.itemCount
      if (currentItem == 0 && endX - startX > 0) {
        parent.requestDisallowInterceptTouchEvent(false)
      } else {
        parent.requestDisallowInterceptTouchEvent(currentItem != itemCount - 1
          || endX - startX >= 0)
      }
    } else if (disY > disX) {
      parent.requestDisallowInterceptTouchEvent(false)
    }
  }
 
  private fun onVerticalActionMove(endY: Int, disX: Int, disY: Int) {
    if (mViewPager2?.adapter == null) {
      return
    }
    val currentItem = mViewPager2?.currentItem
    val itemCount = mViewPager2?.adapter!!.itemCount
    if (disY > disX) {
      if (currentItem == 0 && endY - startY > 0) {
        parent.requestDisallowInterceptTouchEvent(false)
      } else {
        parent.requestDisallowInterceptTouchEvent(currentItem != itemCount - 1
          || endY - startY >= 0)
      }
    } else if (disX > disY) {
        parent.requestDisallowInterceptTouchEvent(false)
    }
  }
 
}
  1. 使用方法用 ViewPager2Container包裹 ViewPager2就可以
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市外遇,隨后出現(xiàn)的幾起案子注簿,更是在濱河造成了極大的恐慌,老刑警劉巖跳仿,帶你破解...
    沈念sama閱讀 216,744評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件诡渴,死亡現(xiàn)場離奇詭異,居然都是意外死亡菲语,警方通過查閱死者的電腦和手機妄辩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評論 3 392
  • 文/潘曉璐 我一進店門惑灵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人眼耀,你說我怎么就攤上這事英支。” “怎么了哮伟?”我有些...
    開封第一講書人閱讀 163,105評論 0 353
  • 文/不壞的土叔 我叫張陵干花,是天一觀的道長。 經(jīng)常有香客問我楞黄,道長池凄,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,242評論 1 292
  • 正文 為了忘掉前任鬼廓,我火速辦了婚禮修赞,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘桑阶。我一直安慰自己,他們只是感情好勾邦,可當我...
    茶點故事閱讀 67,269評論 6 389
  • 文/花漫 我一把揭開白布蚣录。 她就那樣靜靜地躺著,像睡著了一般眷篇。 火紅的嫁衣襯著肌膚如雪萎河。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,215評論 1 299
  • 那天蕉饼,我揣著相機與錄音虐杯,去河邊找鬼。 笑死昧港,一個胖子當著我的面吹牛擎椰,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播创肥,決...
    沈念sama閱讀 40,096評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼达舒,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了叹侄?” 一聲冷哼從身側(cè)響起巩搏,我...
    開封第一講書人閱讀 38,939評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎趾代,沒想到半個月后贯底,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,354評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡撒强,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,573評論 2 333
  • 正文 我和宋清朗相戀三年禽捆,在試婚紗的時候發(fā)現(xiàn)自己被綠了笙什。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,745評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡睦擂,死狀恐怖得湘,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情顿仇,我是刑警寧澤淘正,帶...
    沈念sama閱讀 35,448評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站臼闻,受9級特大地震影響鸿吆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜述呐,卻給世界環(huán)境...
    茶點故事閱讀 41,048評論 3 327
  • 文/蒙蒙 一惩淳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧乓搬,春花似錦思犁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至江掩,卻和暖如春学辱,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背环形。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評論 1 269
  • 我被黑心中介騙來泰國打工策泣, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人抬吟。 一個月前我還...
    沈念sama閱讀 47,776評論 2 369
  • 正文 我出身青樓萨咕,卻偏偏與公主長得像,于是被迫代替她去往敵國和親火本。 傳聞我的和親對象是個殘疾皇子任洞,可洞房花燭夜當晚...
    茶點故事閱讀 44,652評論 2 354

推薦閱讀更多精彩內(nèi)容