TrainApp 状态栏伸缩特效

290 阅读3分钟

目前很常见的状态栏伸缩特效在Android5.0后我们可以通过MaterialDesign库很容易实现,该库的基础就是CoordinatorLayout即协调布局。

所谓的协调布局指的就是内部控件之间彼此存在着动作关联,比如A视图位置发生变化的时候,B视图位置按照一定的规律也进行相应的变化。

首先我们导入CoordinatorLayout库:

或者在Module的build.gradle中直接添加:

implementation 'androidx.coordinatorlayout:coordinatorlayout:1.1.0'

使用的时候我们需要把coordinatorlayout作为布局文件的根布局:

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/cl_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

</androidx.coordinatorlayout.widget.CoordinatorLayout>

协调布局实现效果类似于相对布局RelativeLayout,如果我们需要指定子视图的位置,可以通过下面几个属性实现:

  • android:layout_gravity 指定子视图在coordinatorlayout内部的对其方式
  • layout_anchor 指定视图参照物
  • layout_anchorGravity 指定本视图相对于参照物的对其方式

看个简单的例子:

<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/cl_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:id="@+id/ll_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <com.google.android.material.button.MaterialButton
            android:id="@+id/btn_toast"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="点击显示Toast内容" />
    </LinearLayout>

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/ic_home_black"
        app:layout_anchor="@id/ll_main"
        android:layout_margin="30dp"
        app:layout_anchorGravity="bottom|right" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

然后我们在重写LaunchFragment的onActivityCreated方法,在其中为button绑定事件:

   override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        btn_toast.setOnClickListener {
            Snackbar.make(
                cl_main,
                "我就想看看floatbutton会怎么变化",
                Snackbar.LENGTH_SHORT
            ).show()
        }
    }

效果图:

接下来我们在来看看AppBarLayout,该布局继承自线性布局RelativeLayout,除了线性布局的属性外,还拥有下面几个常用的功能:

  • 支持相应页面主题的滑动行为,也就是说当我们的页面进行上滑或者下拉操作的时候,该布局可以捕捉到该操作
  • 捕获到上述操作后,能够通知toolbar按照指定的规则进行变化

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.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">

    <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">


        <com.google.android.material.appbar.MaterialToolbar
            android:id="@+id/toolbar"
            style="@style/ToolbarTheme"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            app:layout_collapseMode="pin"
            app:layout_scrollFlags="scroll|enterAlways"
            app:popupTheme="@style/popTheme" />
    </com.google.android.material.appbar.AppBarLayout>

    <fragment
        android:id="@+id/nav_host"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        app:navGraph="@navigation/navigation" />/>

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottom_nav"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:background="@color/color_bottom_view"
        app:labelVisibilityMode="labeled"
        app:menu="@menu/menu_bottom" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>

关于scroll flags的源码定义:

接下来我们在LaunchFragment中使用NestedScrollView模拟,布局文件:

<androidx.constraintlayout.widget.ConstraintLayout 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">

    <androidx.core.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <com.google.android.material.textview.MaterialTextView
            android:id="@+id/home_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Launch Fragment"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    </androidx.core.widget.NestedScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>

效果:

接下来我们需要了解一下可折叠工具栏布局CollapsingToolbarLayout,它是专门用来实现子布局内不同元素响应滚动细节的布局。

在app运行的时候,工具栏的高度其实是固定不变的,发生高度变化的布局其实就是CollapsingToolbarLayout。一般情况下我们把CollapsingToolbarLayout和工具栏背景设置为相同的颜色,所以看起来像是统一的工具栏在发生收缩和展开效果 。

和CollapsingToolbarLayout配合使用时我们需要关注toolbar以下的属性值:

  • app:layout_collapseMode 折叠属性模式,指定子视图的折叠模式

pin:固定模式,toolbar固定,不受CollapsingToolbarLayout折叠影响

parallax:视差模式,跟随CollapsingToolbarLayout的收缩和展开,toolbar也会跟着收缩与展开。

none:默认值,CollapsingToolbarLayout折叠多少距离,toolbar也会跟着移动多少距离

其中parallax属性可通过app:layout_collapseParallaxMulti指定,该属性指定视差模式的折叠距离系数。当属性值为1.0的时候,折叠效果等同于pin模式。属性值为0.0的时候等同于none模式。

这里我们使用pin模式作为示例:

 <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="@dimen/appBarSize">

        <com.google.android.material.appbar.CollapsingToolbarLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

            <com.google.android.material.appbar.MaterialToolbar
                android:id="@+id/toolbar"
                style="@style/ToolbarTheme"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_collapseMode="pin"
                app:popupTheme="@style/popTheme" />
        </com.google.android.material.appbar.CollapsingToolbarLayout>
    </com.google.android.material.appbar.AppBarLayout>

这里我们指定了AppBarLayout的高度,大家可以看到其上面的部分其实就是工具栏的位置,下面的部分就是CollapsingToolbarLayout去出toolbar的部分,通常我们会设置相同背景色。可以直接指定AppBarLayout的背景:

    <com.google.android.material.appbar.AppBarLayout
        .....
        android:background="@color/color_header_bar">
        .....
    </com.google.android.material.appbar.AppBarLayout>