ViewPager2 嵌套 ViewPager2 解決方案

最近新業(yè)務(wù)要求集币,在ViewPager2 的item中考阱,再放一個(gè)ViewPager2用來展示Banner效果。發(fā)現(xiàn)兩個(gè)嵌套之后鞠苟,內(nèi)部的ViewPager2無法滑動(dòng)羔砾,首先考慮的就是滑動(dòng)沖突,打算重寫ViewPager2偶妖,修改onInterceptTouchEvent方法。卻發(fā)現(xiàn)ViewPager2是final修飾政溃,無法繼承重寫趾访。只能考慮別的方法。

后來董虱,在ViewPager2官方文檔中找到這么不起眼的一小段

32589F60-E0E4-4757-9FCB-E3EA7EA419EE.png

大致意思就是:ViewPager2 嵌套在相同方向的滾動(dòng)View中是不能滾動(dòng)的扼鞋,如果需要滾動(dòng)ViewPager2申鱼,就需要調(diào)用requestDisallowInterceptTouchEvent,才可以接受到滑動(dòng)事件云头。
而且Google還給出了方案:views-widgets-samples/NestedScrollableHost.kt at master · android/views-widgets-samples · GitHub

我把代碼也貼出來捐友,簡單看下原理

/**
 * Layout to wrap a scrollable component inside a ViewPager2. Provided as a solution to the problem
 * where pages of ViewPager2 have nested scrollable elements that scroll in the same direction as
 * ViewPager2. The scrollable element needs to be the immediate and only child of this host layout.
 *
 * This solution has limitations when using multiple levels of nested scrollable elements
 * (e.g. a horizontal RecyclerView in a vertical RecyclerView in a horizontal ViewPager2).
 */

class NestedScrollableHost : FrameLayout {

    constructor(context: Context) : super(context)
    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)

    private var touchSlop = 0
    private var initialX = 0f
    private var initialY = 0f

    private val parentViewPager: ViewPager2?
        get() {
            var v: View? = parent as? View
            while (v != null && v !is ViewPager2) {
                v = v.parent as? View
            }
            return v as? ViewPager2
        }

    private val child: View? get() = if (childCount > 0) getChildAt(0) else null

    init {
        touchSlop = ViewConfiguration.get(context).scaledTouchSlop
    }

    private fun canChildScroll(orientation: Int, delta: Float): Boolean {
        val direction = -delta.sign.toInt()
        return when (orientation) {
            0 -> child?.canScrollHorizontally(direction) ?: false
            1 -> child?.canScrollVertically(direction) ?: false
            else -> throw IllegalArgumentException()
        }
    }

    override fun onInterceptTouchEvent(e: MotionEvent): Boolean {
        handleInterceptTouchEvent(e)
        return super.onInterceptTouchEvent(e)
    }

    private fun handleInterceptTouchEvent(e: MotionEvent) {
        val orientation = parentViewPager?.orientation ?: return
        // Early return if child can't scroll in same direction as parent
        if (!canChildScroll(orientation, -1f) && !canChildScroll(orientation, 1f)) {
            return
        }

        if (e.action == MotionEvent.ACTION_DOWN) {
            initialX = e.x
            initialY = e.y
            parent.requestDisallowInterceptTouchEvent(true)
        } else if (e.action == MotionEvent.ACTION_MOVE) {
            val dx = e.x - initialX
            val dy = e.y - initialY
            val isVpHorizontal = orientation == ORIENTATION_HORIZONTAL
            // assuming ViewPager2 touch-slop is 2x touch-slop of child
            val scaledDx = dx.absoluteValue * if (isVpHorizontal) .5f else 1f
            val scaledDy = dy.absoluteValue * if (isVpHorizontal) 1f else .5f

            if (scaledDx > touchSlop || scaledDy > touchSlop) {

                if (isVpHorizontal == (scaledDy > scaledDx)) {
                    // Gesture is perpendicular, allow all parents to intercept
                    parent.requestDisallowInterceptTouchEvent(false)
                } else {
                    // Gesture is parallel, query child if movement in that direction is possible
                    if (canChildScroll(orientation, if (isVpHorizontal) dx else dy)) {
                        // Child can scroll, disallow all parents to intercept
                        parent.requestDisallowInterceptTouchEvent(true)
                    } else {
                        // Child cannot scroll, allow all parents to intercept
                        parent.requestDisallowInterceptTouchEvent(false)
                    }
                }
            }
        }
    }
}

用法也很簡單,用NestedScrollableHost把內(nèi)部ViewPager2包裹起來就可以啦溃槐。

我們主動(dòng)包裹住內(nèi)部ViewPager2后匣砖,就代表著需要處理事件了,所以在onInterceptTouchEvent函數(shù)中昏滴,如果接受到了DOWN事件猴鲫,就需要調(diào)用requestDisallowInterceptTouchEvent通知外層的ViewPager2不要攔截事件,讓我們的Host來處理滑動(dòng)事件谣殊。

等到MOVE事件進(jìn)來后拂共,判斷一下能不能順著手勢滑動(dòng)內(nèi)部的ViewPager2?
不能就不給內(nèi)部ViewPager2后續(xù)事件了(主動(dòng)通知外部ViewPager2攔截事件)姻几。

Host的作用就相當(dāng)于是一個(gè)開關(guān)在兩個(gè)ViewPager2之間宜狐。當(dāng)內(nèi)部的還可以滑動(dòng),就允許事件傳遞下去蛇捌,當(dāng)內(nèi)部無法在手勢方向滑動(dòng)抚恒,就通知外部View進(jìn)行事件攔截。

完美解決了ViewPager2的嵌套問題豁陆。

按照這一思想柑爸,對(duì)于好多滑動(dòng)沖突的問題,都可以不用繼承盒音,直接寫一個(gè)Host來解決嵌套問題

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末表鳍,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子祥诽,更是在濱河造成了極大的恐慌譬圣,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,681評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件雄坪,死亡現(xiàn)場離奇詭異厘熟,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)维哈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門绳姨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人阔挠,你說我怎么就攤上這事飘庄。” “怎么了购撼?”我有些...
    開封第一講書人閱讀 169,421評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵跪削,是天一觀的道長谴仙。 經(jīng)常有香客問我,道長碾盐,這世上最難降的妖魔是什么晃跺? 我笑而不...
    開封第一講書人閱讀 60,114評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮毫玖,結(jié)果婚禮上掀虎,老公的妹妹穿的比我還像新娘。我一直安慰自己孕豹,他們只是感情好涩盾,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著励背,像睡著了一般春霍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上叶眉,一...
    開封第一講書人閱讀 52,713評(píng)論 1 312
  • 那天址儒,我揣著相機(jī)與錄音,去河邊找鬼衅疙。 笑死莲趣,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的饱溢。 我是一名探鬼主播喧伞,決...
    沈念sama閱讀 41,170評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼绩郎!你這毒婦竟也來了潘鲫?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,116評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤肋杖,失蹤者是張志新(化名)和其女友劉穎溉仑,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體状植,經(jīng)...
    沈念sama閱讀 46,651評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡浊竟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了津畸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片振定。...
    茶點(diǎn)故事閱讀 40,865評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖肉拓,靈堂內(nèi)的尸體忽然破棺而出后频,到底是詐尸還是另有隱情,我是刑警寧澤帝簇,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布徘郭,位于F島的核電站,受9級(jí)特大地震影響丧肴,放射性物質(zhì)發(fā)生泄漏残揉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評(píng)論 3 336
  • 文/蒙蒙 一芋浮、第九天 我趴在偏房一處隱蔽的房頂上張望抱环。 院中可真熱鬧,春花似錦纸巷、人聲如沸镇草。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽梯啤。三九已至,卻和暖如春存哲,著一層夾襖步出監(jiān)牢的瞬間因宇,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評(píng)論 1 274
  • 我被黑心中介騙來泰國打工祟偷, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留察滑,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,299評(píng)論 3 379
  • 正文 我出身青樓修肠,卻偏偏與公主長得像贺辰,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子嵌施,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評(píng)論 2 361

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