在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个回答

4
使用下面的自定义类来解决您的问题。不要忘记在页面更改监听器上调用onRefresh()方法。
public class ContentWrappingViewPager extends ViewPager
{
    private int width = 0;
    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;
        width = widthMeasureSpec;
        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);
    }

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

            int heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
            ViewGroup.LayoutParams layoutParams = this.getLayoutParams();
            layoutParams.height = heightMeasureSpec;
            this.setLayoutParams(layoutParams);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

4

我从主布局中删除了 NestedScrollView,并将其作为父级容器插入到所有应该在 ViewPager 中加载的 Fragment 布局中,这样就解决了我的问题。


3

我遇到了类似的问题(但没有使用 CollapsingToolbarLayout,只是在 AppBarLayout 中使用简单的 Toolbar + TabLayout)。我希望当滚动一个 NestedScrollView 中的内容时,ViewPager 以外的 Toolbar 滚动隐藏。

我的布局大致如下:

<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.widget.NestedScrollView
                android:id="@+id/container"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:fillViewport="true"
                android:layout_gravity="fill_vertical"
                app:layout_behavior="@string/appbar_scrolling_view_behavior">

                <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.v4.view.ViewPager>
         </android.support.v4.widget.NestedScrollView>

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

这个布局原本没有按照预期工作,但是我通过简单地摆脱NestedScrollView并改用LinearLayout来解决了它。 它在Android支持库v23.1.0上立即为我工作。以下是最终的布局:

<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>

         <LinearLayout
            android:orientation="vertical"
            android:id="@+id/container"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginBottom="4dp"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">

                <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.v4.view.ViewPager>
         </LinearLayout>

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

附注: 这实际上是我项目中的两个布局文件。我将它们复制并粘贴到这里,并尝试将它们构造成一个单一文件的样子。如果我在复制粘贴时出错了,我很抱歉,请让我知道,以便我可以修正它。


3
只需在 NestedScrollView 的 XML 标签中添加 android:fillViewport="true" 即可。
<androidx.core.widget.NestedScrollView
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:fillViewport="true"
       app:layout_behavior="@string/appbar_scrolling_view_behavior"
       >
       <androidx.viewpager.widget.ViewPager
           android:id="@+id/viewpager"
           android:layout_width="match_parent"
           android:layout_height="match_parent"/>
   </androidx.core.widget.NestedScrollView>

1
以下内容应确保视图分页器的正确高度。该片段是视图分页器提供的第一个片段。
<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="match_parent"
        android:orientation="horizontal">

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

        <fragment
            class="com.mydomain.MyViewPagerFragment"
            android:id="@+id/myFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:visibility="gone"/>

    </LinearLayout>

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

1
我有同样的愿望,将一个ViewPager嵌套在一个NestedScrollView中。但这对我也没有用。在我的情况下,ViewPager在一个Fragment中,因此我可以根据与抽屉的交互来切换内容。当然,AppBar、collapse等所有支持库的新功能都必须正常工作。
如果用户在分页器中垂直滚动页面,我希望其他页面也能以相同的方式滚动,给用户整体上的印象是分页器本身滚动了。
我的解决方法是不再将ViewPager嵌套在NestedScrollView中,而是将包含片段的内容嵌套在NestedScrollView中。
然后,在某个片段中发生滚动时,我会通知容器新的滚动位置,并将其存储。
最后,在检测到从分页器向左或向右滑动(使用addOnPageChangeListener查找拖动状态)时,我会通知目标左侧或右侧片段它必须滚动到哪个位置(基于容器对其的了解),以对齐我来自的片段。

所以基本上你编写了自己的ViewPager实现?你能分享一下这方面的代码吗? - joseph
我做过类似的事情(如果我理解正确的话),就是在您的CoordinatorLayout中进行线性布局,其中包含TabLayout(如果需要)和ViewPager,而不是嵌套ScrollView。然后片段本身将嵌套ScrollView作为其根元素。这对我很有效,但我更喜欢只有一个NestedScrollView,而不是在每个片段中重复它。 - Chris

0

我知道一个可行的解决方案:您可以使用Activity,带有CoordinatorLayout和使用FrameLayout作为容器。然后,使用片段管理器将片段放入容器中。例如我的代码:

<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:orientation="vertical">

<android.support.design.widget.AppBarLayout
    android:id="@+id/appbar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:elevation="0dp">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        app:layout_scrollFlags="scroll|enterAlways"
        android:layout_height="@dimen/toolbar_height"
        android:background="?attr/colorPrimary"
        android:gravity="center">

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


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

<FrameLayout
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">

</FrameLayout>

和Fragment:

<LinearLayout 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:orientation="vertical"
tools:context="com.improvemdia.inmyroom.MainFragment">

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

然后使用FragmentManager

getSupportFragmentManager().beginTransaction()
            .replace(R.id.container, new MainFragment())
            .commit();

我认为Fragment布局中的LinearLayout在这里是不必要的。请看我的答案,我在Activity布局中使用了LinearLayout而不是FrameLayout,我的Fragment根布局是ViewPager(没有包装布局)。 - Cramps

0

你只需要在xml文件中删除NestedScrollView,然后在类中添加以下代码即可

yourGridView.setNestedScrollingEnabled(true);


0

我尝试了很多方法但都没有奏效。我参考了this,我的问题得到了解决。


-1
经过数小时尝试上述所有建议后,我终于让它工作了。关键是将NestedScrollView放置在PageViewer内,并为两个视图设置layout_behavior'@string/appbar_scrolling_view_behavior'。最终布局如下:
<CoordinatorLayout>
    <ViewPager app:layout_behavior="@string/appbar_scrolling_view_behavior">
        <NestedScrollView fillViewport="true" app:layout_behavior="@string/appbar_scrolling_view_behavior">
            <Content>...</Content>
        </NestedScrollView>
    </ViewPager>
    <AppBarLayout>...</AppBarLayout>
</CoordinatorLayout>

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