轻松上手Snackbar控件

一、前言

    Snackbar 控件是 Material design 产物,它的作用是显示弹出消息,这个系统的 Toast 类似,但是它又有优于 Toast 的地方,它可以添加用户交互,如果需要,它可以一直显示,直至操作取消或者调用取消接口。如果你的应用中有这的场景,那么就可以考虑 Snackbar 这个控件了。Snackbar 提示显示在屏幕下方(如果是大屏幕,显示在左下角),Snackbar 提示将会在屏幕内容最上层显示,不能叠加显示(每次只能显示一个,这个跟 Toast 一样),同时显示多个将会放入队里,一个个显示。

二、轻松上手

2.1 引入依赖

    Snackbar 控件不是系统控件,使用需要引入依赖库:

// 如果你的项目使用support支持库,请使用该依赖
implementation "com.android.support:design:28.0.0"

// 如果你的项目使用androidx支持库,请使用该依赖
implementation "com.google.android.material:material:1.2.1"

注意:引入依赖时根据项目选择其中一个,support支持库和androidx支持库不能混用。

2.2 开始使用

2.2.1 简单示例

    SnackbarToast 在使用过程中基本差不多,其中一个不同点就是,构建 Snackbar 的时候,需要传入一个 View 视图,这个视图是用来寻找父级视图的,这个视图不能为空,如果使用了 setAnchorView(int) API 设置了锚点 View 的话,锚点 View也会在这个 View 视图中进行寻找。另外就是延迟时间上有差别, Snackbar 有三个延迟时间选项,分别是:

  • Snackbar.LENGTH_SHORT:短时显示

  • Snackbar.LENGTH_LONG:长时显示

  • Snackbar.LENGTH_INDEFINITE:未定义,不会自动消失,显示之后需要其他条件触发才会消失(比如滑动,动作触发等)。

  • 示例代码

 val s = Snackbar.make(findViewById(R.id.linearLayout), "这是测试Snackbar", Snackbar.LENGTH_LONG)
                s.show()
  • 效果
    简单的Snackbar示例

2.2.2 与用户交互的提示

    Snackbar 的一个独特之处就是可以实现用户交互,这是 Toast 无法做到的。可通过 Snackbar.setAction() API设置交互动作。

  • 示例代码
val s = Snackbar.make(findViewById(R.id.linearLayout), "这是测试Snackbar2", Snackbar.LENGTH_INDEFINITE)
// 有两个参数,一个是显示的文字内容,一个是操作回调
s.setAction("撤销") {
    // 动作回调,这里无需再调用dismiss(),内部会立即触发消失,并且忽略掉显示延时时间
    Toast.makeText(this@SnackbarActivity, "动作按钮被点击", Toast.LENGTH_SHORT).show()
}
s.show()

注意:Snackbar 设置了操作,当用户点击了操作按钮之后,会触发 Snackbar 消失,会忽略显示延时立即消失。另外,在动作回调中也无需调用 dismiss() API。

  • 效果
    带用户交互的Snackbar

2.2.3 自定义样式

    Snackbar 的默认样式如果无法满足需求,可以通过提供的 API 修改它的样式,这些API有:

  • setActionTextColor():设置动作字体颜色(支持 ColorStateList

  • setBackgroundTint():设置背景着色

  • setBackgroundTintList:设置背景着色( ColorStateList

  • setBackgroundTintMode:设置背景着色模式,默认是 PorterDuff.Mode.SRC(替换着色)

  • setTextColor():设置提示文字的颜色

  • setAnimationMode():设置动画类型,ANIMATION_MODE_SLIDE(滑出) 和 ANIMATION_MODE_FADE (淡出) 二选一

  • 示例代码

val s = Snackbar.make(findViewById(R.id.linearLayout), "这是测试Snackbar3", Snackbar.LENGTH_INDEFINITE)
s.setAction("撤销") {
    Toast.makeText(this@SnackbarActivity, "动作按钮被点击", Toast.LENGTH_SHORT).show()
}
s.setBackgroundTint(Color.CYAN) // 背景色
s.setTextColor(Color.BLACK) // 提示文字颜色
s.setActionTextColor(Color.RED) // 动作按钮文字颜色
// 背景着色模式默认是PorterDuff.Mode.SRC(即使用设置的背景色替换原来的颜色),如果不需要特殊着色效果,可以不设置此项
s.setBackgroundTintMode(PorterDuff.Mode.SRC) // 背景色着色模式
s.show()
  • 效果
    自定义Snackbar的样式

2.2.4 监听Snackbar状态

    在构建 Snackbar 时可以添加一个回调,监听它的状态,在必要时进行某些操作,添加监听回调通过 addCallback() API 进行。Snackbar.Callback 包含两个回调方法,onShown(snackbar: Snackbar?)(显示) 和 onDismissed(snackbar: Snackbar?, event: Int)(消失),其中消失回调中的事件类型有一下几种:

  • Snackbar.Callback.DISMISS_EVENT_ACTION:用户点击了动作

  • Snackbar.Callback.DISMISS_EVENT_TIMEOUT:延时超时

  • Snackbar.Callback.DISMISS_EVENT_SWIPE:用户滑动操作

  • Snackbar.Callback.DISMISS_EVENT_CONSECUTIVE:新的 Snackbar 显示

  • Snackbar.Callback.DISMISS_EVENT_MANUAL:调用了dismiss()接扣

  • 示例代码

val s = Snackbar.make(findViewById(R.id.linearLayout), "这是测试Snackbar4", Snackbar.LENGTH_INDEFINITE)
s.setAction("撤销") {
    Toast.makeText(this@SnackbarActivity, "动作按钮被点击", Toast.LENGTH_SHORT).show()
}
s.addCallback(object: Snackbar.Callback() {
    override fun onShown(snackbar: Snackbar?) {
        super.onShown(snackbar)
        Toast.makeText(this@SnackbarActivity, "Snackbar显示", Toast.LENGTH_SHORT).show()
    }

    override fun onDismissed(snackbar: Snackbar?, event: Int) {
        super.onDismissed(snackbar, event)
        Toast.makeText(this@SnackbarActivity, "Snackbar消失 envent -> ${event.let {
            when(it) {
                Snackbar.Callback.DISMISS_EVENT_ACTION -> "用户点击操作"
                Snackbar.Callback.DISMISS_EVENT_TIMEOUT -> "延时超时"
                Snackbar.Callback.DISMISS_EVENT_SWIPE -> "用户滑动操作"
                Snackbar.Callback.DISMISS_EVENT_CONSECUTIVE -> "新的Snackbar显示"
                Snackbar.Callback.DISMISS_EVENT_MANUAL -> "dismiss()接口"
                else -> "未知操作"

            }
        }}", Toast.LENGTH_SHORT).show()
    }
})
s.show()
  • 效果
    为Snackbar添加监听回调

2.2.5 指定锚点控制显示位置

    Snackbar 默认是显示在最底部(大屏幕显示在左下角),但是可以通过指定锚点 View,控制显示的位置。

注意:指定了锚点 View 之后,Snackbar 是显示在锚点 View 的上方。

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

    <androidx.constraintlayout.widget.ConstraintLayout        
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/linearLayout"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/colorAccent"
        android:layout_marginBottom="50dp">
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/btn_show_snackbar_tips"
            android:onClick="onClick"
            app:layout_constraintTop_toTopOf="parent"
            android:text="Snackbar提示" />
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/btn_show_snackbar_tips2"
            android:onClick="onClick"
            app:layout_constraintTop_toBottomOf="@+id/btn_show_snackbar_tips"
            android:text="Snackbar提示2(带Action)" />
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/btn_show_snackbar_tips3"
            android:onClick="onClick"
            app:layout_constraintTop_toBottomOf="@+id/btn_show_snackbar_tips2"
            android:text="Snackbar提示3(自定义属性)" />
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/btn_show_snackbar_tips4"
            android:onClick="onClick"
            app:layout_constraintTop_toBottomOf="@+id/btn_show_snackbar_tips3"
            android:text="Snackbar提示4(带回调)" />
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/btn_show_snackbar_tips5"
            android:onClick="onClick"
            app:layout_constraintTop_toBottomOf="@+id/btn_show_snackbar_tips4"
            android:text="Snackbar提示5(指定显示位置)" />
        <!-- 这里添加一个锚点 -->
        <View
            android:id="@+id/bottomAnchor"
            android:layout_width="match_parent"
            android:layout_height="1dp"
            app:layout_constraintTop_toBottomOf="parent"/>
    </androidx.constraintlayout.widget.ConstraintLayout>

</androidx.coordinatorlayout.widget.CoordinatorLayout>
val s = Snackbar.make(findViewById(R.id.btn_show_snackbar_tips), "这是测试Snackbar5", Snackbar.LENGTH_INDEFINITE)
s.setAction("撤销") {
    Toast.makeText(this@SnackbarActivity, "动作按钮被点击", Toast.LENGTH_SHORT).show()
}
s.setAnchorView(R.id.bottomAnchor)
s.animationMode = Snackbar.ANIMATION_MODE_FADE
s.show()
  • 效果

添加锚点View控制显示位置

三、高手进阶

3.1 CoordinatorLayout 配合效果

    SnackbarCoordingatorLayout 配合,可以实现一些特殊效果,比如对于类似 FloatingActionButton 这样的控件,在 Snackbar 显示时会上移,而不是被遮挡。如果你的 Snackbar 是非延时自动消失的,建议使用这种,避免 FloatingActionButton 操作被遮挡。

  • 示例代码
<?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:id="@+id/coordinatorLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/linearLayout"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/colorAccent"
        android:layout_marginBottom="50dp">
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/btn_show_snackbar_tips"
            android:onClick="onClick"
            app:layout_constraintTop_toTopOf="parent"
            android:text="Snackbar提示" />
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/btn_show_snackbar_tips2"
            android:onClick="onClick"
            app:layout_constraintTop_toBottomOf="@+id/btn_show_snackbar_tips"
            android:text="Snackbar提示2(带Action)" />
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/btn_show_snackbar_tips3"
            android:onClick="onClick"
            app:layout_constraintTop_toBottomOf="@+id/btn_show_snackbar_tips2"
            android:text="Snackbar提示3(自定义属性)" />
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/btn_show_snackbar_tips4"
            android:onClick="onClick"
            app:layout_constraintTop_toBottomOf="@+id/btn_show_snackbar_tips3"
            android:text="Snackbar提示4(带回调)" />
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/btn_show_snackbar_tips5"
            android:onClick="onClick"
            app:layout_constraintTop_toBottomOf="@+id/btn_show_snackbar_tips4"
            android:text="Snackbar提示5(指定显示位置)" />

        <View
            android:id="@+id/bottomAnchor"
            android:layout_width="match_parent"
            android:layout_height="1dp"
            app:layout_constraintTop_toBottomOf="parent"/>
    </androidx.constraintlayout.widget.ConstraintLayout>

    <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="@dimen/fab_margin"
        app:backgroundTint="@color/colorPrimary"
        app:srcCompat="@android:drawable/ic_dialog_email" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>
  • 效果
    结合CoordinatorLayout使用

注意:并不是所有的内容都会有推移的效果,另外,CoordinatorLayout 需要引入库,support支持库使用 “com.android.support:design:28.0.0”,androidx 支持库使用 “com.google.android.material:material:1.2.1”。

3.2 自定义布局

    Snackbar 的布局非常简单,其实这也是 Material Design 的理念,但是如果你的需求还是需要在 Snackbar 实现自定义布局也是可以的。实际上,在 Snackbar 上实现自定义布局,实际上只是在原来的布局容器(Snackbar.SnackbarLayout,是 FrameLayout 的间接子类)上层添加视图,另外,如果在上面覆盖了布局,那么原来的 Snackbar 部分 API将会失效(主要是设置显示内容和样式的 API),需要自己在自定义不居中进行相应的设定。

  • 示例
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:background="@android:color/white">
    <ImageView
        android:id="@+id/imageView"
        android:layout_width="80dp"
        android:layout_height="80dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:scaleType="centerInside"/>
    <TextView
        android:id="@+id/textView"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toRightOf="@+id/imageView"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/button"
        android:gravity="left|center_vertical"
        android:textSize="16sp"
        android:textColor="@android:color/black"/>
    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:background="@android:color/transparent"/>


</androidx.constraintlayout.widget.ConstraintLayout>
val s = Snackbar.make(findViewById(R.id.btn_show_snackbar_tips), "这是测试Snackbar5", Snackbar.LENGTH_INDEFINITE)
val layout = s.view as Snackbar.SnackbarLayout
layout.setPadding(0, 0, 0, 0)

val contentView = LayoutInflater.from(this@SnackbarActivity).inflate(R.layout.layout_snackbar_custom, null)
val imageView = contentView.findViewById<ImageView>(R.id.imageView)
val textView = contentView.findViewById<TextView>(R.id.textView)
val button = contentView.findViewById<Button>(R.id.button)


imageView.setImageResource(android.R.drawable.ic_dialog_alert)
textView.text = "记录已删除"

button.text = "撤销"
button.setOnClickListener {
    s.dismiss()
    // 如果自定义的布局不是铺满全屏或者没有设置自己的背景色,远提示字符不要设置任何字符
    Toast.makeText(this@SnackbarActivity, "", Toast.LENGTH_SHORT).show()
}

layout.addView(contentView, ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT))
contentView.bringToFront()
s.show()
  • 效果
    自定义Snackbar布局

说明:Snackbar 自定义布局是在原来布局上面添加视图,如果覆盖的视图没有设置自己的背景色,请在 Snackbar 显示的文字设置为空字符"",并且不要使用 setAction() API。

四、编后语

    Snackbar 在 Material Design 设计中是非常好用的一个控件,简介提示并且可以用户交互。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值