首页 > 代码库 > Android Material Design 系列之 SnackBar详解

Android Material Design 系列之 SnackBar详解

SnackBar是google Material Design提供的一种轻量级反馈组件。支持从布局的底部显示一个简洁的提示信息,支持手动滑动取消操作,同时在同一个时间内只能显示一个SnackBar.

 

Snackbar是如何实现的呢?我们主要讨论Snackbar的显示逻辑,包括:延迟消失和同一时间只支持一个Snackbar显示

首先我们先看下Snackbar用到的两个类

技术分享

<style>p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px "PingFang SC"; color: #454545 } p.p2 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica; color: #454545; min-height: 14.0px } p.p3 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica; color: #454545 } span.s1 { font: 12.0px Helvetica } span.s2 { font: 12.0px "PingFang SC" }</style>

其中SnackbarManager是个单例,利用单例的形式来保证每次只会显示一个Snackbar。

 

Snackbar中的sHandler用于SnackbarManager.Callback回调函数中,每个Snackbar都会去实现SnackbarManager.Callback的回调,包括show和dimiss两个方法。SnackbarManager也是通过这个回调来区分每个不同的Snackbar

private boolean isCurrentSnackbarLocked(ConvinentSnackbarManager.Callback callback) {
    return mCurrentSnackbar != null && mCurrentSnackbar.isSnackbar(callback);
}

  

<style>p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px "PingFang SC"; color: #454545 } span.s1 { font: 12.0px Helvetica }</style>

为了方便用户能够对Snackbar的行为进行自定义操作,Snackbar提供了 Snackbar.Callback,包括了Snackbar显示,消失后的行为。用户可以根据自己的业务需求,设置这个callback.具体提供的接口方法如下:

/**
         * Called when the given {@link ConvinentSnackbar} has been dismissed, either through a time-out,
         * having been manually dismissed, or an action being clicked.
         *
         * @param snackbar The snackbar which has been dismissed.
         * @param event    The event which caused the dismissal. One of either:
         *                 {@link #DISMISS_EVENT_SWIPE}, {@link #DISMISS_EVENT_ACTION},
         *                 {@link #DISMISS_EVENT_TIMEOUT}, {@link #DISMISS_EVENT_MANUAL} or
         *                 {@link #DISMISS_EVENT_CONSECUTIVE}.
         * @see ConvinentSnackbar#dismiss()
         */
        public void onDismissed(ConvinentSnackbar snackbar, @ConvinentSnackbar.Callback.DismissEvent int event) {
            // empty
        }

        /**
         * Called when the given {@link ConvinentSnackbar} is visible.
         *
         * @param snackbar The snackbar which is now visible.
         * @see ConvinentSnackbar#show()
         */
        public void onShown(ConvinentSnackbar snackbar) {
            // empty
        }

  

<style>p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px "PingFang SC"; color: #454545 } span.s1 { font: 12.0px Helvetica }</style>

SnackbarManager中,为了保存每个一个Snackbar,提供了SnackBarRecord的内部类,这个类有两个成员变量,一个是Snackbar的callback,一个是这个Snackbar的duration。但是SnackBarManager不是用一个队列去保存每一个新生成的不同的Snackbar。只会保存当前和一下要显示的。而下一个的规则是:在一个Snackbar的显示周期中(已经有一个Snackbar存在的情况下),只会把最后一个新的Snackbar作为下一个Snackbar。假如你每点击一次按钮都会新生成一个Snackbar,那么在2.75s(LONG_DURATION_MS)内,你点击了N次,SnackBarManager只会显示1次和第N次。其它的都不会显示。并且第N次的显示是在第一次显示结束后会自动调用显示

public void show(int duration, ConvinentSnackbarManager.Callback callback) {
        synchronized (mLock) {
            if (isCurrentSnackbarLocked(callback)) {
                // Means that the callback is already in the queue. We‘ll just update the duration
                mCurrentSnackbar.duration = duration;

                // If this is the ConvinentSnackbar currently being shown, call re-schedule it‘s
                // timeout
                mHandler.removeCallbacksAndMessages(mCurrentSnackbar);
                scheduleTimeoutLocked(mCurrentSnackbar);
                return;
            } else if (isNextSnackbarLocked(callback)) {
                // We‘ll just update the duration
                mNextSnackbar.duration = duration;
            } else {
                // Else, we need to create a new record and queue it
                mNextSnackbar = new ConvinentSnackbarManager.SnackbarRecord(duration, callback);
            }

            if (mCurrentSnackbar != null && cancelSnackbarLocked(mCurrentSnackbar,
                    ConvinentSnackbar.Callback.DISMISS_EVENT_CONSECUTIVE)) {
                // If we currently have a ConvinentSnackbar, try and cancel it and wait in line
                return;
            } else {
                // Clear out the current snackbar
                mCurrentSnackbar = null;
                // Otherwise, just show it now
                showNextSnackbarLocked();
            }
        }
    }

  

<style>p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica; color: #454545 } span.s1 { font: 12.0px "PingFang SC" }</style>

SnackbarManager里也有一个Handler,这个Handler是用于延迟消失动画的。每次生成一个SnackBar,如果duration不是设置为LENGTH_INDEFINITE(不消失),SnackbarManager在显示之后,都会用这个Handler的sendMessageDelayed

 来进行延迟发送一个消失消息。Handler在收到这个消息后,会调用SnackbarManager.Callback 

,通过SnackbarManager的Callback来实现Snackbar的显示和消失。

 

Snackbar的优点:

 提供了比Toast更灵活和更友好的显示方法。

 

Snackbar的优点:

1.显示位置过于单一,现在目前只支持底部显示。

2.滑动操作过于单一,现在只支持从左向右滑动取消。

 

对Snackbar进行了些改动,github的项目为ConvinentSnackbar。地址为:

https://github.com/stephen-wu-yuan/ConvinentSnackbar

改组件支持设置滑动方向和支持顶部显示。

 

 

 

 

 

 

Android Material Design 系列之 SnackBar详解