一、ToolBar
ToolBar是Android 5.0推出的一个新的导航控件用于取代之前的ActionBar,由于其高度的可定制性、灵活性、具有Material Design风格等优点。
1.1 ToolBar属性
整理 Toolbar 比较常用的属性
-
app:navigationIcon
设置 navigation button -
app:logo
设置 logo 图标 -
app:title
设置标题 -
app:titleTextColor
设置标题文字颜色 -
app:subtitle
设置副标题 -
app:subtitleTextColor
设置副标题文字颜色 -
app:titleTextAppearance
设置 title text 相关属性,如:字体,颜色,大小等等 -
app:subtitleTextAppearance
设置 subtitle text 相关属性,如:字体,颜色,大小等等 -
app:logoDescription
logo 描述 -
app:menu
设置menu -
android:background
Toolbar 背景 -
android:theme
主题
1.2 更改主题
为了能够正常使用ToolBar,我们需要隐藏原来的ActionBar,这个可以在主题中修改,在values/styles.xml中做出如下修改:
<style name="AppTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
</style>
1.3 xml中的设置
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/purple_500"
app:menu="@menu/toolbar_menu"
app:title="标题"
app:titleTextColor="@color/white"/>
1.4 Menu的布局文件
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/outLogin"
android:title="退出登录" />
<item
android:id="@+id/appModel"
android:title="夜间模式" />
<item
android:id="@+id/collect"
android:icon="@drawable/ic_baseline_stars_24"
android:title="收藏"
app:showAsAction="ifRoom" />
</menu>
app:showAsAction
属性 ,这个属性是设置菜单该显示方式,取值有 5 种,主要应用的有 ifRoom、never、always 这三种:
- ifRoom 表示 如果 Toolbar 上有显示空间就显示在 Toolbar 上,如果没有空间就展示在溢出菜单里;
- never 表是总是显示在溢出菜单里;
- always 表示总是显示在 Toolbar 上;
注意: Toolbar中的action按钮只会显示图标,菜单中的action按钮只会显示文字。
1.5 代码中的设置
// 设置ToolBar标题
toolbar.title = "ToolBar"
// 设置ToolBar副标题
toolbar.subtitle = "this is toolbar"
// 设置navigation button
toolbar.navigationIcon = resources.getDrawable(R.drawable.ic_baseline_arrow_back_24, null)
// 设置Logo图标
toolbar.logo = resources.getDrawable(R.drawable.ic_baseline_group_24, null)
// 设置溢出菜单的图标
toolbar.overflowIcon = resources.getDrawable(R.drawable.ic_baseline_more_vert_24, null)
// 设置Menu
toolbar.inflateMenu(R.menu.toolbar_menu)
// 设置Navigation Button监听
toolbar.setNavigationOnClickListener {
finish()
}
// 设置Menu监听
toolbar.setOnMenuItemClickListener { item ->
when (item.itemId) {
R.id.collect -> Toast.makeText(this@ToolBarActivity, "收藏", Toast.LENGTH_SHORT)
.show()
R.id.outLogin -> Toast.makeText(this@ToolBarActivity, "退出登录", Toast.LENGTH_SHORT)
.show()
R.id.appModel -> Toast.makeText(this@ToolBarActivity, "夜间模式", Toast.LENGTH_SHORT)
.show()
}
false
}
1.6 标题居中
查看api,toolbar没有使其居中的方法,也就提供了使其距左右,上下边距大小的方法。不过不用担心,这里还是有办法的。看如下代码:
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/purple_500"
app:navigationIcon="@drawable/abc_vector_test"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="标题"
android:textColor="@color/white"
android:textSize="22sp" />
</androidx.appcompat.widget.Toolbar>
效果图:
二、MaterialToolbar
除了使用toolbar,也可以使用MaterialToolbar
2.1 解剖结构和关键属性
基本的属于和toolbar相同
- (1)容器
- (2)导航图标(可选)
- (3)标题(可选)
- (4)动作项目(可选)
- (5)溢出菜单(可选)
2.1.1 容器属性
Attribute | Related method(s) | Default value | |
Color | android:background | setBackground、getBackground | ?attr/colorPrimary |
MaterialToolbar Elevation | android:elevation | setElevation、getElevation | 4dp |
AppBarLayout elevation | android:stateListAnimator | setStateListAnimator、getStateListAnimator | 0dp to 4dp |
2.1.2 导航图标属性
Attribute | Related method(s) | Default value | |
MaterialToolbar icon | app:navigationIcon | setNavigationIcon、getNavigationIcon | null |
MaterialToolbar icon color | app:navigationIconTint | setNavigationIconTint | ?attr/colorControlNormal (as Drawable tint) |
2.1.3 标题属性
Attribute | Related method(s) | Default value | |
MaterialToolbar title text | app:title | setTitle、getTitle | null |
MaterialToolbar subtitle text | app:subtitle | setSubtitle、getSubtitle | null |
MaterialToolbar title color | app:titleTextColor | setTitleTextColor | ?android:attr/textColorPrimary |
MaterialToolbar subtitle color | app:subtitleTextColor | setSubtitleTextColor | ?android:attr/textColorSecondary |
2.1.4 动作项目属性
Attribute | Related method(s) | Default value | |
MaterialToolbar menu | app:menu | inflateMenu、getMenu | null |
MaterialToolbar icon color | N/A | N/A | ?attr/colorControlNormal (as Drawable tint) |
2.1.5 溢出菜单属性
Attribute | Related method(s) | Default value | |
MaterialToolbar icon | android:src and app:srcCompat in actionOverflowButtonStyle (in app theme) | setOverflowIcon、getOverflowIcon | @drawable/abc_ic_menu_overflow_material (before API 23) or @drawable/ic_menu_moreoverflow_material (after API 23) |
MaterialToolbar overflow theme | app:popupTheme | setPopupTheme、getPopupTheme | @style/ThemeOverlay.MaterialComponents.* |
三、CoordinatorLayout
CoordinatorLayout,又名协调者布局。简单来说,CoordinatorLayout是用来协调其子view并以触摸影响布局的形式产生动画效果的一个super-powered FrameLayout。注意:CoordinatorLayout是一个顶级父View。
而CoordinatorLayout主要依靠Behavior
进行协调。在CoordinatorLayout内部,每个child都必须带一个Behavior(其实不携带也行,不携带就不能被协调),CoordinatorLayout就根据每个child所携带的Behavior信息进行协调。
3.1 浮动操作按钮与Snackbar
CoordinatorLayout可以用来配合浮动操作按钮的 layout_anchor
和 layout_gravity
属性创造出浮动效果,layout_anchor 指定参照物, anchorGravity
指定相对于参照物的位置,设置为 bottom|right则表示将FloatingActionButton放置于参照物的右下角。
当Snackbar在显示的时候,往往出现在屏幕的底部。为了给Snackbar留出空间,浮动操作按钮需要向上移动。
示例XML代码:
<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/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvToDoList"
android:layout_width="match_parent"
android:layout_height="match_parent"></androidx.recyclerview.widget.RecyclerView>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:layout_margin="16dp"
android:src="@mipmap/ic_launcher"
app:layout_anchor="@id/rvToDoList"
app:layout_anchorGravity="bottom|right|end" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
只要使用CoordinatorLayout作为基本布局,将自动产生向上移动的动画。在这里,FloatingActionButton并没有设置Behavior,因为浮动操作按钮有一个默认的 behavior
来检测Snackbar的添加并让按钮在Snackbar之上呈现上移与Snackbar等高的动画。
3.2 AppBarLayout 与NestedScrollView
对于AppBarLayout 与NestedScrollView的使用下面有例子说明,我们可以看到NestedScrollView会使用app:layout_behavior="@string/appbar_scrolling_view_behavior"
,指向AppBarLayout.ScrollingViewBehavior
,用来通知AppBarLayout 这个特殊的view何时发生了滚动事件,这个behavior需要设置在触发滚动事件的view之上。
NestedScrollView
即 支持嵌套滑动的 ScrollView
。
因此,在一些需要支持嵌套滑动的情景中,比如一个 ScrollView
内部包裹一个 RecyclerView
,那么就会产生滑动冲突,这个问题就需要你自己去解决。而如果使用 NestedScrollView
包裹 RecyclerView
,嵌套滑动天然支持,你无需做什么就可以实现前面想要实现的功能了。
四、AppBarLayout
AppBarLayout 继承自LinearLayout,子控件默认为竖直方向显示,可以用它来让Toolbar响应滚动事件;它支持滑动手势;它的子控件可以通过在代码里调用setScrollFlags(int)
或者在XML里app:layout_scrollFlags
来设置它的滑动手势。当然实现这些的前提是它的根布局必须是 CoordinatorLayout。
4.1 scroll
Child View 伴随着滚动事件而滚出或滚进屏幕。注意两点:第一点,如果使用了其他值,必定要使用这个值才能起作用;第二点:如果在这个child View前面的任何其他Child View没有设置这个值,那么这个Child View的设置将失去作用。
示例XML代码:
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/purple_500"
app:layout_scrollFlags="scroll|enterAlways"
app:menu="@menu/toolbar_menu"
app:navigationIcon="@drawable/abc_vector_test"
app:title="标题"
app:titleTextColor="@color/white" />
</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">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:text="@string/large_text" />
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
下面是app:layout_scrollFlags="scroll"
的效果:
往上滑动就不说了,往下滑动就是当下面的滚动布局滑动到顶端时,标题栏toolbar才会滑出来。
4.2 enterAlways
快速返回模式。其实就是向下滚动时Scrolling View和Child View之间的滚动优先级问题。对比scroll
和scroll | enterAlways
设置,发生向下滚动事件时,前者优先滚动Scrolling View,后者优先滚动Child View,当优先滚动的一方已经全部滚进屏幕之后,另一方才开始滚动。
下面是app:layout_scrollFlags="scroll|enterAlways"
的效果:
往下滑动时,标题栏 toolbar 会优先滑出来,然后滚动布局才开始滑动。
4.3 snap
简单理解,就是Child View滚动比例的一个吸附效果。也就是说,Child View不会存在局部显示的情况,滚动Child View的部分高度,当我们松开手指时,Child View要么向上全部滚出屏幕,要么向下全部滚进屏幕,有点类似ViewPager的左右滑动。
下面是app:layout_scrollFlags="scroll|enterAlways|snap"
的效果:
4.4 enterAlwaysCollapsed
enterAlways的附加值。这里涉及到Child View的高度和最小高度,向下滚动时,Child View先向下滚动最小高度值,然后Scrolling View开始滚动,到达边界时,Child View再向下滚动,直至显示完全。
示例XML代码:
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="@color/purple_500"
android:minHeight="56dp"
app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"
app:menu="@menu/toolbar_menu"
app:navigationIcon="@drawable/abc_vector_test"
app:title="标题"
app:titleTextColor="@color/white" />
</com.google.android.material.appbar.AppBarLayout>
下面是app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"
的效果:
默认toolbar 显示的正常高度值我们设置成的100dp,当我们上滑的时候,toolbar直到完全隐藏时,下面的滚动布局才开始发生滚动;下滑时toolbar会优先滑出设置的最小高度值,然后当滚动布局下滑到顶部时,后面的toolbar部分,才开始完全显示(滑)出来。
4.5 exitUntilCollapsed
这里也涉及到最小高度。发生向上滚动事件时,Child View向上滚动退出直至最小高度,然后Scrolling View开始滚动。也就是,Child View不会完全退出屏幕。
下面是app:layout_scrollFlags="scroll|enterAlways|exitUntilCollapsed"
的效果:
默认toolbar 显示的正常高度值我们设置成的100dp,当我们上滑的时候,高度达到最小高度40dp时,toolbar不在滑动了。
五、CollapsingToolbarLayout
collapsingToolbarLayout一般不会单独出现在布局文件中,而是作为另一个控件CoordinatorLayout的子元素出现,CoordinatorLayout(协调布局)这个控件很强大, design库的动态效果都依赖于该控件。
先看下效果图:
示例XML代码:
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="180dp">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_scrollFlags="scroll">
<ImageView
android:layout_width="match_parent"
android:layout_height="180dp"
android:scaleType="centerCrop"
android:src="@drawable/a"
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"
app:menu="@menu/toolbar_menu"
app:navigationIcon="@drawable/abc_vector_test"
app:title="标题"
app:titleTextColor="@color/white" />
</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">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:text="@string/large_text" />
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
这里我们重点关注 CollapsingToolbarLayout控件 以下的几个属性:
-
app:contentScrim="?attr/colorPrimary"
当上滑到toolbar 高度期间直到达到toolbar高度时,给toolbar设置的背景色,以及过渡颜色。当然,这里不仅仅可以设置颜色,也可以设置图片。 如果不设置该属性,标题栏会过渡为以之前的图片为背景。 -
app:layout_scrollFlags="scroll"
和介绍 AppBarLayout 时,给Toolbar 设置和配置一样。其效果上图已做展示。 -
app:expandedTitleGravity="center_horizontal"
从单词意思可以看出,这是展开后,title的位置。 -
app:expandedTitleMarginStart="50dp"
从单词意思可以看出,这是展开后,title 距离开始位置的边距。 -
app:collapsedTitleGravity="center"
从单词意思可以看出,这是收缩后,title 位置 -
app:expandedTitleTextAppearance="@style/transparentText"
展开后标题文字的样式 -
app:collapsedTitleTextAppearance="@style/ToolbarTitle"
折叠后标题文字的样式
app:layout_collapseMode
有两个参数:
- parallax 视图将以视差方式滚动。请参见layout_collapseParallaxMultiplier属性更改乘数。
- pin 视图将固定在适当的位置。
六、上下文操作栏
上下文操作栏为所选项目提供操作。顶部的应用栏可以转换为上下文操作栏,在执行某项操作或将其撤消之前一直保持活动状态。
这个会创建一个单独的操作栏,不需要在布局文件中添加标签
In res/values/themes.xml
:
<style name="Theme.App" parent="Theme.MaterialComponents.*.NoActionBar">
...
<item name="windowActionModeOverlay">true</item>
<item name="actionModeStyle">@style/Widget.App.ActionMode</item>
<item name="actionModeCloseDrawable">@drawable/ic_close_24dp</item>
<item name="actionBarTheme">@style/ThemeOverlay.MaterialComponents.Dark.ActionBar</item>
</style>
In res/values/styles.xml
:
<style name="Widget.App.ActionMode" parent="Widget.AppCompat.ActionMode">
<item name="titleTextStyle">?attr/textAppearanceHeadline6</item>
<item name="subtitleTextStyle">?attr/textAppearanceSubtitle1</item>
<item name="background">@color/material_grey_900</item>
</style>
In res/menu/contextual_action_bar.xml
:
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/share"
android:icon="@drawable/ic_share_24dp"
android:title="@string/share"
android:contentDescription="@string/content_description_share"
app:showAsAction="ifRoom" />
<item
android:id="@+id/delete"
android:icon="@drawable/ic_delete_24dp"
android:title="@string/delete"
android:contentDescription="@string/content_description_delete"
app:showAsAction="ifRoom" />
<item
android:id="@+id/more"
android:title="@string/more"
android:contentDescription="@string/content_description_more"
app:showAsAction="never" />
</menu>
In code:
val callback = object : ActionMode.Callback {
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
menuInflater.inflate(R.menu.contextual_action_bar, menu)
return true
}
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean {
return false
}
override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?): Boolean {
return when (item?.itemId) {
R.id.share -> {
// Handle share icon press
true
}
R.id.delete -> {
// Handle delete icon press
true
}
R.id.more -> {
// Handle more item (inside overflow menu) press
true
}
else -> false
}
}
override fun onDestroyActionMode(mode: ActionMode?) {
}
}
val actionMode = startSupportActionMode(callback)
actionMode?.title = "1 selected"