android ui 学习系列 - 初识 CoordinatorLayout

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 的最好的初始资料,我们先来看这个


11111.gif

系统的 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折叠时,此布局也会有视差折叠效果
  • layout_collapseParallaxMultiplier
    视差滚动因子,值为:0~1。
FloatingActionButton 的参数设置:
  • layout_anchor
    设置锚点,FloatingActionButton 的中心点和谁对齐
  • layout_anchorGravity
    锚点对齐位置

好了这些布局的关系和属性设置我们都了解了之后就可以自己来玩来,这里面可是大有可为的,咱们来好好玩玩


自由使用系统默认提供的 Behavior

我们不要局限在系统给我们提供的 ScrollActivity 的写法中,适当的变通还是可以有点啊,比如现在很常见的,标题栏中我们加一张图片进去做打底图,或者不用 toolBar ,自己封装组合 view 都是可以的,我们先来做下面这个样式的


ezgif.com-video-to-gif.gif

这个实现还是很简单的,我们先来看看 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 的颜色设置为黑色

Snip20171028_11.png

imageview

不知道大家注意没有,我们设置 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 的高度才行,要不就会挤在一起


Snip20171028_15.png

那么我们怎么解决这个问题呢,看下面:

在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>
ezgif.com-video-to-gif.gif

为啥要特别说 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 过来的,放哔哩哔哩这个也算是经典学习类目之一了

828721-61defc66bb3e9fb8.gif

我录屏转换gif 太大了,特意压缩了下分辨率才能传上来,效果可能不是很好


ezgif.com-video-to-gif.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);
        }
    }

参考资料

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容