CoordinatorLayout是support.design包中的控件,它可以说是Design库中最重要的控件。
他提供了一种全新的布局玩法 - 折叠(关联),何为折叠,就是让一些指定控件的位置,形态随着指定的控件的变化而变化,最简单的例子,就是标题栏随着列表的滚动而显示或消失或这折叠,这是5.0添加的新特性,之前我们监听滚动列表的滚动状态也可以做到这个效果,但是代码会很耦合。这次 google 提供了这个 CoordinatorLayout 顶层容器,配合 Behavior 来封装解耦了这个需求,让我们编写相关业务代码更简单,同理效果也是更好。
本地 demo 地址: github
有个别人的简单入门:
核心套路
我先说说 CoordinatorLayout 的核心套路,这里插一句是为了以后好找:
- 给管滚动控件加 app:layout_behavior="@string/appbar_scrolling_view_behavior"
- 给 CollapsingToolbarLayout 设置
- 滚动样式:app:layout_scrollFlags="scroll|exitUntilCollapsed"
- 折叠后标题栏颜色:app:contentScrim="@color/colorAccent"
- 折叠后系统状态栏栏颜色:app:layout_scrollFlags="scroll|exitUntilCollapsed"
- 给具体的 view 设置折叠模式: app:layout_collapseMode="pin"
CoordinatorLayout 使用详细讲解
这里有2个概念:
- CoordinatorLayout
顶层容器,用来协调配合子 view 的位置,状态关联。 - Behavior
定义了子 view 具体的关联行为。
Design 里面有默认的 Behavior 实现,就是一下这个 layout 组合:
<android.support.design.widget.CoordinatorLayout
android:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout
android:fitsSystemWindows="true"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.design.widget.CollapsingToolbarLayout
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:statusBarScrim="@color/colorAccent"
app:titleEnabled="false"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<android.support.v7.widget.Toolbar
app:popupTheme="@style/AppTheme.PopupOverlay"
app:layout_collapseMode="parallax"
app:layout_collapseParallaxMultiplier="0.7"
app:layout_collapseMode="pin"/>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
app:layout_behavior="@string/appbar_scrolling_view_behavior">
</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>
就是上面这几个布局 CoordinatorLayout + AppBarLayout + CollapsingToolbarLayout + Toolbar + NestedScrollView
这里有2个角色划分:
- 关联发起者,提供/触发滚动事件
这里的关联发起者就是可以滚动的 view,上面的例子用的是 NestedScrollView ,当然也可以是 RecyclerView,只要可以滚动就行,最重要的是给滚动控件设置 layout_behavior
app:layout_behavior="@string/appbar_scrolling_view_behavior">
设置 layout_behavior 标记,就是设置发起滚动关联的事件源
- 关联消费者,消费滚动事件的
就是这个 Behavior 了,系统有默认的实现了 Behavior 的控件,就是上面例子我们使用的- AppBarLayout
- CollapsingToolbarLayout
- Toolbar
剩下的 view 就需要我们自已去定义 Behavior 的行为了,具体的在自定义 Behavior 的章节再说。这里先说系统提供的。
系统默认提供的 Behavior
系统 activity 模板里的 ScrollingActivity ,使用的就是系统提供的 Behavior ,是我们学习 Behavior 的最好的初始资料,我们先来看这个
系统的 xml 布局定义:
<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"
tools:context="com.bloodcrown.croodnatorlayouttest2.ScrollingActivity">
<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">
<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>
<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:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="com.bloodcrown.croodnatorlayouttest2.ScrollingActivity"
tools:showIn="@layout/activity_scrolling">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/text_margin"
android:text="@string/large_text"/>
</android.support.v4.widget.NestedScrollView>
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/fab_margin"
app:layout_anchor="@id/app_bar"
app:layout_anchorGravity="bottom|end"
app:srcCompat="@android:drawable/ic_dialog_email"/>
</android.support.design.widget.CoordinatorLayout>
恩,这个布局精简过后,就是我们上面列的默认的 Behavior 实现Behavior 实现 layout 组合:
<android.support.design.widget.CoordinatorLayout>
<android.support.design.widget.AppBarLayout>
<android.support.design.widget.CollapsingToolbarLayout
app:contentScrim="?attr/colorPrimary"
app:statusBarScrim="?attr/colorPrimary"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<android.support.v7.widget.Toolbar
app:layout_collapseMode="pin"/>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</android.support.design.widget.CoordinatorLayout>
好了这里就是重点了,大家注意
先说说这几个布局的嵌套关系
- CoordinatorLayout
必须是顶层布局,behavior 就是CoordinatorLayout 提供的特性,没有CoordinatorLayout 作为顶层布局,behavior 不起作用 - AppBarLayout
标题栏总部局,继承的是 LinearLayout ,里面放折叠布局 CollapsingToolbarLayout 等,不放在 CollapsingToolbarLayout 的 view 不参与折叠效果 - CollapsingToolbarLayout
折叠布局,放在这里面的 view 都会参与折叠,当然你也可以设置关闭折叠模式。
然后呢,再来看看这几个布局的参数设置:
滚动布局设置参数:
- layout_behavior
给滚动控件设置 layout_behavior 标记,声明该滚动控件对外提供滚动关联事件,这里使用系统默认提供的值,
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
AppBarLayout 的参数设置:
layout_scrollFlags,这是 AppBarLayout 的滚动模式设置,有5种值,给标题栏中需要滚动的部分设置,不设置的 view 不会参与滚动
- scroll
此布局和滚动事件关联 - enterAlways
向下滚动时,此布局展开 - enterAlwaysCollapsed
假设你定义了一个最小高度(minHeight)同时enterAlways也定义了,那么view将在到达这个最小高度的时候开始显示,并且从这个时候开始慢慢展开,当滚动到顶部的时候展开完。 - exitUntilCollapsed
当你定义了一个minHeight,此布局将在滚动到达这个最小高度的时候折叠。 - snap
当一个滚动事件结束,如果视图是部分可见的,那么它将被滚动到收缩或展开。例如,如果视图只有底部25%显示,它将折叠。相反,如果它的底部75%可见,那么它将完全展开。
CollapsingToolbarLayout 的参数设置:
contentScrim / statusBarScrim,这2个属性,是设置标题栏在折叠后的内容颜色和状态栏颜色
- contentScrim
内容颜色,默认使用 colorPrimary 的色值 - statusBarScrim
状态栏颜色,默认使用 colorPrimaryDark 的色值 - layout_collapseMode
CollapsingToolbarLayout 布局中 view 的折叠模式,有3种模式,注意是给CollapsingToolbarLayout 中的子布局使用的,注意他和layout_scrollFlags的区别,layout_scrollFlags是使用外层的:- off
这个是默认属性,布局将正常显示,没有折叠的行为。 - pin
CollapsingToolbarLayout折叠后,此布局将固定在顶部。 - parallax
CollapsingToolbarLayout折叠时,此布局也会有视差折叠效果
- off
- layout_collapseParallaxMultiplier
视差滚动因子,值为:0~1。
FloatingActionButton 的参数设置:
- layout_anchor
设置锚点,FloatingActionButton 的中心点和谁对齐 - layout_anchorGravity
锚点对齐位置
好了这些布局的关系和属性设置我们都了解了之后就可以自己来玩来,这里面可是大有可为的,咱们来好好玩玩
自由使用系统默认提供的 Behavior
我们不要局限在系统给我们提供的 ScrollActivity 的写法中,适当的变通还是可以有点啊,比如现在很常见的,标题栏中我们加一张图片进去做打底图,或者不用 toolBar ,自己封装组合 view 都是可以的,我们先来做下面这个样式的
这个实现还是很简单的,我们先来看看 xml 的写法
<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:fitsSystemWindows="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.bloodcrown.croodnatorlayouttest2.FreeActivity1">
<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"
app:theme="@style/AppTheme.AppBarOverlay">
<android.support.design.widget.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:contentScrim="@color/colorAccent"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
app:statusBarScrim="@color/colorAccent"
app:titleEnabled="false">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:scaleType="centerCrop"
android:src="@mipmap/x001"
app:layout_collapseMode="parallax"
app:layout_collapseParallaxMultiplier="0.7"/>
<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"
app:title="">
<!--<TextView-->
<!--android:layout_width="wrap_content"-->
<!--android:layout_height="wrap_content"-->
<!--android:layout_gravity="top|left"-->
<!--android:gravity="top|left"-->
<!--android:text="测试"/>-->
</android.support.v7.widget.Toolbar>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<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:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="com.bloodcrown.croodnatorlayouttest2.ScrollingActivity"
tools:showIn="@layout/activity_scrolling">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/text_margin"
android:text="@string/large_text"/>
</android.support.v4.widget.NestedScrollView>
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="@dimen/fab_margin"
android:src="@mipmap/ic_launcher_round"
app:layout_anchor="@id/app_bar"
app:layout_anchorGravity="bottom|right"/>
录屏时toolbar 里面的 textview 注释了,效果图里看不到,实际上还是能正常显示的,大家放心。
我们首先让系统状态烂透明,这样才能让图片实现顶置显示
public class FreeActivity1 extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_free1);
intiStateView();
}
private void intiStateView() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
WindowManager.LayoutParams layoutParams = getWindow().getAttributes();
layoutParams.flags = (layoutParams.flags | WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
}
CollapsingToolbarLayout 折叠布局继承帧布局,我们在里面放什么view 都行,只要设置扯着模式就行,这里放入了一个 imageview,使用了0.7的折叠视差效果,和起点 app 小说详情页
的标题栏一样,滚动时收缩都是带视差效果的。
以下有2个注意点:
fitsSystemWindows 属性
在我们设置系统状态栏透明后,会造成 toolbar 占用系统状态栏的位置的问题,这时我们需要 设置android:fitsSystemWindows="true" 这个属性。这个 fitsSystemWindows 属性,要从跟布局一直设置到具体的填充系统状态烂的布局才行(在例子中是imageview 这个 view),有些麻烦。
我的xml 设置:
<android.support.design.widget.CoordinatorLayout
android:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout
android:fitsSystemWindows="true">
<android.support.design.widget.CollapsingToolbarLayout
android:fitsSystemWindows="true">
<ImageView
android:fitsSystemWindows="true"/>
toolbar 占用系统状态栏的位置:这里把 toolbar 的颜色设置为黑色
不知道大家注意没有,我们设置 android:fitsSystemWindows="true" 后,就是给 toolbar 内部添加了一个系统状态栏高度的 maggin,要是在标题栏在展开模式时 toolbar 没有自己的颜色设置的话,其实这个 fitsSystemWindows 写不写都行,写了toolbar 的内容会往下走一点,不写toolbar 的内容会正好卡在系统状态烂的下面,觉得贴的紧的话,自己加一个 padding 上去也可以的,具体的还是看需求,在这里把问题高清楚方便之后我们的具体使用
这张图是我们添加 fitsSystemWindows="true" 之后,toolbar 的位置在状态栏的下面,toolbar 黑色背景真丑,但是为了看看效果也是没办法的。这个 toolbar 里面的 textview 的位置设置的是 top|left,文字上面的 maggin和系统状态栏的高度其实是一样的。所以说设置 fitsSystemWindows 标记,就是给 toolbar 加了一个系统状态栏高度的 maggin
toolbar 的使用方式
toolbar的使用有2种:
-
和系统的 actionbar 关联
- height 高度必须使用系统默认值 ?attr/actionBarSize,不能是别的值
- 这时 toolbar 不能作为一个 layout 使用,toolbar 就算放置了 view 也不会显示
-
单纯作为一个layout 使用
- 这时 toolbar 可以作为一个 layout 使用,内部可以放 view,并且会正常显示出来
- actionbar 的那个特性就都用不了了,比如menuitem 的那3个小点点就会显示了
- height 高度可以是任何值,上面那个系统默认值,wrap 和具体值都可以
具体取舍看具体需求把,我比较喜欢单纯当做 layout 来用,这样灵活。
toolbar和系统的 actionbar 关联 代码
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
搭配 TabLayout 使用
在页面的编写时常常会用到 TabLayout ,或是替代的开源库,这个我们应该怎么和我们上面的例子结合呢,为啥说这个呢,因为 appbarlayout 在折叠后,高度会是 toolbar 的高度,这时候 toolbar 的高度要适配 TabLayout 的高度才行,要不就会挤在一起
那么我们怎么解决这个问题呢,看下面:
在xml中 TabLayout 和 Toolbar 同级,toolbar 的高度设置为 tollbar 本身的 height + TabLayout 的 height ,这种做法需要知道 TabLayout 和 Toolbar 的确切高度值。xml 里面要是height 没有确定值那就用代码计算一下再设置也行
<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"
app:theme="@style/AppTheme.AppBarOverlay">
<android.support.design.widget.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:contentScrim="@color/colorAccent"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
app:statusBarScrim="@color/colorAccent"
app:title=""
app:titleEnabled="false">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:scaleType="centerCrop"
android:src="@mipmap/x001"
app:layout_collapseMode="parallax"
app:layout_collapseParallaxMultiplier="0.7"/>
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="90dp"
app:layout_collapseMode="pin"
app:popupTheme="@style/AppTheme.PopupOverlay"
app:title="">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|left"
android:gravity="top|left"
android:text="测试"
android:textSize="16sp"/>
</android.support.v7.widget.Toolbar>
<android.support.design.widget.TabLayout
android:id="@+id/view_tab"
android:layout_width="match_parent"
android:layout_height="45dp"
android:layout_gravity="bottom"
android:gravity="center_vertical"></android.support.design.widget.TabLayout>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
为啥要特别说 tablayout 呢,因为上面我们要让 tablayout 站在背景图片中,参与折叠,要是不在背景图片就不要放到 CollapsingToolbarLayout 里面就好了,更省事。
这段是我 copy 过来的:
TabLayout没有设置app:layout_collapseMode,在CollapsingToolbarLayout收缩时就不会消失。
CollapsingToolbarLayout收缩时的高度是Toolbar的高度,所以我们需要把Toolbar的高度增加,给TabLayout留出位置,这样收缩后TabLayout就不会和Toolbar重叠。
Toolbar的高度增加,title会相应下移。android:gravity="top"方法使Toolbar的title位于Toolbar的上方,然后通过app:titleMarginTop调整下title距顶部高度,这样Toolbar就和原来显示的一样了。
仿 bilibili 视频详情页
图我是 copy 过来的,放哔哩哔哩这个也算是经典学习类目之一了
我录屏转换gif 太大了,特意压缩了下分辨率才能传上来,效果可能不是很好
分析下思路:
- toolbar 固定在顶部
- 监听 appbarlayout,在折叠和展开时,toolbar 显示不同的 view
- 点击 FloatingActionButton ,让appbarlayout不再相应滚动事件
- 点击播放可以使 appbarlayout 从折叠状态切换到展开状态
基本需求和思路就是上面几条,前面的例子玩会了,我们搭建这个 bilibili 页面就会了,区别在于 toolbar 在折叠和展开时,显示不同的 view。
toolbar 定义不同的状态的 view
这个简单,这里我把 toolbar 作为 layout 使用,不可系统 actionbar 管理,里面定义2条 view ,一个显示,一个隐藏
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="50dp"
app:layout_collapseMode="pin"
app:popupTheme="@style/AppTheme.PopupOverlay"
app:title="">
<android.support.constraint.ConstraintLayout
android:id="@+id/appbar_show"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_arrow_back_black_24dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="BiLiBiLi大讲堂开播啦"
android:textColor="@android:color/white"
android:textSize="22sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</android.support.constraint.ConstraintLayout>
<android.support.constraint.ConstraintLayout
android:id="@+id/appbar_hint"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:visibility="gone">
<ImageView
android:id="@+id/appbar_hint_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_play_circle_filled_black_24dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/appbar_hint_name"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="@+id/appbar_hint_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:text="立即播放"
android:textColor="@android:color/white"
android:textSize="22sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@+id/appbar_hint_icon"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</android.support.constraint.ConstraintLayout>
</android.support.v7.widget.Toolbar>
可以看到我定义了 show 和 hint 2个 view,对于展开和折叠状态
监听 appbarlayout
CollapsingToolbarLayout 收缩布局本身没有监听函数,他也是监听 appbarlayout 实现动画的,这里我们也去监听 appbarlayout 的滚动变化,自己记录维护 appbarlayout 的展开和折叠状态值。
先定义 appbarlayout 的3种状态值
// appbar展开状态
public static final int STAET_EXPANDED = 1;
// appbar折叠状态
public static final int STAET_COLLAPSED = -1;
// appbar中间状态
public static final int STAET_ING = 0;
然后再去监听 appbarlayout 滚动变化
mAppBar.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
@Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
// int totalScrollRange = appBarLayout.getTotalScrollRange();
// Log.d("AAA", "total:" + totalScrollRange + "/Offset:" + verticalOffset);
verticalOffset = Math.abs(verticalOffset);
// 初始展开状态
if (verticalOffset == 0) {
if (state != STAET_EXPANDED) {
// 是展开状态,重置标记为展开,显示 appbar 布局控件
state = STAET_EXPANDED;
appBar_show.setVisibility(View.VISIBLE);
appBar_hint.setVisibility(View.GONE);
}
return;
}
// 折叠状态
if (verticalOffset >= appBarLayout.getTotalScrollRange()) {
if (state != STAET_COLLAPSED) {
// 是折叠状态,重置标记为折叠,显示 appbar 布局控件
state = STAET_COLLAPSED;
appBar_hint.setVisibility(View.VISIBLE);
appBar_show.setVisibility(View.GONE);
}
return;
}
// 中间状态
if (state != STAET_ING) {
// 是中间状态,重置标记为ing,显示 appbar 布局控件
state = STAET_ING;
appBar_show.setVisibility(View.VISIBLE);
appBar_hint.setVisibility(View.GONE);
}
}
});
这里面有几个注意点:
- verticalOffset
这个函数返回的参数是表示当前 appbarlayout 的累计滚动偏移量,是负数,我们要取绝对值才行,这里我也是打印数值之后才知道的, 为啥是负数呢,因为 appbarlayout 的 Y 轴在向上运动,就是在减少啊,所以是负数啊。 - appbarlayout 可以滚动的最大值
appBarLayout.getTotalScrollRange() 这个方法可以返回最大值,然后我们判断累计滚动偏移量大于等于这个最大值了,就是处于折叠到底的状态了。
appbarlayout不再相应滚动事件
这个好做,现成的 api,只要让滚动 view 不再发射滚动事件就行了
mFAB.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (state == STAET_EXPANDED) {
if (state_scroll == SCROLL_ING) {
// 取消滚动控件的嵌套滚动,核心的就是这个函数了,大家注意啊
mScrollView.setNestedScrollingEnabled(false);
state_scroll = SCROLL_NO;
} else if (state_scroll == SCROLL_NO) {
mScrollView.setNestedScrollingEnabled(true);
state_scroll = SCROLL_ING;
}
}
}
});
折叠状态切换到展开状态
这个页简单,现成的 api, setExpanded(true)
mPlayButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (state == STAET_COLLAPSED) {
// 处于折叠状态时才能玩
mAppBar.setExpanded(true);
state = STAET_EXPANDED;
}
}
});
完整代码:
<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"
tools:context="com.bloodcrown.croodnatorlayouttest2.FreeActivity1">
<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"
app:theme="@style/AppTheme.AppBarOverlay">
<android.support.design.widget.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:contentScrim="@color/colorAccent"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
app:statusBarScrim="@color/colorAccent"
app:title=""
app:titleEnabled="false">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:scaleType="centerCrop"
android:src="@mipmap/x001"
app:layout_collapseMode="parallax"
app:layout_collapseParallaxMultiplier="0.7"/>
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="50dp"
app:layout_collapseMode="pin"
app:popupTheme="@style/AppTheme.PopupOverlay"
app:title="">
<android.support.constraint.ConstraintLayout
android:id="@+id/appbar_show"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_arrow_back_black_24dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="BiLiBiLi大讲堂开播啦"
android:textColor="@android:color/white"
android:textSize="22sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</android.support.constraint.ConstraintLayout>
<android.support.constraint.ConstraintLayout
android:id="@+id/appbar_hint"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:visibility="gone">
<ImageView
android:id="@+id/appbar_hint_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_play_circle_filled_black_24dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/appbar_hint_name"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="@+id/appbar_hint_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:text="立即播放"
android:textColor="@android:color/white"
android:textSize="22sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@+id/appbar_hint_icon"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</android.support.constraint.ConstraintLayout>
</android.support.v7.widget.Toolbar>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:id="@+id/view_nest"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="com.bloodcrown.croodnatorlayouttest2.ScrollingActivity"
tools:showIn="@layout/activity_scrolling">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/text_margin"
android:text="@string/large_text"/>
</android.support.v4.widget.NestedScrollView>
<android.support.design.widget.FloatingActionButton
android:id="@+id/view_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="@dimen/fab_margin"
android:src="@drawable/ic_play_circle_filled_black_24dp"
app:layout_anchor="@id/app_bar"
app:layout_anchorGravity="bottom|right"/>
</android.support.design.widget.CoordinatorLayout>
public class BiliBiLiActivity extends AppCompatActivity {
// appbar展开状态
public static final int STAET_EXPANDED = 1;
// appbar折叠状态
public static final int STAET_COLLAPSED = -1;
// appbar中间状态
public static final int STAET_ING = 0;
int state = 2;
// appbar 响应滚动状态
public static final int SCROLL_ING = -1;
// appbar 不响应滚动状态
public static final int SCROLL_NO = 0;
int state_scroll = SCROLL_ING;
View appBar_show;
View appBar_hint;
FloatingActionButton mFAB;
AppBarLayout mAppBar;
NestedScrollView mScrollView;
View mPlayButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bili_bi_li);
intiStateView();
appBar_show = findViewById(R.id.appbar_show);
appBar_hint = findViewById(R.id.appbar_hint);
mFAB = (FloatingActionButton) findViewById(R.id.view_fab);
mAppBar = (AppBarLayout) findViewById(R.id.app_bar);
mScrollView = (NestedScrollView) findViewById(R.id.view_nest);
mPlayButton = findViewById(R.id.appbar_hint_name);
mAppBar.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
@Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
// int totalScrollRange = appBarLayout.getTotalScrollRange();
// Log.d("AAA", "total:" + totalScrollRange + "/Offset:" + verticalOffset);
verticalOffset = Math.abs(verticalOffset);
// 初始展开状态
if (verticalOffset == 0) {
if (state != STAET_EXPANDED) {
// 是展开状态,重置标记为展开,显示 appbar 布局控件
state = STAET_EXPANDED;
appBar_show.setVisibility(View.VISIBLE);
appBar_hint.setVisibility(View.GONE);
}
return;
}
// 折叠状态
if (verticalOffset >= appBarLayout.getTotalScrollRange()) {
if (state != STAET_COLLAPSED) {
// 是折叠状态,重置标记为折叠,显示 appbar 布局控件
state = STAET_COLLAPSED;
appBar_hint.setVisibility(View.VISIBLE);
appBar_show.setVisibility(View.GONE);
}
return;
}
// 中间状态
if (state != STAET_ING) {
// 是中间状态,重置标记为ing,显示 appbar 布局控件
state = STAET_ING;
appBar_show.setVisibility(View.VISIBLE);
appBar_hint.setVisibility(View.GONE);
}
}
});
mFAB.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (state == STAET_EXPANDED) {
if (state_scroll == SCROLL_ING) {
// 取消滚动控件的嵌套滚动
mScrollView.setNestedScrollingEnabled(false);
state_scroll = SCROLL_NO;
} else if (state_scroll == SCROLL_NO) {
mScrollView.setNestedScrollingEnabled(true);
state_scroll = SCROLL_ING;
}
}
}
});
mPlayButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (state == STAET_COLLAPSED) {
// 处于折叠状态时才能玩
mAppBar.setExpanded(true);
state = STAET_EXPANDED;
}
}
});
}
private void intiStateView() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
WindowManager.LayoutParams layoutParams = getWindow().getAttributes();
layoutParams.flags = (layoutParams.flags | WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
}