无法使用 NestedScrollView 平滑滚动 AppBarLayout 和折叠工具栏
Posted
技术标签:
【中文标题】无法使用 NestedScrollView 平滑滚动 AppBarLayout 和折叠工具栏【英文标题】:Unable to scroll AppBarLayout and collapsing toolbar with NestedScrollView smoothly 【发布时间】:2016-04-04 07:37:18 【问题描述】:我正在开发一个 android 应用程序,我在其中使用 CoordinatorLayout
、AppBarLayout
和 CollapsingToolbarLayout
来使用折叠工具栏功能。
我在布局中使用NestedScrollView
在同一布局中展开和折叠AppBarLayout
。当我尝试从屏幕中心向上滚动时,它不起作用,但是当我尝试从屏幕右上角向上滚动屏幕时,它会平滑滚动。
下面提到的是我的xml文件
layout.xml
<android.support.v4.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_
xmlns:app="http://schemas.android.com/apk/res-auto"
android:fillViewport="true"
android:fitsSystemWindows="true"
android:paddingBottom="2dp"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:paddingTop="5dp"
android:layout_gravity="fill_vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<RelativeLayout
android:layout_
android:layout_>
<LinearLayout
android:layout_
android:layout_
android:background="@color/fragment_back_color"
android:orientation="vertical">
<LinearLayout
android:layout_
android:layout_
android:orientation="horizontal"
android:padding="5dp">
<ImageView
android:layout_
android:layout_
android:layout_gravity="center"
android:background="@drawable/new_recharge" />
<com.spiceladdoo.views.RobotTextviewRegular
android:layout_
android:layout_
android:layout_gravity="center"
android:layout_marginLeft="10dp"
android:text="NEW PAYMENT"
android:textColor="@color/offer_name_text_color" />
</LinearLayout>
<RelativeLayout
android:layout_
android:layout_
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:background="@color/white"
android:paddingBottom="20dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="20dp">
<HorizontalScrollView
android:id="@+id/hsv"
android:layout_
android:layout_
android:layout_alignParentTop="true"
android:fillViewport="true"
android:measureAllChildren="false"
android:scrollbars="none">
<LinearLayout
android:layout_
android:layout_
android:layout_centerInParent="true"
android:orientation="horizontal">
<LinearLayout
android:id="@+id/wallet_layout"
android:layout_
android:layout_
android:gravity="center"
android:orientation="vertical">
<ImageView
android:id="@+id/wallet_recharge"
android:layout_
android:layout_
android:layout_gravity="center"
android:background="@drawable/wallet_recherge" />
<com.spiceladdoo.views.RobotTextviewRegular
android:layout_
android:layout_
android:layout_gravity="center"
android:layout_marginTop="5dp"
android:gravity="center"
android:text="WALLET"
android:textColor="@color/offer_name_text_color"
android:textSize="12sp" />
>
</LinearLayout>
<View
android:layout_
android:layout_
android:background="@color/white" />
<LinearLayout
android:id="@+id/prepaid_layout"
android:layout_
android:layout_
android:gravity="center"
android:orientation="vertical">
<ImageView
android:id="@+id/prepaid_recharge"
android:layout_
android:layout_
android:layout_gravity="center"
android:background="@drawable/prepaid_recherge" />
<com.spiceladdoo.views.RobotTextviewRegular
android:layout_
android:layout_
android:layout_gravity="center"
android:layout_marginTop="5dp"
android:gravity="center"
android:text="PREPAID"
android:textColor="@color/offer_name_text_color"
android:textSize="12sp" />
>
</LinearLayout>
<View
android:layout_
android:layout_
android:background="@color/white" />
<LinearLayout
android:id="@+id/postpaid_layout"
android:layout_
android:layout_
android:gravity="center"
android:orientation="vertical">
<ImageView
android:id="@+id/postpaid_recharge"
android:layout_
android:layout_
android:layout_gravity="center"
android:background="@drawable/postpaid_recherge" />
<com.spiceladdoo.views.RobotTextviewRegular
android:layout_
android:layout_
android:layout_gravity="center"
android:layout_marginTop="5dp"
android:gravity="center"
android:text="POSTPAID"
android:textColor="@color/offer_name_text_color"
android:textSize="12sp" />
>
</LinearLayout>
<View
android:layout_
android:layout_
android:background="@color/white" />
<LinearLayout
android:id="@+id/dth_layout"
android:layout_
android:layout_
android:gravity="center"
android:orientation="vertical">
<ImageView
android:id="@+id/dth_recharge"
android:layout_
android:layout_
android:layout_gravity="center"
android:background="@drawable/dth_recherge" />
<com.spiceladdoo.views.RobotTextviewRegular
android:layout_
android:layout_
android:layout_gravity="center"
android:layout_marginTop="5dp"
android:gravity="center"
android:text="DTH"
android:textColor="@color/offer_name_text_color"
android:textSize="12sp" />
>
</LinearLayout>
<View
android:layout_
android:layout_
android:background="@color/white" />
<LinearLayout
android:id="@+id/landline_layout"
android:layout_
android:layout_
android:gravity="center"
android:orientation="vertical">
<ImageView
android:id="@+id/landline_recharge"
android:layout_
android:layout_
android:layout_gravity="center"
android:background="@drawable/landline_recherge" />
<com.spiceladdoo.views.RobotTextviewRegular
android:layout_
android:layout_
android:layout_gravity="center"
android:layout_marginTop="5dp"
android:gravity="center"
android:text="LANDLINE"
android:textColor="@color/offer_name_text_color"
android:textSize="12sp" />
>
</LinearLayout>
<View
android:layout_
android:layout_
android:background="@color/white" />
<LinearLayout
android:id="@+id/datacard_layout"
android:layout_
android:layout_
android:gravity="center"
android:orientation="vertical">
<ImageView
android:id="@+id/datacard_recharge"
android:layout_
android:layout_
android:layout_gravity="center"
android:background="@drawable/datacard_recherge" />
<com.spiceladdoo.views.RobotTextviewRegular
android:layout_
android:layout_
android:layout_gravity="center"
android:layout_marginTop="5dp"
android:gravity="center"
android:text="DATACARD"
android:textColor="@color/offer_name_text_color"
android:textSize="12sp" />
>
</LinearLayout>
</LinearLayout>
</HorizontalScrollView>
</RelativeLayout>
<LinearLayout
android:layout_
android:layout_
android:orientation="horizontal"
android:paddingBottom="10dp"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:paddingTop="10dp">
<ImageView
android:layout_
android:layout_
android:layout_gravity="center"
android:background="@drawable/recent" />
<com.spiceladdoo.views.RobotTextviewRegular
android:layout_
android:layout_
android:layout_gravity="center"
android:layout_marginLeft="10dp"
android:text="RECENT"
android:textColor="@color/offer_name_text_color" />
</LinearLayout>
<ListView
android:id="@+id/recent_recharge_list"
android:layout_
android:layout_
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp">
</ListView>
</LinearLayout>
<FrameLayout xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/recharge_container"
android:layout_
android:layout_
android:visibility="visible"
tools:ignore="MergeRootFrame">
</FrameLayout>
</RelativeLayout>
</android.support.v4.widget.NestedScrollView>
想要的结果是当我尝试从屏幕中心向上滚动时,它应该像我从手机右上角向上滚动一样顺畅。
请观看下面提到的视频以更清楚地了解问题
https://www.dropbox.com/s/gscfc8vfc7kkpxp/device-2015-12-30-160119.mp4?dl=0
【问题讨论】:
【参考方案1】:我相信,我已经成功了:
您可以找到源代码 here - 随意尝试;
我以google\designlibdemo为例。
这是我的Activity
的样子:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawer_layout"
android:layout_
android:layout_
android:fitsSystemWindows="true">
<include layout="@layout/include_list_viewpager"/>
<android.support.design.widget.NavigationView
android:id="@+id/nav_view"
android:layout_
android:layout_
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="@layout/nav_header"
app:menu="@menu/drawer_view"/>
</android.support.v4.widget.DrawerLayout>
它托管ViewPager
:include_list_viewpager.xml
:
<?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:id="@+id/main_content"
android:layout_
android:layout_>
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_
android:layout_
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/collapsing_toolbar"
android:layout_
android:layout_
app:layout_scrollFlags="scroll|enterAlways|snap"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleMarginStart="48dp"
app:expandedTitleMarginEnd="64dp"
android:fitsSystemWindows="true">
<ImageView
android:id="@+id/image"
android:src="@drawable/header_image"
android:layout_
android:layout_
android:scaleType="centerCrop"
android:fitsSystemWindows="true"
app:layout_collapseMode="parallax"/>
<View
android:background="#AA50AA00"
android:layout_
android:layout_
app:layout_collapseMode="parallax"/>
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_
android:title=""
android:layout_
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:layout_collapseMode="pin" />
</android.support.design.widget.CollapsingToolbarLayout>
<android.support.design.widget.TabLayout
android:id="@+id/tabs"
android:background="#50AA00"
app:tabMode="scrollable"
app:tabIndicatorColor="#FFF"
android:layout_
android:layout_ />
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_
android:layout_
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</android.support.design.widget.CoordinatorLayout>
CollapsingToolbarLayout
主机Toolbar
(app:layout_collapseMode="pin"
) 及以上ImageView
(app:layout_collapseMode="parallax"
)。
ViewPager Fragment 中托管的有这样的布局:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_
xmlns:app="http://schemas.android.com/apk/res-auto"
android:fillViewport="true"
android:fitsSystemWindows="true"
android:layout_gravity="fill_vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:orientation="vertical"
android:background="#DDD"
android:layout_
android:layout_>
<LinearLayout
android:layout_
android:layout_
android:orientation="horizontal"
android:padding="16dp">
<ImageView
android:src="@drawable/mobile"
android:layout_marginTop="2dp"
android:layout_
android:layout_ />
<TextView
android:text="@string/prepaid_recharge"
android:layout_marginStart="16dp"
android:layout_gravity="center_vertical"
android:textColor="#000"
android:fontFamily="sans-serif-medium"
android:textSize="14sp"
android:textAllCaps="true"
android:layout_
android:layout_ />
</LinearLayout>
........
<ListView
android:id="@+id/recent_recharge_list"
android:layout_
android:layout_/>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
所以它有这种行为app:layout_behavior="@string/appbar_scrolling_view_behavior"
和填充ViewPort
(android:fillViewport="true"
)
实际上 - 这就是你所需要的。 Fragment
类很标准:
public class RechargeFragment extends Fragment
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
return inflater.inflate(R.layout.recharge_fragment, container, false);
Activity
还是很标准的。
同样,您可以找到我的代码示例here。
注意!我发现它在模拟器上的效果很差(根本不流畅)。
希望对你有帮助。
【讨论】:
我已经检查了您的演示项目。滚动太慢或有时会挂起。所以不值得使用。 @KushminderGarg 你检查了 real 设备,而不是模拟器?正如您在 gif 上看到的那样,它完全无需挂起即可工作。 (我在生产中有类似的代码,还没有人抱怨滚动) 我是在真机而不是模拟器上检查。 一点都不流畅。卡住了。 :( 这实际上把我带回了地球。 参考您的 GIF,您最近充值列表中的内容是否可以滚动。我无法在嵌套滚动视图内的视图寻呼机中滚动列表。【参考方案2】:在使用 CoordinatorLayout
将 AppbarLayout
、CollapsingToolbarLayout
和 NestedScrollView
作为子视图时,我几乎遇到了类似的问题。
以下代码是直接来自我的项目工作区的工作部分。希望对您有所帮助!
<?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:id="@+id/main_content"
android:layout_
android:layout_>
<android.support.design.widget.AppBarLayout
android:id="@+id/appBar"
android:layout_
android:layout_
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/collapsing_toolbar"
android:layout_
android:layout_
app:contentScrim="?attr/colorPrimary"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<ImageView
android:id="@+id/cover_pic"
android:layout_
android:layout_
android:scaleType="centerCrop"
app:layout_collapseMode="parallax"
android:src="@drawable/cookin"/>
<android.support.v7.widget.Toolbar
android:id="@+id/mToolbar"
android:layout_
android:layout_
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:layout_collapseMode="pin"/>
</android.support.design.widget.CollapsingToolbarLayout>
<android.support.design.widget.TabLayout
android:id="@+id/tabLayout"
android:layout_
android:layout_
android:layout_gravity="bottom"
android:background="?attr/colorPrimary"
app:tabMode="scrollable"/>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:layout_
android:layout_
android:fillViewport="true"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<android.support.v4.view.ViewPager
android:id="@+id/tab_viewpager"
android:layout_
android:layout_
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</android.support.v4.widget.NestedScrollView>
<RelativeLayout
android:layout_
android:layout_>
<LinearLayout
android:layout_alignParentBottom="true"
android:layout_
android:layout_
android:orientation="vertical">
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab_phone"
android:layout_
android:layout_
android:layout_gravity="bottom|end"
android:layout_marginRight="@dimen/fab_margin"
android:visibility="invisible"
app:backgroundTint="@color/colorFAB2"
app:elevation="6dp"
android:layout_margin="5dp"
app:pressedTranslationZ="12dp"
android:src="@drawable/ic_phone_white_24dp" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab_book"
android:layout_
android:layout_
android:layout_gravity="bottom|end"
android:layout_margin="5dp"
android:layout_marginRight="@dimen/fab_margin"
android:visibility="invisible"
app:elevation="6dp"
app:backgroundTint="@color/colorFAB1"
app:pressedTranslationZ="12dp"
android:src="@drawable/ic_receipt_white_24dp" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab_add"
android:layout_
android:layout_
android:layout_gravity="bottom|end"
app:elevation="6dp"
app:backgroundTint="@color/colorAccent"
app:pressedTranslationZ="12dp"
android:layout_margin="@dimen/fab_margin"
android:src="@drawable/ic_add_white_24dp" />
</LinearLayout>
</RelativeLayout>
</android.support.design.widget.CoordinatorLayout>
【讨论】:
它可以让我在view pager
中滚动内容吗?
这不允许我在 viewpager 中滚动内容。滚动直接影响工具栏并折叠它,但视图分页器内的内容保持静态【参考方案3】:
在NestedScrollView
的子视图中尝试android:clickable="true"
,如下所示:
<android.support.v4.widget.NestedScrollView
android:layout_
android:layout_
android:fillViewport="true"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:layout_
android:layout_
android:orientation="vertical"
android:clickable="true">
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
【讨论】:
【参考方案4】:看起来像 this 的副本。虽然它指向“已解决”的线程,但另一个与 RecyclerView 相关的更多。
我根据Manolo Garcia 和Kirill Boyarshinov 来自 RecyclerView 线程的解决方案编写了我的行为。在我的情况下,onNestedFling()
在制作投掷手势时没有被调用,所以我破解了onNestedPreScroll()
。我今天一直在使用 NestedScroll 在 ViewPager 中工作,还没有在不同的场景下测试过下面的解决方案(尽管它基于我一年前为 RecyclerView 编写的类似代码)。
第一次更新的行为(在 xml 中添加为 app:layout_behavior="your.package.FlingBehavior">
为 android.support.design.widget.AppBarLayout
):
public final class FlingBehavior extends AppBarLayout.Behavior
private static final String TAG = FlingBehavior.class.getName();
private static final int TOP_CHILD_FLING_THRESHOLD = 1;
private static final float OPTIMAL_FLING_VELOCITY = 3500;
private static final float MIN_FLING_VELOCITY = 20;
boolean shouldFling = false;
float flingVelocityY = 0;
public FlingBehavior()
public FlingBehavior(Context context, AttributeSet attrs)
super(context, attrs);
@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target,
int velocityX, int velocityY, int[] consumed)
super.onNestedPreScroll(coordinatorLayout, child, target, velocityX, velocityY, consumed);
if (velocityY > MIN_FLING_VELOCITY)
shouldFling = true;
flingVelocityY = velocityY;
else
shouldFling = false;
@Override
public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout abl, View target)
super.onStopNestedScroll(coordinatorLayout, abl, target);
if (shouldFling)
Log.d(TAG, "onNestedPreScroll: running nested fling, velocityY is " + flingVelocityY);
onNestedFling(coordinatorLayout, abl, target, 0, flingVelocityY, true);
@Override
public boolean onNestedFling(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target,
float velocityX, float velocityY, boolean consumed)
if (target instanceof RecyclerView && velocityY < 0)
Log.d(TAG, "onNestedFling: target is recyclerView");
final RecyclerView recyclerView = (RecyclerView) target;
final View firstChild = recyclerView.getChildAt(0);
final int childAdapterPosition = recyclerView.getChildAdapterPosition(firstChild);
consumed = childAdapterPosition > TOP_CHILD_FLING_THRESHOLD;
// prevent fling flickering when going up
if (target instanceof NestedScrollView && velocityY > 0)
consumed = true;
if (Math.abs(velocityY) < OPTIMAL_FLING_VELOCITY)
velocityY = OPTIMAL_FLING_VELOCITY * (velocityY < 0 ? -1 : 1);
Log.d(TAG, "onNestedFling: velocityY - " + velocityY + ", consumed - " + consumed);
return super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed);
现在它应该可以平滑滚动,但 NestedScrollView(和 RecyclerView)的内容可以在 AppBarLayout 折叠之前滚动,这看起来很奇怪。解决它follow this answer。进行小改动以使用fullScroll(ScrollView.FOCUS_UP)
方法而不是scrollTo(0, 0)
,否则您可能会注意到在快速滚动期间会出现小闪烁,此处:
AppBarLayout appBarLayout = findViewById(...);
appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener()
@Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset)
shouldScroll = Math.abs(verticalOffset) == appBarLayout.getTotalScrollRange();
);
NestedScrollView nestedScrollView = findViewById(...);
nestedScrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener()
@Override
public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY)
if (!shouldScroll)
nestedScrollView.fullScroll(ScrollView.FOCUS_UP);
);
在那之后我称之为结束,但您可能希望进一步使其滚动更顺畅,就像在 Google Play 中一样,有(我认为未解决)关于它的线程 here。
【讨论】:
这是一个很好的解决方案,但由于某种原因,在滚动事件期间永远不会触发 NestedScrollView.OnScrollChangeListener。知道为什么会这样吗?我在滚动视图上使用app:layout_behavior="@string/appbar_scrolling_view_behavior"
,但它似乎无法使用/不使用它。
您确定没有触发该事件吗?现在在 AS 调试中似乎并不总是对我有用,对于日志也是如此。我知道这可能是一个疯狂的建议,但尝试在事件中添加 Toast 并构建一次以测试是否显示 toast。如果上述方法不起作用,那么您的布局 xml 可能有问题?它应该或多或少像这样:pastebin.com/Wcrrv8rC以上是关于无法使用 NestedScrollView 平滑滚动 AppBarLayout 和折叠工具栏的主要内容,如果未能解决你的问题,请参考以下文章
NestedScrollView 和 Horizontal RecyclerView 平滑滚动
SwipeRefreshLayout 内的 NestedScrollView 内的 RecyclerView 不能平滑滚动
NestedScrollView 内的 RecyclerView :使水平滚动更容易
如何禁用 NestedScrollView&CollapsingToolbarLayout 的滚动,例如当下面没有更多内容时?