<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>滑动冲突归档 - 日志</title>
	<atom:link href="https://www.log.show/tag/%E6%BB%91%E5%8A%A8%E5%86%B2%E7%AA%81/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.log.show/tag/滑动冲突/</link>
	<description>LOG.SHOW</description>
	<lastBuildDate>Sat, 27 Dec 2025 10:54:49 +0000</lastBuildDate>
	<language>zh-Hans</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0</generator>

<image>
	<url>https://www.log.show/wp-content/uploads/2025/11/cropped-logo-32x32.png</url>
	<title>滑动冲突归档 - 日志</title>
	<link>https://www.log.show/tag/滑动冲突/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Android ViewPager2嵌套滑动冲突</title>
		<link>https://www.log.show/log/android-viewpager2-nested-problem/</link>
					<comments>https://www.log.show/log/android-viewpager2-nested-problem/#respond</comments>
		
		<dc:creator><![CDATA[LOGGER]]></dc:creator>
		<pubDate>Sat, 27 Dec 2025 10:54:19 +0000</pubDate>
				<category><![CDATA[LOG]]></category>
		<category><![CDATA[Android]]></category>
		<category><![CDATA[Android开发]]></category>
		<category><![CDATA[ViewPager2]]></category>
		<category><![CDATA[嵌套滑动]]></category>
		<category><![CDATA[嵌套问题]]></category>
		<category><![CDATA[开发]]></category>
		<category><![CDATA[滑动冲突]]></category>
		<guid isPermaLink="false">https://log.show/?p=484</guid>

					<description><![CDATA[<p>当你使用两个ViewPager2嵌套时，确实是会有恶心的滑动冲突。默认是优先响应父布局的ViewPager2的 [&#8230;]</p>
<p><a href="https://www.log.show/log/android-viewpager2-nested-problem/">Android ViewPager2嵌套滑动冲突</a>最先出现在<a href="https://www.log.show">日志</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">当你使用两个ViewPager2嵌套时，确实是会有恶心的滑动冲突。默认是优先响应父布局的ViewPager2的。</p>



<p class="wp-block-paragraph">而如果你希望它优先响应子布局，当子布局滑动到最后一格的时候才响应父布局，下面是我的解决方案：</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">以下内容，我们假设父布局的ViewPager2存在A,B,C,D四个页面，A页面存在子ViewPager2</p>
</blockquote>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p class="wp-block-paragraph">首先我是用的广播来触发的：</p>



<ol class="wp-block-list">
<li>默认关闭父布局滑动</li>



<li>子布局滑动到最后一格，发送广播通知父布局打开滑动</li>



<li>父布局切换A，关闭父布局滑动</li>
</ol>



<p class="wp-block-paragraph">但是问题很明显，我从B,C,D重新滑到A页面，父布局是已经被通知关闭滑动的，我没办法再次滑动返回B,C,D页面。所以我采用了下面的Google提供的方案。</p>



<h3 class="wp-block-heading">首先新建一个工具 NestedScrollableHost</h3>



<pre class="wp-block-code"><code>
class NestedScrollableHost @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null
) : FrameLayout(context, attrs) {

    private var touchSlop = ViewConfiguration.get(context).scaledTouchSlop
    private var initialX = 0f
    private var initialY = 0f

    // 找到直接子节点中的 ViewPager2
    private val parentViewPager: ViewPager2?
        get() {
            var v: View? = parent as? View
            while (v != null &amp;&amp; v !is ViewPager2) {
                v = v.parent as? View
            }
            return v as? ViewPager2
        }

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

    private fun canChildScroll(orientation: Int, delta: Float): Boolean {
        val direction = -delta.sign.toInt()
        return when (orientation) {
            ViewPager2.ORIENTATION_HORIZONTAL -&gt; child?.canScrollHorizontally(direction) ?: false
            ViewPager2.ORIENTATION_VERTICAL -&gt; child?.canScrollVertically(direction) ?: false
            else -&gt; throw IllegalArgumentException()
        }
    }

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

    private fun handleInterceptTouchEvent(e: MotionEvent) {
        val orientation = parentViewPager?.orientation ?: return

        // 只有当子 View 能够滑动时，才处理拦截逻辑
        if (!canChildScroll(orientation, -1f) &amp;&amp; !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 == ViewPager2.ORIENTATION_HORIZONTAL

            val scaledDx = dx.absoluteValue * if (isVpHorizontal) 1f else .5f
            val scaledDy = dy.absoluteValue * if (isVpHorizontal) .5f else 1f

            if (scaledDx &gt; touchSlop || scaledDy &gt; touchSlop) {
                if (isVpHorizontal == (scaledDy &gt; scaledDx)) {
                    // 滑动方向与 ViewPager2 方向不一致，允许父级拦截
                    parent.requestDisallowInterceptTouchEvent(false)
                } else {
                    // 滑动方向一致，判断子 View 是否能继续滑动
                    if (canChildScroll(orientation, if (isVpHorizontal) dx else dy)) {
                        parent.requestDisallowInterceptTouchEvent(true)
                    } else {
                        parent.requestDisallowInterceptTouchEvent(false)
                    }
                }
            }
        }
    }
}</code></pre>



<h3 class="wp-block-heading">在子布局外嵌套这个View</h3>



<p class="wp-block-paragraph">在默认情况下，你嵌套之后发现父布局的滑动不会被触发，子布局正常了。这个问题的原因很简单，解决方案↓</p>



<h3 class="wp-block-heading">解决滑动监听的问题</h3>



<p class="wp-block-paragraph">ViewPager2实际上是一个RecyclerView，因此它默认打开了越界回弹（OverScroll）。就是那种滑动到边缘的拉伸效果（早期Android版本可能是一个奇怪的发光效果）。当这个效果触发时，系统会认为内层 View 已经处理了这次滑动，从而不会把剩余的位移传递给父布局。</p>



<p class="wp-block-paragraph">所以你需要禁用这个属性：</p>



<pre class="wp-block-code"><code>// 在代码中找到内层的 ViewPager2
val innerViewPager = findViewById&lt;ViewPager2&gt;(R.id.inner_view_pager)

// ViewPager2 内部是一个 RecyclerView，需要找到并设置它
(innerViewPager.getChildAt(0) as? RecyclerView)?.overScrollMode = View.OVER_SCROLL_NEVER</code></pre>



<p class="wp-block-paragraph">当然也有其它的可能，比如你的NestedScrollableHost嵌套层级太深。你需要保证你的NestedScrollableHost下一层就是你的Viewpager2的子视图！</p>



<h3 class="wp-block-heading">其他思路：</h3>



<p class="wp-block-paragraph">这里有一个更粗暴，更清晰的方案，但是可能会有奇怪的问题，不过我认为可以记录下来(与我之前的想法比较接近，可能会有奇怪的问题)：</p>



<pre class="wp-block-code"><code>innerViewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
    override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
        val isAtEnd = position == (innerViewPager.adapter?.itemCount ?: 0) - 1
        val isAtStart = position == 0
        
        if ((isAtEnd &amp;&amp; positionOffsetPixels == 0) || (isAtStart &amp;&amp; positionOffsetPixels == 0)) {
            // 到达边界，允许父类拦截
            innerViewPager.parent.requestDisallowInterceptTouchEvent(false)
        } else {
            // 未到边界，禁止父类拦截
            innerViewPager.parent.requestDisallowInterceptTouchEvent(true)
        }
    }
})</code></pre>
<p><a href="https://www.log.show/log/android-viewpager2-nested-problem/">Android ViewPager2嵌套滑动冲突</a>最先出现在<a href="https://www.log.show">日志</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.log.show/log/android-viewpager2-nested-problem/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
