CoordinatorLayout 中的 ViewPager 缩小意外

15
在我的Android应用程序中,使用一个包含TabLayoutViewPagerToolbar布局,并将它们组合在一个CoordinatorLayout中。在ViewPager的第一页中,使用一个RecyclerView来展示CardView项。我的问题是,ViewPager会被调整大小,导致CardView列表项被裁剪。主要布局基本上如下所示:
<android.support.design.widget.CoordinatorLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <android.support.design.widget.AppBarLayout
        android:layout_height="wrap_content"
        android:layout_width="match_parent" >

        <android.support.v7.widget.Toolbar
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

        <android.support.design.widget.TabLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

    </android.support.design.widget.AppBarLayout>

    <android.support.v4.view.ViewPager 
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</android.support.design.widget.CoordinatorLayout>

我的ViewPager提供的第一个片段如下:

<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <android.support.v7.widget.RecyclerView
        android:scrollbars="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="8dp"/>

</FrameLayout>

这将呈现出类似于下面的内容:

enter image description here

当我在布局中点击按钮时,我使用startActivityForResult来调用另一个活动,在返回我的活动时,有时候我的列表突然被裁剪,只有第一项的一半可见:

enter image description here

ViewPager中水平滑动到另一个页面,然后返回,问题就消失了,因此似乎没有正确触发重新布局。但是按下HOME键然后恢复我的活动并不能解决问题。请注意,即使我没有以任何方式修改布局,只是从startActivityForResult调用中返回,也会发生这种情况。是的,它只会有时发生...而且我没有运行可以解释明显随机行为的后台线程。
起初我以为是RecyclerView缩小了,但使用HierarchyViewer我能够发现实际上是整个ViewPager缩小到了原来的一半高度。

我尝试了各种方法来解决这个问题,包括在整个视图层次结构上调用invalidate()requestLayout(),但似乎没有什么帮助(尽管滑动到另一页然后再返回可以解决它)。此外,这些都不是我想采取的解决方案... 然后我尝试将我的ViewPager高度更改为wrap_content,这确实解决了这个特定的问题;在返回到我的活动后,我的RecyclerView中的第一个项目永远不会被裁剪,并且我可以向下滚动到其他项目。然而,现在我的列表中最后一个项目总是被裁剪,如下面的截图所示,其中列表已经滚动到底部:

enter image description here

由于我现在已经到了一个不太理解发生了什么的地步,所以我需要帮助。对我来说,match_parent很有意义,但我应该如何思考呢?使用match_parent时,我的视图被裁剪了,这是有合理原因的吗?还是我实际上遇到了ViewPager、RecyclerView和/或CoordinatorLayout中的错误?如何确保我的ViewPager始终填充AppBar下方的整个屏幕区域,并且我的RecyclerView可以垂直滚动以正确渲染所有CardView列表项?

match_parent 可以工作,因为布局行为将在布局子项被布局时将其放置在 AppBarLayout 下方。 - Nikola Despotoski
感谢@NikolaDespotoski,但这并没有解决我的任何问题。使用match_parent时,我遇到了一个奇怪的问题,即从子活动返回后,viewpager的大小会发生变化,这是主要问题。 - JHH
也许您可以创建一个 MCVE。似乎很难复现这个问题。 - tachyonflux
尝试将ViewPager的layout:height保持为0dp,并添加layout:weight1 - JavaGhost
@JavaGhost layout_weight 属性适用于 LinearLayout,而不是 CoordinatorLayout,后者是扩展自 FrameLayout 的。这并不能起到帮助作用。 - JHH
显示剩余4条评论
3个回答

4
原来这几乎可以肯定是CoordinatorLayout中的一个错误,或者更有可能是AppBarLayout$ScrollingViewBehavior中的错误。在努力创建一个MCVE的过程中,我发现我的子activity拥有一个IME,这导致了ViewPager的收缩 - 当onActivityResult结束后恢复我的activity时,由于IME减少了屏幕的实际空间,ViewPager被缩小了,但尽管IME不再显示,而且CoordinatorLayout确实扩展了,但它仍然没有扩展回去。
在调试和跟踪CoordinatorLayoutViewPageronLayoutonMeasure方法之后,我现在相当确定CoordinatorLayout没有正确地将大小变化传播给其子元素。
我发现只有当调用足够延迟(10毫秒从未起作用,100毫秒大多时间可以)时,我才能“修复”问题,即在我的ViewPager上调用requestLayout
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    mHandler.postDelayed(new Runnable() {
        @Override
        public void run() {
            mViewPager.requestLayout();
        }
    }, 100);
}

显然这并不是一个健壮的解决方案,但经过进一步调查发现我可能甚至不需要使用CoordinatorLayout,因为我没有任何高级滚动行为。所以我的解决方案将是简单地选择LinearLayoutRelativeLayout作为我的根视图组。简单明了,没有必要复杂化。

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    <android.support.design.widget.AppBarLayout
        android:layout_height="wrap_content"
        android:layout_width="match_parent" >

        <android.support.v7.widget.Toolbar
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

        <android.support.design.widget.TabLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

    </android.support.design.widget.AppBarLayout>

    <android.support.v4.view.ViewPager 
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>

我会尝试将此简化为一个简单的示例,并向谷歌报告错误。

至于我应该用什么高度来设置我的 ViewPager,我最初使用的 match_parent 仍然似乎是合理的。事实上使用wrap_content解决了问题(但引起了其他问题),这可能只是由于bug造成的不一致性。


事实证明,这个 bug 已经在支持库中修复了。感谢 @marmor 在 https://dev59.com/dFwY5IYBdhLWcg3wCD-W#33275205 中指出这一点。 - JHH

3

1
感谢您指出这些其他问题,它们确实看起来相同。昨天我升级了支持库,但没有效果,但可能我的设置仍然有问题,因为今天我从头开始下载了Android Studio 1.4,获取了一个新的SDK并重新加载了所有依赖项。支持库现在是@ 23.1。使用此设置,我不再能够重现该问题。我将标记您的答案为已接受的答案,因为它实际上解决了问题(我的答案仅使用了解决方法)。享受奖励。;) - JHH

0
在你的片段中,只需删除frameLayout并将recyclerView设置为父级...希望它能正常工作:
 <android.support.v7.widget.RecyclerView
        android:scrollbars="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="8dp">


<android.support.v7.widget.RecyclerView/>

谢谢你的建议,但不,这并没有起作用。正如我所说,当我的活动恢复时,整个ViewPager会意外地被调整大小,即使它设置为match_parent - 更改其中一个用作子页面的片段的布局似乎并不能帮助解决问题。然而,我并不排除在应用兼容库中存在错误,因此可能会出现一个看似没有意义的更改间接修复问题,但不幸的是它并没有。你提出这个建议的想法是什么? - JHH
为什么不尝试用另一个片段替换你正在使用的片段...或者如果你正在调用一个活动,请先完成当前活动,然后从被调用的活动中按返回按钮调用它... - Chordin4tion
嗯,那可能行得通,但我并不真正想要采取一些像放弃基本功能startActivityForResult这样的冒险解决方案。虽然它可能有效,因为我的问题显然与时间相关,但它会让我的代码变得混乱,我需要处理更多状态,并且性能可能会受到一些影响。 - JHH

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接