悬浮按钮:FloatingActionButton
按照Material Design的理念,应用程序的界面不仅仅是一个平面,而应该是有立体效果的。在官方给出的示例中,最简单且最具代表性的立面设计就是悬浮按钮了,这种按钮不属于主界面平面的一部分,而是位于另外一个维度的,因此就会给人一种悬浮的感觉
FloatingActionButton是Material库中提供的一个控件,这个控件可以帮助我们比较轻松地实现悬浮按钮的效果,还可以通过给按钮指定一个图标来表明这个按钮的作用是什么
下面开始具体实现。首先仍然需要提前准备好一个图标,这里放置了一张ic_done.png到drawable-xxhdpi目录下。然后修改activity_main.xml中的代码
<androidx.drawerlayout.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawerLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:src="@drawable/ic_done" />
</FrameLayout>
...
</androidx.drawerlayout.widget.DrawerLayout>
这里在主屏幕布局中加入了一个FloatingActionButton。这个控件的用法并没有什么特别的地方,layout_width和layout_height属性都指定成wrap_content,layout_gravity属性指定将这个控件放置于屏幕的右下角
其中end的工作原理和之前的start是一样的,即如果系统语言是从左往右的,那么end就表示在右边,如果系统语言是从右往左的,那么end就表示在左边
然后通过layout_margin属性给控件的四周留点边距,紧贴着屏幕边缘肯定是不好看的,最后通过src属性给FloatingActionButton设置了一个图标
运行程序,就可以看到一个漂亮的悬浮按钮就在屏幕的右下方出现了
如果仔细观察的话,会发现这个悬浮按钮的下面还有一点阴影。其实这很好理解,因为FloatingActionButton是悬浮在当前界面上的,既然是悬浮,那么理所应当会有投影,Material库连这种细节都帮我们考虑到了,而且悬浮的高度还可以被指定
使用app:elevation属性给FloatingActionButton指定一个高度值。高度值越大,投影范围也越大,但是投影效果越淡;高度值越小,投影范围也越小,但是投影效果越浓
接下来增加FloatingActionButton的点击事件,毕竟,一个按钮首先要能点击才有意义。修改MainActivity中的代码,如下所示:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
...
fab.setOnClickListener {
Toast.makeText(this, "FAB clicked", Toast.LENGTH_SHORT).show()
}
}
...
}
其实它和普通的Button其实没什么两样,都是调用setOnClickListener()方法来设置按钮的点击事件,
这里只是弹出了一个Toast
重新运行程序,并点击“FloatingActionButton”
可交互提示:Snackbar
现在已经了解了FloatingActionButton的基本用法,不过在处理点击事件的时候,仍然是使用Toast作为提示工具的
而Material库提供的一个更加先进的提示工具——Snackbar
Snackbar并不是Toast的替代品,它们有着不同的应用场景。Toast的作用是告诉用户现在发生了什么事情,但用户只能被动接收这个事情,因为没有什么办法能让用户进行选择。而Snackbar则在这方面进行了扩展,它允许在提示中加入一个可交互按钮,当用户点击按钮的时候,可以执行一些额外的逻辑操作
打个比方,如果在执行删除操作的时候只弹出一个Toast提示,那么用户要是误删了某个重要数据的话,肯定会十分抓狂吧,但是如果增加一个Undo按钮,就相当于给用户提供了一种弥补措施,从而大大降低了事故发生的概率,提升了用户体验
Snackbar的用法也非常简单,它和Toast是基本相似的,只不过可以额外增加一个按钮的点击事件。修改MainActivity中的代码,如下所示:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
...
fab.setOnClickListener { view ->
Snackbar.make(view, "Data deleted", Snackbar.LENGTH_SHORT).setAction("Undo") {
Toast.makeText(this, "Data restored", Toast.LENGTH_SHORT).show()
}
.show()
}
}
...
}
这里调用了Snackbar的make()方法来创建一个Snackbar对象
make()方法的第一个参数需要传入一个View,只要是当前界面布局的任意一个View都可以,Snackbar会使用这个View自动查找最外层的布局,用于展示提示信息;第二个参数就是Snackbar中显示的内容;第三个参数是Snackbar显示的时长,这些和Toast都是类似的
接着这里又调用了一个setAction()方法来设置一个动作,从而让Snackbar不仅仅是一个提示,而是可以和用户进行交互的。简单起见,我们在动作按钮的点击事件里面弹出一个Toast提示。最后调用show()方法让Snackbar显示出来
运行一下程序,并点击悬浮按钮
可以看到,Snackbar从屏幕底部出现了,上面有我们设置的提示文字,还有一个“Undo”按钮,按钮是可以点击的。过一段时间后,Snackbar会自动从屏幕底部消失。
不管是出现还是消失,Snackbar都是带有动画效果的,因此视觉体验也会比较好
加强版的FrameLayout:CoordinatorLayout
刚才弹出的Snackbar提示将悬浮按钮遮挡住了,而如果能让CoordinatorLayout监听到Snackbar的弹出事件,那么它会自动将内部的FloatingActionButton向上偏移,从而确保不会被Snackbar遮挡,这时可以使用CoordinatorLayout来实现
CoordinatorLayout可以说是一个加强版的FrameLayout,由AndroidX库提供。它在普通情况下的作用和FrameLayout基本一致,但是它拥有一些额外的Material能力
CoordinatorLayout可以监听其所有子控件的各种事件,并自动做出最为合理的响应
CoordinatorLayout的使用也非常简单,只需要将原来的FrameLayout替换一下就可以了。修改activity_main.xml中的代码,如下所示:
<androidx.drawerlayout.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawerLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:src="@drawable/ic_done" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
...
</androidx.drawerlayout.widget.DrawerLayout>
由于CoordinatorLayout本身就是一个加强版的FrameLayout,因此这种替换不会有任何的副作用。现在重新运行一下程序,并点击悬浮按钮
可以看到,悬浮按钮自动向上偏移了Snackbar的同等高度,从而确保不会被遮挡。当Snackbar消失的时候,悬浮按钮会自动向下偏移回到原来的位置
悬浮按钮的向上和向下偏移也是伴随着动画效果的,且和Snackbar完全同步,整体效果看上去特别赏心悦目