在NestedScrollView中使用ViewPager

123

我需要创建一个类似于 Google Newsstand 的界面,它是在可折叠的标题栏(纵向滚动)上使用ViewPager(横向滚动)。我的一个要求是使用在2015年Google IO上推出的新设计支持库Design Support Library。 (http://android-developers.blogspot.ca/2015/05/android-design-support-library.html)

在Chris Banes的示例基础上(https://github.com/chrisbanes/cheesesquare),我已经实现了可折叠效果,但是只能使用基本的LinearLayout而没有水平滚动。

我试图用ViewPager替换LinearLayout,但是只得到一个空白屏幕。我尝试调整宽度、权重和各种视图组,但是仍然是空白屏幕。看起来ViewPager 和NestedScrollView不兼容。

我试图通过使用HorizontalScrollView进行一些变通:它可以工作,但是我失去了PagerTitleStrip的功能和对单个面板的聚焦(我无法在两个面板之间停止水平滚动)。

现在我没有更多的想法,如果有人能帮助我找到解决方案...

谢谢

这是我的最新布局文件:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="@dimen/header_height"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <android.support.design.widget.CollapsingToolbarLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:contentScrim="?attr/colorPrimary"
            app:expandedTitleMarginEnd="64dp"
            app:expandedTitleMarginStart="48dp"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

            <include
                layout="@layout/part_header"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:layout_collapseMode="parallax"/>

            <android.support.v7.widget.Toolbar
                android:id="@+id/activity_main_toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_collapseMode="pin"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>

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

    <android.support.v4.widget.NestedScrollView
        android:id="@+id/activity_main_nestedscrollview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fillViewport="true"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <android.support.v4.view.ViewPager
            android:id="@+id/activity_main_viewpager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="#FFA0"/>


    </android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>

你尝试过将 ViewPager 包裹在 LinearLayout 中吗?只是好奇这样做是否能正确渲染 ViewPager - CzarMatt
是的,我尝试过了,但结果仍然相同。我使用了许多组件进行了许多测试,但无论层次结构如何,ViewPager都不会在NestedScrollView中呈现。 - Kyso84
我刚刚创建了一个测试应用程序,其中包含一个ViewPager和一个NestedScrollView作为唯一的布局 - 看起来运行良好。我还成功地将其放入了一个DrawerLayout中。也许你的代码中还有其他方面阻止了你想要的功能。能否分享更多源代码? - CzarMatt
哇,你能左右滑动和上下滑动吗? 我只是使用了FragmentPagerAdapter来在我的分页器中显示片段。你能分享一下你的示例代码吗? - Kyso84
我在这里粘贴了一个简单的布局示例:http://pastebin.com/jXPw6mtf 编辑:您可以在“ViewPager”中加载任何片段。我能够上下滚动视图,以及左右滚动“ViewPager”。 - CzarMatt
显示剩余3条评论
21个回答

134

我曾经遇到过同样的问题。如果你想修改xml文件,可以通过使用 setFillViewport(true) 方法来解决问题。

<android.support.v4.widget.NestedScrollView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:id="@+id/nest_scrollview"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="mio.kon.yyb.nestedscrollviewbug.ScrollingActivity"
    tools:showIn="@layout/activity_scrolling">


        <android.support.v4.view.ViewPager
            android:id="@+id/viewpager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

</android.support.v4.widget.NestedScrollView>

如果您有一个 Activity 并在程序中进行更改,那么应该按照以下方式进行。
  NestedScrollView scrollView = (NestedScrollView) findViewById (R.id.nest_scrollview);
    scrollView.setFillViewport (true);

enter image description here


34
请注意,这也可以在 XML 中设置,只需在 NestedScrollView 对象中添加 android:fillViewport="true" 即可。 文档 将此属性描述为“定义滚动视图是否应将其内容拉伸以填充视口”。 - Krøllebølle
9
这并没有解决占位片段中的滚动问题。 - Atieh
3
谢谢!我在NestedScrollView上设置了android:fillViewport="true",并在fragment内的listView上设置了android:nestedScrollingEnabled="true"... 修复了滚动问题。 - radubogdan
@Krøllebølle NestedScrollView没有这个属性,你必须通过编程来设置它。 - user1443317
这对我有用。当我将viewpager的高度设置为NestedScrollView中的match_parent时,它不会显示视图,我遇到了问题。 - Waheed Nazir

74

好的,我用 ViewPagerNestedScrollView 制作了一个小演示。我遇到的问题是 ViewPagerListView 的高度问题。因此,我对 ListViewViewPager 的高度测量进行了一些修改。

如果有人想查看代码,请点击链接:https://github.com/TheLittleNaruto/SupportDesignExample/

输出:

输入图像描述


如果碎片没有listview,则不起作用,即使是对于listview,如果所有碎片的项目数量计数相同,则可以正常工作。 - Pankaj Arora
最好使用RecyclerView。 - CaptRisky
3
我可能有点晚了,但我今天正在处理这个问题,并且通过在ViewPager和每个Fragment的父视图上添加"app:layout_behavior = "@string/appbar_scrolling_view_behavior",已经(大体上)使它运行起来了。 - Graeme
1
毫无疑问,这个方法是可行的,但是这不是把问题搞复杂了吗?我通过摆脱NestedScrollView,改用LinearLayout来解决这个问题。如果ViewPager中的内容需要滚动,那么使用NestedScrollView相比LinearLayout有什么好处呢?如果有错误请指正,谢谢。 - Cramps
1
非常感谢!我已经快要抓狂了,你的WrapContentHeightViewPager 真是太棒了。我欠你一个人情! - Mavamaarten
显示剩余8条评论

52
在您的NestedScrollView中添加android:fillViewport="true"。期望到此为止。

3
它解决了问题,但ViewPager的高度将会是match_parent(占据整个页面高度)。 - Manukumar

31

不要把ViewPager放在NestedScrollView里面,而是采用相反的方式。

将NestedScrollView放在ViewPager的子视图中,即Fragment的布局。如果Fragment的列表布局非常简单,你很可能甚至不需要使用NestedScrollView。

示例布局

Activity布局:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/app_bar"
        android:layout_width="match_parent"
        android:layout_height="@dimen/app_bar_height"
        android:fitsSystemWindows="true"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/toolbar_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fitsSystemWindows="true"
            app:contentScrim="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:minHeight="@dimen/app_bar_height"
                android:scaleType="centerCrop"
                android:src="@drawable/my_bg"
                app:layout_collapseMode="parallax"
                app:layout_collapseParallaxMultiplier="0.8"
                app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed" />

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_collapseMode="pin"
                app:popupTheme="@style/AppTheme.PopupOverlay" />

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

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

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

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

        <android.support.v4.view.ViewPager
            android:id="@+id/viewPager"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1" />
    </LinearLayout>

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

如果您不需要TabLayout,可以放弃LinearLayout并将ViewPager设置为独立的。但请确保在ViewPager属性中使用app:layout_behavior="@string/appbar_scrolling_view_behavior"

简单片段的布局:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/recycler_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

复杂片段的布局:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.NestedScrollView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillViewport="true"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Hello World"/>
    </RelativeLayout>
</android.support.v4.widget.NestedScrollView>

示例应用程序:CollapsingToolbarPoc

输入图像描述


这就是让我成功的那个。谢谢! - rjr-apps
嗨Ino,这似乎是有效的,但当我在TabLayout上方添加一个视图时,它就会出问题。你能给点帮助吗? - hushed_voice
你救了我的一天。 :) - androidXP

21
我遇到了同样的问题。 当把NestedScrollView放在ViewPager的外面时,它不起作用。 但是把NestedScrollView放在我在ViewPager中加载的片段布局内部就可以正常工作。
<p></p>

<pre><code><android.support.v4.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">
        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
            android:id="@id/fragment_detail"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="fill_vertical"
            android:orientation="vertical">
    ...Content...
    </LinearLayout>
</android.support.v4.widget.NestedScrollView>


如果内容是一个带有另一个NestedScrollView的协调布局,它能正常工作吗?父CoordinatorLayout会监听那里的滚动事件吗? - Atieh
这对我有用。我将NestedScrollView添加到ViewPager的每个项目中,而不是ViewPager本身。谢谢。 - jerogaren
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Palak Darji
嵌套的ScrollView对RecyclerView的性能影响很大。 - Dev4Life

17

您可以尝试这样做,ViewPager 可以正常工作,但 ViewPager 的高度是固定的。

我仍在寻找更好的解决方案……,因此请告诉我任何可以改进它的地方,谢谢。

<android.support.v4.widget.NestedScrollView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:minHeight="800dp"
        android:orientation="vertical">

        <android.support.v4.view.ViewPager
            android:id="@+id/pager"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"/>
    </LinearLayout>
</android.support.v4.widget.NestedScrollView>

你尝试过将它封装到“android.support.design.widget.CoordinatorLayout”和可折叠的工具栏中吗? - Kyso84
你的解决方案似乎证明了问题出在高度上。@Paul,有什么改进吗? - jasxir
3
这个对我起了作用,但我还需要在嵌套视图上调用setFillViewport(true)。 - Akah

13

我遇到了同样的问题,只需进行一些更改就可以解决。 ViewPagerNestedScrollView 中不起作用。将您的 ViewPager 直接放置在您的 CoordinatorLayout 中。然后,每个 ViewPager 的 Fragment 的内容应该被包含在 NestedScrollView 中。


即使这是一个使用协调布局(CoordinatorLayout)和另一个嵌套滚动视图(NestedScrollView)的程序,我也可以对其进行翻译吗? - Atieh
1
这种方式无法加载片段列表视图数据! - Palak Darji

9

只需将此行代码放入NestedScrollView中即可。

android:fillViewport="true"

1
非常感谢。如果我想通过直接触摸CollapsingToolbarLayout来滚动,该怎么办? - Pratik Saluja
2
将 "app:layout_behavior="@string/appbar_scrolling_view_behavior"" 添加到您的NestedScrollView中。 - Mami

6
您不需要使用NestedScrollView来实现视差效果。试试像这样的方式:
您不需要使用NestedScrollView来实现视差效果。尝试使用以下代码:
<android.support.design.widget.CoordinatorLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:animateLayoutChanges="true">

        <android.support.v7.widget.Toolbar
            android:id="@+id/my_toolbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_scrollFlags="scroll|enterAlways"/>

        <android.support.design.widget.TabLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/my_tab_layout"
            android:fillViewport="false"
            app:tabMode="fixed"/>
     </android.support.design.widget.AppBarLayout>

            <android.support.v4.view.ViewPager
                android:id="@+id/my_view_pager"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

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

3
只有当你所有的ViewPager片段都是RecyclerViewNestedScrollView时,这个方法才有效。如果它们不是(例如ListView),那么在Lollipop及以上版本中的解决方法是调用listView.setNestedScrollingEnabled(true) - AndyDeveloper
终于有东西可以正常工作了,而不需要添加额外的滚动容器!顺便说一下,你也可以在xml中设置setNestedScrollingEnabled(true):android:nestedScrollingEnabled="true"。 - Analizer
1
如果我在一个选项卡中折叠工具栏,然后切换到另一个选项卡并将工具栏拉回来,它就是空的。必须在非折叠状态下切换选项卡才能再次获取它。如此接近,却又如此遥远 :( - me--

6
我有一个布局,其中带有应用程序工具栏,并在其下方使用了NestedScrollView,在NestedScrollView内部是一个LinearLayout,在该LinearLayout下方是ViewPager。想法是NestedScrollView内的LinearLayout是固定的 - 您可以在ViewPager视图之间左右滑动,但是此内容至少不会在水平方向上移动。由于有嵌套滚动视图,您仍应能够垂直滚动所有内容。这就是我的想法。因此,我将嵌套的NestedScrollView高度设置为match_parent,填充viewport,然后将所有子布局/ViewPager设置为wrap_content。
在我的脑海中,这很正确。但它没有起作用 - ViewPager允许我向左/向右滑动,但无论viewpager页面内容是否推到当前屏幕的底部以下,都无法进行垂直滚动。事实证明,这基本上是因为ViewPager不尊重其各个页面上的wrap_content。
我通过对ViewPager进行子类化来解决了这个问题,使其根据当前选定的项目更改高度,从而强制其表现得像layout_height =“wrap_content”:
public class ContentWrappingViewPager extends ViewPager {

    public ContentWrappingViewPager(Context context) {
        super(context);
    }

    public ContentWrappingViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        int height = 0;
        if (getChildCount() > getCurrentItem()) {
            View child = getChildAt(getCurrentItem());
            child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
            int h = child.getMeasuredHeight();
            if(h > height) height = h;
        }

        heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}

这个解决方案的灵感来自于这个回答。对我很有效!


但是滚动视图自动滚回顶部了?如何解决这个问题? - Vivek Bhardwaj
谢谢你,你救了我。 - Bharat Sonawane

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