Android CoordinatorLayout + AppbarLayout + Viewpager 总是滚动
Android CoordinatorLayout + AppbarLayout + Viewpager always scrolling
我有一个经典的布局,顶部有一个 ToolBar,下面有一个 TabLayout,还有一个 ViewPager 从 TabLayout 切换标签。当 ViewPager 中的内容可滚动时,ToolBar 应该滚动到看不见的地方,而 TabLayout 应该跟随并在到达顶部时保持不变。
这一切在我当前的代码中都很好,除了 ToolBar 总是可滚动的,无论 ViewPager 内容的大小如何。请参阅下面的代码。关于如何解决这个问题有什么好主意吗?
<?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:orientation="vertical">
<android.support.design.widget.AppBarLayout
android:id="@+id/app_bar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/primary"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.ToolBar"
app:layout_scrollFlags="scroll|enterAlways" />
<android.support.design.widget.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:scrollbars="horizontal"
app:tabIndicatorColor="@color/black_text" />
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="@+id/tabs_activity_view_pager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</android.support.design.widget.CoordinatorLayout>
编辑:
我可以看到viewPager 的高度与整个根视图的高度相同。这可能是有意为之,因为 appbar_scrolling_view_behavior 似乎确实添加了顶部和底部偏移量。然而,它看起来确实很奇怪,因为它会导致工具栏和标签栏始终滚动。
尝试在 TabLayout 上添加这些属性:
app:layout_collapseMode="pin"
app:tabMode="fixed"
AppBarLayout 上的这个:
android:fitsSystemWindows="true"
* 更新 *
我试过了,但这还不够,因为工具栏仍然可以滚动。
解决方案是对 ViewPager(及其内容)做一些逻辑。
从 xml 布局文件中删除工具栏 scroll_flag 属性。
您必须执行一些 java 代码来检查 ViewPager 内容高度是否 > 然后是 screenHeight - (toolbar + tabBar)。如果为真,则以编程方式将 scroll_flag 设置为:
LayoutParams params;
params = // get layout params from your toolbar
params.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL
| AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS);
// set params
toolbar.setLayoutParams(params);
我建议你试试 this 示例。
此布局与示例中的布局相似。
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:layout_scrollFlags="scroll|enterAlways" />
<android.support.design.widget.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="@dimen/fab_margin"
android:src="@drawable/ic_done" />
</android.support.design.widget.CoordinatorLayout>
ViewPager
高度应该是 match_parent
而不是 wrap_content
。
基于其他示例、我自己的代码以及 appbar_scrolling_view_behavior 的(有点乱)源代码:
public boolean onDependentViewChanged(CoordinatorLayout parent, View child,
View dependency) {
final CoordinatorLayout.Behavior behavior =
((CoordinatorLayout.LayoutParams) dependency.getLayoutParams()).getBehavior();
if (behavior instanceof Behavior) {
// Offset the child so that it is below the app-bar (with any overlap)
final int appBarOffset = ((Behavior) behavior)
.getTopBottomOffsetForScrollingSibling();
final int expandedMax = dependency.getHeight() - mOverlayTop;
final int collapsedMin = parent.getHeight() - child.getHeight();
if (mOverlayTop != 0 && dependency instanceof AppBarLayout) {
// If we have an overlap top, and the dependency is an AppBarLayout, we control
// the offset ourselves based on the appbar's scroll progress. This is so that
// the scroll happens sequentially rather than linearly
final int scrollRange = ((AppBarLayout) dependency).getTotalScrollRange();
setTopAndBottomOffset(AnimationUtils.lerp(expandedMax, collapsedMin,
Math.abs(appBarOffset) / (float) scrollRange));
} else {
setTopAndBottomOffset(dependency.getHeight() - mOverlayTop + appBarOffset);
}
}
return false;
}
我正在以解释问题的方式阅读这篇文章,因为这是此代码的预期行为。
我认为我们需要编写自己的滚动行为,专门针对 RecyclerView,
我刚刚遇到了同样的问题。
解决方法很简单,设置你的viewpager高度
android:layout_height="wrap_content"
简单的解决方法是
- 使用相对布局包装您的应用栏布局和页面查看器。
- 给你的 appbar 布局一些 id
- 在页面视图中设置 android:layout_below="Your_appbar_layout"
例如:
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:id="@+id/your_appBar_ID"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/primary"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.ToolBar"
app:layout_scrollFlags="scroll|enterAlways" />
<android.support.design.widget.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:scrollbars="horizontal"
app:tabIndicatorColor="@color/black_text" />
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="@+id/tabs_activity_view_pager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/your_appBar_ID"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</RelativeLayout>
使用 ListView 作为 ViewPager 的数据?如果是这样,你需要listView.setNestedScrollingEnabled(true);
我已经解决了问题,尝试了示例 Google 模板并发现
app:layout_behavior="@string/appbar_scrolling_view_behavior"
行必须添加到视图寻呼机属性中 xml。它解决了我的问题。
我有一个经典的布局,顶部有一个 ToolBar,下面有一个 TabLayout,还有一个 ViewPager 从 TabLayout 切换标签。当 ViewPager 中的内容可滚动时,ToolBar 应该滚动到看不见的地方,而 TabLayout 应该跟随并在到达顶部时保持不变。
这一切在我当前的代码中都很好,除了 ToolBar 总是可滚动的,无论 ViewPager 内容的大小如何。请参阅下面的代码。关于如何解决这个问题有什么好主意吗?
<?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:orientation="vertical">
<android.support.design.widget.AppBarLayout
android:id="@+id/app_bar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/primary"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.ToolBar"
app:layout_scrollFlags="scroll|enterAlways" />
<android.support.design.widget.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:scrollbars="horizontal"
app:tabIndicatorColor="@color/black_text" />
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="@+id/tabs_activity_view_pager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</android.support.design.widget.CoordinatorLayout>
编辑:
我可以看到viewPager 的高度与整个根视图的高度相同。这可能是有意为之,因为 appbar_scrolling_view_behavior 似乎确实添加了顶部和底部偏移量。然而,它看起来确实很奇怪,因为它会导致工具栏和标签栏始终滚动。
尝试在 TabLayout 上添加这些属性:
app:layout_collapseMode="pin"
app:tabMode="fixed"
AppBarLayout 上的这个:
android:fitsSystemWindows="true"
* 更新 *
我试过了,但这还不够,因为工具栏仍然可以滚动。 解决方案是对 ViewPager(及其内容)做一些逻辑。
从 xml 布局文件中删除工具栏 scroll_flag 属性。 您必须执行一些 java 代码来检查 ViewPager 内容高度是否 > 然后是 screenHeight - (toolbar + tabBar)。如果为真,则以编程方式将 scroll_flag 设置为:
LayoutParams params;
params = // get layout params from your toolbar
params.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL
| AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS);
// set params
toolbar.setLayoutParams(params);
我建议你试试 this 示例。
此布局与示例中的布局相似。
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:layout_scrollFlags="scroll|enterAlways" />
<android.support.design.widget.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="@dimen/fab_margin"
android:src="@drawable/ic_done" />
</android.support.design.widget.CoordinatorLayout>
ViewPager
高度应该是 match_parent
而不是 wrap_content
。
基于其他示例、我自己的代码以及 appbar_scrolling_view_behavior 的(有点乱)源代码:
public boolean onDependentViewChanged(CoordinatorLayout parent, View child,
View dependency) {
final CoordinatorLayout.Behavior behavior =
((CoordinatorLayout.LayoutParams) dependency.getLayoutParams()).getBehavior();
if (behavior instanceof Behavior) {
// Offset the child so that it is below the app-bar (with any overlap)
final int appBarOffset = ((Behavior) behavior)
.getTopBottomOffsetForScrollingSibling();
final int expandedMax = dependency.getHeight() - mOverlayTop;
final int collapsedMin = parent.getHeight() - child.getHeight();
if (mOverlayTop != 0 && dependency instanceof AppBarLayout) {
// If we have an overlap top, and the dependency is an AppBarLayout, we control
// the offset ourselves based on the appbar's scroll progress. This is so that
// the scroll happens sequentially rather than linearly
final int scrollRange = ((AppBarLayout) dependency).getTotalScrollRange();
setTopAndBottomOffset(AnimationUtils.lerp(expandedMax, collapsedMin,
Math.abs(appBarOffset) / (float) scrollRange));
} else {
setTopAndBottomOffset(dependency.getHeight() - mOverlayTop + appBarOffset);
}
}
return false;
}
我正在以解释问题的方式阅读这篇文章,因为这是此代码的预期行为。
我认为我们需要编写自己的滚动行为,专门针对 RecyclerView,
我刚刚遇到了同样的问题。 解决方法很简单,设置你的viewpager高度
android:layout_height="wrap_content"
简单的解决方法是 - 使用相对布局包装您的应用栏布局和页面查看器。 - 给你的 appbar 布局一些 id - 在页面视图中设置 android:layout_below="Your_appbar_layout" 例如:
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:id="@+id/your_appBar_ID"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/primary"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.ToolBar"
app:layout_scrollFlags="scroll|enterAlways" />
<android.support.design.widget.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:scrollbars="horizontal"
app:tabIndicatorColor="@color/black_text" />
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="@+id/tabs_activity_view_pager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/your_appBar_ID"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</RelativeLayout>
使用 ListView 作为 ViewPager 的数据?如果是这样,你需要listView.setNestedScrollingEnabled(true);
我已经解决了问题,尝试了示例 Google 模板并发现
app:layout_behavior="@string/appbar_scrolling_view_behavior"
行必须添加到视图寻呼机属性中 xml。它解决了我的问题。