安卓UI组件开发学习——可折叠式标题栏的组合使用

564 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 23 天,点击查看活动详情

前言

之前在滑动菜单使用的文章中,我们的标题栏是使用Toolbar写的,但与我们基础的ActionBar看不出有什么不一样的地方,除了可以响应RecyclerView的滚动事件来进行隐藏和显示,所以我们可以采用Material Design去设计一个可折叠式标题栏。

正篇

首先我们看看接下来几个要用到的组件是如何使用的。

CollapsingToolbarLayout

这是一个作用于Toolbar基础之上的布局,同样由Material库提供,而它的作用就是使Toolbar效果很丰富。
但要注意的是,这个布局不可以单独使用,我们要让他作为AppBarLayout的直接子布局使用,而AppBarLayout又必须是CoordinatorLayout的子布局,所以我们需要组合来使用:

<?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"
    android:fitsSystemWindows="true">

    <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/appBar"
        android:layout_width="match_parent"
        android:layout_height="250dp"
        android:fitsSystemWindows="true">

        <com.google.android.material.appbar.CollapsingToolbarLayout
            android:id="@+id/collapsingToolbar"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
            app:contentScrim="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

        </com.google.android.material.appbar.CollapsingToolbarLayout>

    </com.google.android.material.appbar.AppBarLayout>

</androidx.coordinatorlayout.widget.CoordinatorLayout>

我们布局采用最外层为CoordinatorLayout,然后嵌套一个AppBarLayout,最后往AppBarLayout中再嵌套我们的新布局CollapsingToolbarLayout,而新布局中我们通过android:theme属性去指定了主题,使用app:contentScrim属性设置在该布局趋于折叠状态和折叠之后的背景色,我们的CollapsingToolbarLayout布局其实在折叠之后就呈现出之前我们使用的Toolbar的样式,所以我们给这个背景色再设置为colorPrimary,而app:layout_scrollFlags代表设置滚动方式的,scroll就是随着下面的内容滚动而一起滚动,exitUntilCollapsed则是随着滚动完成折叠之后就保留在界面上,不再移出屏幕。

接下来我们再为CollapsingToolbarLayout定义标题栏的具体内容:

<com.google.android.material.appbar.CollapsingToolbarLayout
    android:id="@+id/collapsingToolbar"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
    app:contentScrim="?attr/colorPrimary"
    app:layout_scrollFlags="scroll|exitUntilCollapsed">

    <ImageView
        android:id="@+id/fruitImageView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="centerCrop"
        app:layout_collapseMode="parallax" />

    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        app:layout_collapseMode="pin" />

</com.google.android.material.appbar.CollapsingToolbarLayout>

我们为其加了一个ImageView子布局和Toolbar子布局,这就表示CollapsingToolbarLayout这个更高级的标题栏样式由一张图片与一个普通标题栏组合而成。

这里的属性设置中,app:layout_collapseMode是用于指定当前控件在CollapsingToolbarLayout折叠过程中的折叠模式,pin代表折叠的过程中位置始终保持不变,而parallax则表示会在折叠的过程中产生一定的错位偏移,达到更好的视觉效果。

写好我们的标题栏后,我们在标题栏布局下面添加一个滚动视图以及一个悬浮按钮,这里滚动视图我们使用布局NestedScrollView,它和 ScrollView 布局一样,都只能接受一个直接子布局,如果需要滚动布局内部加很多其他布局组件之类的,通常就会先在滚动布局里面嵌套一个LinearLayout,然后在LinearLayout中写其他的子布局结构(具体内容),布局代码如下:

<?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="250dp">

        <com.google.android.material.appbar.CollapsingToolbarLayout
            android:id="@+id/collapsingToolbar"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
            app:contentScrim="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

            <ImageView
                android:id="@+id/fruitImageView"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:scaleType="centerCrop"
                app:layout_collapseMode="parallax" />

            <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_collapseMode="pin" />

        </com.google.android.material.appbar.CollapsingToolbarLayout>

    </com.google.android.material.appbar.AppBarLayout>

    <androidx.core.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <LinearLayout
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <com.google.android.material.card.MaterialCardView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="15dp"
                android:layout_marginLeft="15dp"
                android:layout_marginRight="15dp"
                android:layout_marginTop="35dp"
                app:cardCornerRadius="4dp">

                <TextView
                    android:id="@+id/fruitContentText"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_margin="10dp" />

            </com.google.android.material.card.MaterialCardView>

        </LinearLayout>

    </androidx.core.widget.NestedScrollView>

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="16dp"
        android:src="@drawable/ic_comment"
        app:layout_anchor="@id/appBar"
        app:layout_anchorGravity="bottom|end" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>

其中app:layout_behavior属性指定了一个布局行为,我们将MaterialCardView作为其内部的子布局然后为其加上TextView形成一个文字卡片样式布局,我们最后在布局中又增加了一个FloatingActionButton作为悬浮按钮,这样,我们的一套布局就完成了。

完成布局代码构建后,我们需要在Avtivity做出相应调整,代码如下:

package com.example.materialdemo

import android.os.Bundle
import android.view.MenuItem
import androidx.appcompat.app.AppCompatActivity
import com.bumptech.glide.Glide
import com.example.materialdemo.databinding.ActivityFruitBinding

class FruitActivity : AppCompatActivity() {

    companion object {
        const val FRUIT_NAME = "fruit_name"
        const val FRUIT_IMAGE_ID = "fruit_image_id"
    }

    lateinit var binding: ActivityFruitBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityFruitBinding.inflate(layoutInflater)
        setContentView(binding.root)
        val fruitName = intent.getStringExtra(FRUIT_NAME) ?: ""
        val fruitImageId = intent.getIntExtra(FRUIT_IMAGE_ID, 0)
        setSupportActionBar(binding.toolbar)
        supportActionBar?.setDisplayHomeAsUpEnabled(true)
        binding.collapsingToolbar.title = fruitName
        Glide.with(this).load(fruitImageId).into(binding.fruitImageView)
        binding.fruitContentText.text = generateFruitContent(fruitName)
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        when (item.itemId) {
            android.R.id.home -> {
                finish()
                return true
            }
        }
        return super.onOptionsItemSelected(item)
    }

    private fun generateFruitContent(fruitName: String) = fruitName.repeat(500)

}

不过,如果想启动我们这个新的Activity,首先应该在我们的首页添加跳转的点击事件,在点击事件里使用Intent跳转页面:

val position = holder.adapterPosition
val fruit = fruitList[position]
val intent = Intent(context, FruitActivity::class.java).apply {
    putExtra(FruitActivity.FRUIT_NAME, fruit.name)
    putExtra(FruitActivity.FRUIT_IMAGE_ID, fruit.imageId)
}
context.startActivity(intent)

最终效果如下:

985a4ddc268a761c790282d0c7ba8b2e.gif

总结

Material库的东西太多,这只是其中用的比较多的地方,需要我们日积月累,慢慢熟悉它们。