先上图:
实现上图效果,首先来了解两个知识点:
1、AppBarLayout子布局的5种滚动标识(即app:layout_scrollFlags属性)
scroll
Child View 伴随着滚动事件而滚出或滚进屏幕。如果使用了其他值,必定要使用这个值才能起作用。
示例XML代码:
<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
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/tb_toolbar"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_56"
app:titleTextColor="@color/white"
app:title="@string/app_name"
app:theme="@style/OverFlowMenuTheme"
app:popupTheme="@style/AppTheme"
android:background="@color/blue"
app:layout_scrollFlags="scroll"/>
</android.support.design.widget.AppBarLayout>
对应效果图:
enterAlways
快速返回模式。其实就是向下滚动时Scrolling View和Child View之间的滚动优先级问题。对比scroll和scroll | enterAlways设置,发生向下滚动事件时,前者优先滚动Scrolling View,后者优先滚动Child View,当优先滚动的一方已经全部滚进屏幕之后,另一方才开始滚动。
示例XML代码:
...
app:layout_scrollFlags="scroll|enterAlways"
...
对应效果图:
enterAlwaysCollapsed
enterAlways的附加值。这里涉及到Child View的高度和最小高度,向下滚动时,Child View先向下滚动最小高度值,然后Scrolling View开始滚动,到达边界时,Child View再向下滚动,直至显示完全。
示例XML代码:
...
android:layout_height="@dimen/dp_200"
android:minHeight="@dimen/dp_56"
...
app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"
...
对应效果图:
exitUntilCollapsed
这里也涉及到最小高度。发生向上滚动事件时,Child View向上滚动退出直至最小高度,然后Scrolling View开始滚动。也就是,Child View不会完全退出屏幕。
示例XML代码:
...
android:layout_height="@dimen/dp_200"
android:minHeight="@dimen/dp_56"
...
app:layout_scrollFlags="scroll|exitUntilCollapsed"
...
snap
简单理解,就是Child View滚动比例的一个吸附效果。也就是说,Child View不会存在局部显示的情况,滚动Child View的部分高度,当我们松开手指时,Child View要么向上全部滚出屏幕,要么向下全部滚进屏幕,有点类似ViewPager的左右滑动。
示例XML代码:
...
android:layout_height="@dimen/dp_200"
...
app:layout_scrollFlags="scroll|snap"
...
对应效果图:
2、CollapsingToolbarLayout子布局的3种折叠模式(即app:layout_collapseMode属性)
off
这个是默认属性,布局将正常显示,没有折叠的行为。
pin
CollapsingToolbarLayout折叠后,此布局将固定在顶部。(本文Toolbar的app:layout_collapseMode属性就是这个)
parallax
CollapsingToolbarLayout折叠时,此布局也会有视差折叠效果。
了解基础知识后再去实现本文效果。
XML代码:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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.CoordinatorLayout
android:id="@+id/coordinator_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true">
<android.support.design.widget.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#fff"
app:elevation="0dp">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/main.collapsing"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
app:collapsedTitleGravity="center_horizontal"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<ImageView
android:id="@+id/usercenter_bg"
android:layout_width="match_parent"
android:layout_height="200dp"
android:scaleType="centerCrop"
android:src="@mipmap/dog"/>
<!-- 标题 -->
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#00000000"
app:layout_collapseMode="pin">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="AppBarLayout"
android:textColor="@color/colorWhite"
android:textSize="18sp"/>
</android.support.v7.widget.Toolbar>
</android.support.design.widget.CollapsingToolbarLayout>
<!-- 此处可替换为指示器 -->
<TextView
android:id="@+id/usercenter_title_mic"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@color/color_material_light"
android:gravity="center"
android:text="我是悬浮的">
</TextView>
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/appbar_recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
</android.support.v7.widget.RecyclerView>
</android.support.design.widget.CoordinatorLayout>
</LinearLayout>
需要注意三点:
1、CollapsingToolbarLayout作为AppBarLayout的子布局,其app:layout_scrollFlags属性设为scroll|exitUntilCollapsed,即CollapsingToolbarLayout向上滚动退出直至最小高度,然后AppBarLayout开始滚动。也就是,CollapsingToolbarLayout不会完全退出屏幕。注意要给CollapsingToolbarLayout设置android:minHeight属性。
2、Toolbar作为CollapsingToolbarLayout的子布局,其app:layout_collapseMode属性设为pin,即CollapsingToolbarLayout折叠后,此布局将固定在顶部。
3、RecyclerView的app:layout_behavior属性需要设为@string/appbar_scrolling_view_behavior。没有设置的话,AppBarLayout将不会响应滚动布局(Recyclerview、NestedScrollView等)的滚动事件。
然后运行项目即可实现本文效果。不过细心地盆友会发现,当CollapsingToolbarLayout收缩的时候,往下滑动RecyclerView的时候,CollapsingToolbarLayout伸展会很不连贯。因此我们要对AppBarLayout进行优化,使整体滑动连贯起来。
大概思路是,当RecyclerView滚动到顶部的时候,根据当前滑动的速度进行判断,是否让AppBarLayout被动展开(appBarLayout.setExpanded(true, true))。
首先定义接口OnAppbarExpandListener,用于通知AppBarLayout是否展开:
public interface OnAppbarExpandListener
{
void onExpand();
}
然后在Activity中实现该接口:
@Override
public void onExpand()
{
//AppBarLayout展开
appBarLayout.setExpanded(true, true);
}
接着编写RvScrollListener类,在滚动中判断AppBarLayout展开时机:
/**
* 判断RecyclerView是否滚动到顶部,从而对AppBarLayout进行伸展
*/
class RvScrollListener extends RecyclerView.OnScrollListener
{
OnAppbarExpandListener onAppbarExpandListener;
int scrollY = 0;
public RvScrollListener(OnAppbarExpandListener onAppbarExpandListener)
{
this.onAppbarExpandListener = onAppbarExpandListener;
}
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState)
{
super.onScrollStateChanged(recyclerView, newState);
//判断快速滚动:当速度大于10且滚到顶部时,让AppBarLayout展开
if (newState == RecyclerView.SCROLL_STATE_IDLE)
{
if (!recyclerView.canScrollVertically(-1) && scrollY < -10)
{
onAppbarExpandListener.onExpand();//通知AppBarLayout伸展
scrollY = 0;
}
}
//判断慢速滚动:当滚动到顶部时靠手指拖动后的惯性让RecyclerView处于Fling状态时的速度大于5时,让AppBarLayout展开
if (newState == RecyclerView.SCROLL_STATE_SETTLING)
{
if (!recyclerView.canScrollVertically(-1) && scrollY < -5)
{
onAppbarExpandListener.onExpand();
scrollY = 0;
}
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy)
{
super.onScrolled(recyclerView, dx, dy);
scrollY = dy;
}
}
最后给RecyclerView添加滚动监听addOnScrollListener:
mRecyclerView.addOnScrollListener(new RvScrollListener(this));
即完成对AppBarLayout嵌套滚动不连贯的优化。
源码地址:github
参考文章:Android 详细分析AppBarLayout的五种ScrollFlags