Android界面设计:Material Design之可折叠式标题栏


虽说现在的标题栏是使用Toolbar来编写的,不过它看上去和传统的ActionBar没什么两样,只不过可以响应RecyclerView的滚动事件来进行隐藏和显示。还可以根据自己的喜好随意定制标题栏的样式
这里实现一个可折叠式标题栏的效果,要借助CollapsingToolbarLayout这个工具

可折叠式标题栏:CollapsingToolbarLayout

顾名思义,CollapsingToolbarLayout是一个作用于Toolbar基础之上的布局,它也是由Material库提供的
CollapsingToolbarLayout可以让Toolbar的效果变得更加丰富,不仅仅是展示一个标题栏,而且能够实现非常华丽的效果
不过,CollapsingToolbarLayout是不能独立存在的,它在设计的时候就被限定只能作为AppBarLayout的直接子布局来使用。而AppBarLayout又必须是CoordinatorLayout的子布局,因此要实现的功能其实需要综合运用前面所了解的各种知识

首先我们需要一个额外的Activity作为水果的详情展示界面,右击com.example.materialtest包→New→Activity→Empty Activity,创建一个FruitActivity,并将布局名指定成activity_fruit.xml
然后开始编写水果详情展示界面的布局。由于整个布局文件比较复杂,这里准备采用分段编写的方式
activity_fruit.xml中的内容主要分为两部分,一个是水果标题栏,一个是水果内容详情

首先实现标题栏部分,这里使用CoordinatorLayout作为最外层布局

<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">
	 
</androidx.coordinatorlayout.widget.CoordinatorLayout>

这里就注意要始终记得定义一个xmlns:app的命名空间,在Material Design的开发中会经常用到它

接着在CoordinatorLayout中嵌套一个AppBarLayout

<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.AppBarLayout>
	 
</androidx.coordinatorlayout.widget.CoordinatorLayout>

这里给AppBarLayout定义了一个id,将它的宽度指定为match_parent,高度指定为250 dp

接下来在AppBarLayout中再嵌套一个CollapsingToolbarLayout

<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="@color/colorPrimary"
			 app:layout_scrollFlags="scroll|exitUntilCollapsed">
		 </com.google.android.material.appbar.CollapsingToolbarLayout>
		 
	 </com.google.android.material.appbar.AppBarLayout>
	 
</androidx.coordinatorlayout.widget.CoordinatorLayout>

这里使用了新的布局CollapsingToolbarLayout。其中,id、layout_width和layout_height这几个属性比较简单
android:theme属性指定了一个ThemeOverlay.AppCompat.Dark.ActionBar的主题
app:contentScrim属性用于指定CollapsingToolbarLayout在趋于折叠状态以及折叠之后的背景色,其实CollapsingToolbarLayout在折叠之后就是一个普通的Toolbar,那么背景色肯定应该是colorPrimary了
app:layout_scrollFlags属性也是见过的,只不过之前是给Toolbar指定的,现在也移到外面来了。其中

  • scroll表示CollapsingToolbarLayout会随着水果内容详情的滚动一起滚动,
  • exitUntilCollapsed表示当CollapsingToolbarLayout随着滚动完成折叠之后就保留在界面上,不再移出屏幕。

接下来,在CollapsingToolbarLayout中定义标题栏的具体内容,如下所示:

<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="@color/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.coordinatorlayout.widget.CoordinatorLayout>

在CollapsingToolbarLayout中定义了一个ImageView和一个Toolbar,也就意味着,这个高级版的标题栏将是由普通的标题栏加上图片组合而成的
app:layout_collapseMode它用于指定当前控件在CollapsingToolbarLayout折叠过程中的折叠模式,其中Toolbar指定成pin,表示在折叠的过程中位置始终保持不变,ImageView指定成parallax,表示会在折叠的过程中产生一定的错位偏移,这种模式的视觉效果会非常好

然后准备在activity_fruit.xml中加入一个悬浮按钮。这个界面是一个水果详情展示界面,那么就加入一个表示评论作用的悬浮按钮
首先需要提前准备好一个图标,这里放置了一张ic_comment.png到drawable-xxhdpi目录下
然后修改
activity_fruit.xml中的代码,如下所示:

<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.AppBarLayout>
	 
	 <androidx.core.widget.NestedScrollView
		 android:layout_width="match_parent"
		 android:layout_height="match_parent"
		 app:layout_behavior="@string/appbar_scrolling_view_behavior">
		 ...
	 </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>

这里加入了一个FloatingActionButton,它和AppBarLayout以及NestedScrollView是平级的FloatingActionButton中使用app:layout_anchor属性指定了一个锚点,将锚点设置为AppBarLayout,这样悬浮按钮就会出现在水果标题栏的区域内,接着又使用app:layout_anchorGravity属性将悬浮按钮定位在标题栏区域的右下角

好了,现在终于将整个activity_fruit.xml布局都编写完了。接下来我们开始编写功能逻辑,修改FruitActivity中的代码,如下所示:

class FruitActivity : AppCompatActivity() {
	 companion object {
		 const val FRUIT_NAME = "fruit_name"
		 const val FRUIT_IMAGE_ID = "fruit_image_id"
	 }
	 
	 override fun onCreate(savedInstanceState: Bundle?) {
		 super.onCreate(savedInstanceState)
		 setContentView(R.layout.activity_fruit)
		 val fruitName = intent.getStringExtra(FRUIT_NAME) ?: ""
		 val fruitImageId = intent.getIntExtra(FRUIT_IMAGE_ID, 0)
		 setSupportActionBar(toolbar)
		 supportActionBar?.setDisplayHomeAsUpEnabled(true)
		 collapsingToolbar.title = fruitName
		 Glide.with(this).load(fruitImageId).into(fruitImageView)
		 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)
}

首先,在onCreate()方法中,通过Intent获取了传入的水果名和水果图片的资源id
接着使用了Toolbar的标准用法,将它作为ActionBar显示,并启用Home按钮。由于Home按钮的默认图标就是一个返回箭头,因此就不用额外设置别的图标了
接下来开始填充界面上的内容,调用CollapsingToolbarLayout的setTitle()方法,将水果名设置成当前界面的标题,然后使用Glide加载传入的水果图片,并设置到标题栏的ImageView上面
接着需要填充水果的内容详情,由于这只是一个示例程序,并不需要什么真实的数据,所以使用了一个generateFruitContent()方法将水果名循环拼接500次,从而生成了一个比较长的字符串,将它设置到了TextView上面
最后,在onOptionsItemSelected()方法中处理了Home按钮的点击事件,当点击这个按钮时,就调用finish()方法关闭当前的Activity,从而返回上一个Activity

还差最关键的一步,就是处理RecyclerView的点击事件,不然的话,根本就无法打开FruitActivity。修改FruitAdapter中的代码,如下所示:

class FruitAdapter(val context: Context, val fruitList: List<Fruit>) :
 		RecyclerView.Adapter<FruitAdapter.ViewHolder>() {
	 ...
	 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
		 val view = LayoutInflater.from(context).inflate(R.layout.fruit_item, parent, false)
		 val holder = ViewHolder(view)
		 holder.itemView.setOnClickListener {
			 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)
		 }
		 return holder
	 }
	 ...
}

这里给fruit_item.xml的最外层布局注册了一个点击事件监听器,然后在点击事件中获取当前点击项的水果名和水果图片资源id,把它们传入Intent中,最后调用startActivity()方法启动FruitActivity

运行程序,并点击界面上的任意一个水果,比如点击了葡萄
在这里插入图片描述
可以尝试向上拖动水果内容详情,会发现水果背景图上的标题会慢慢缩小,并且背景图会产生一些错位偏移的效果
在这里插入图片描述
这是由于用户想要查看水果的内容详情,此时界面的重点在具体的内容上面,因此标题栏就会自动进行折叠,从而节省屏幕空间
继续向上拖动,直到标题栏变成完全折叠状态
在这里插入图片描述
可以看到,标题栏的背景图片不见了,悬浮按钮也自动消失了,现在水果标题栏变成了一个最普通的Toolbar。这是由于用户正在阅读具体的内容,需要给他们提供最充分的阅读空间
而如果这个时候向下拖动水果内容详情,就会执行一个完全相反的动画过程

充分利用系统状态栏空间

仔细观察一下,会发现水果的背景图片和系统的状态栏总有一些不搭的感觉,如果能将背景图和状态栏融合到一起,那这个视觉体验绝对能提升好几个档次

想要让背景图能够和系统状态栏融合,需要借助android:fitsSystemWindows这个属性来实现
在CoordinatorLayout、AppBarLayout、CollapsingToolbarLayout这种嵌套结构的布局中,将控件的android:fitsSystemWindows属性指定成true,就表示该控件会出现在系统状态栏里
对应到的程序,那就是水果标题栏中的ImageView应该设置这个属性了
不过只给ImageView设置这个属性是没有用的,必须将ImageView布局结构中的所有父布局都设置上这个属性才可以
修改activity_fruit.xml中的代码,如下所示:

<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"
			 android:fitsSystemWindows="true"
			 app:contentScrim="@color/colorPrimary"
			 app:layout_scrollFlags="scroll|exitUntilCollapsed">
			 
			 <ImageView
				 android:id="@+id/fruitImageView"
				 android:layout_width="match_parent"
				 android:layout_height="match_parent"
				 android:scaleType="centerCrop"
				 android:fitsSystemWindows="true"
				 app:layout_collapseMode="parallax" />
			 ...
		 </com.google.android.material.appbar.CollapsingToolbarLayout>
		 
	 </com.google.android.material.appbar.AppBarLayout>
	 ...
</androidx.coordinatorlayout.widget.CoordinatorLayout>

即使将android:fitsSystemWindows属性都设置好了也没有用,因为还必须在程序的主题中将状态栏颜色指定成透明色才行
指定成透明色的方法很简单,在主题中将android:statusBarColor属性的值指定成@android:color/transparent就可以了

打开res/values/themes.xml文件,添加一个透明主题

<style name="FruitActivityTheme" parent="AppTheme">
 	<item name="android:statusBarColor">@android:color/transparent</item>
</style>

这里添加了一个FruitActivityTheme主题,它是专门给FruitActivity使用的
FruitActivityTheme的父主题是AppTheme,也就是说,它继承了AppTheme中的所有特性
在此基础之上,将FruitActivityTheme中的状态栏的颜色指定成透明色

最后,还需要让FruitActivity使用这个主题才可以,修改AndroidManifest.xml中的代码

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
	 package="com.example.materialtest">
	 
	 <application
		 android:allowBackup="true"
		 android:icon="@mipmap/ic_launcher"
		 android:label="@string/app_name"
		 android:roundIcon="@mipmap/ic_launcher_round"
		 android:supportsRtl="true"
		 android:theme="@style/AppTheme">
		 ...
		 <activity
			 android:name=".FruitActivity"
			 android:theme="@style/FruitActivityTheme">
		 </activity>
	 </application>
	 
</manifest>

使用android:theme属性单独给FruitActivity指定了FruitActivityTheme这个主题

重新运行程序
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值