Material​轻量级反馈:Snackbar

QuibblerQuibbler 2020-10-06 约 16 分钟 1738 次阅读

Material轻量级反馈:Snackbar

了解Material Design的开发者对Snackbar不陌生,这是和Toast一样的轻量级反馈。关于Toast详见Toast:前后台弹出Toast不一致?。

1、Snackbar

Snackbar和Toast类似,方法也都差不多。Snackbar继承自BaseTransientBottomBar。

1.1、使用Snackbar

Snackbar不是系统自带的,Toast自API 1就跟随系统。要使用Snackbar在MDC库中需要额外添加依赖:

implementation 'com.google.android.material:material:1.2.1'

1.2、创建Snackbar

Snackbar对象只能通过Snackbar类提供的两个static方法make(...)创建,因为Snackbar的构造函数是private私有的

private Snackbar(ViewGroup parent,View content,
                   com.google.android.material.snackbar.ContentViewCallback contentViewCallback) {
    super(parent, content, contentViewCallback);
    accessibilityManager =
        (AccessibilityManager) parent.getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
  }

①static Snackbar make(View view,CharSequence text,int duration):内部创建Layout,并设置传入的参数值。

②static Snackbar make(View view, @StringRes int resId,int duration):调用上面的方法

public static Snackbar make(@NonNull View view, @StringRes int resId, @Duration int duration) {
    return make(view, view.getResources().getText(resId), duration);
  }

1.3、显示Snackbar

了解一下显示Snackbar的流程。

public void show() {
    super.show();
  }

调用父类BaseTransientBottomBar的show()方法

public void show() {
    SnackbarManager.getInstance().show(getDuration(), managerCallback);
  }

没想到是用SnackbarManager来管理Snackbar,而且还是单例模式

...
  private static SnackbarManager snackbarManager;
  //单例
  static SnackbarManager getInstance() {
    if (snackbarManager == null) {
      snackbarManager = new SnackbarManager();
    }
    return snackbarManager;
  }
  
  //私有构造方法
  private SnackbarManager() {
    lock = new Object();
    handler =
        new Handler(
            Looper.getMainLooper(),
            new Handler.Callback() {
              @Override
              public boolean handleMessage(@NonNull Message message) {
                switch (message.what) {
                  case MSG_TIMEOUT:
                    handleTimeout((SnackbarRecord) message.obj);
                    return true;
                  default:
                    return false;
                }
              }
            });
  }

看看这个Snackbar管理器是如何show() Snackbar的:

public void show(int duration, Callback callback) {
    synchronized (lock) {
      if (isCurrentSnackbarLocked(callback)) {
        // Means that the callback is already in the queue. We'll just update the duration
        currentSnackbar.duration = duration;
        // If this is the Snackbar currently being shown, call re-schedule it's
        // timeout
        handler.removeCallbacksAndMessages(currentSnackbar);
        scheduleTimeoutLocked(currentSnackbar);
        return;
      } else if (isNextSnackbarLocked(callback)) {
        // We'll just update the duration
        nextSnackbar.duration = duration;
      } else {
        // Else, we need to create a new record and queue it
        nextSnackbar = new SnackbarRecord(duration, callback);
      }
      if (currentSnackbar != null
          && cancelSnackbarLocked(currentSnackbar, Snackbar.Callback.DISMISS_EVENT_CONSECUTIVE)) {
        // If we currently have a Snackbar, try and cancel it and wait in line
        return;
      } else {
        // Clear out the current snackbar
        currentSnackbar = null;
        // Otherwise, just show it now
        showNextSnackbarLocked();
      }
    }
  }

具体的方法不用细看,有一个callback回调和一个Handler。其中的Handler在SnackbarManager私有构造函数中使用MainLooper初始化

interface Callback {
    void show();
    void dismiss(int event);
  }

而这个传入的SnackbarManager.Callback回调是在Snackbar的父类BaseTransientBottomBar初始化的成员变量:

SnackbarManager.Callback managerCallback =
      new SnackbarManager.Callback() {
        @Override
        public void show() {
          handler.sendMessage(handler.obtainMessage(MSG_SHOW, BaseTransientBottomBar.this));
        }
        @Override
        public void dismiss(int event) {
          handler.sendMessage(
              handler.obtainMessage(MSG_DISMISS, event, 0, BaseTransientBottomBar.this));
        }
      };

BaseTransientBottomBar中定义了静态的handler,在类加载的时候初始化,用来show()和dismiss() Snackbar。

static {
    handler =
        new Handler(
            Looper.getMainLooper(),
            new Handler.Callback() {
              @Override
              public boolean handleMessage(@NonNull Message message) {
                switch (message.what) {
                  case MSG_SHOW:
                    ((BaseTransientBottomBar) message.obj).showView();
                    return true;
                  case MSG_DISMISS:
                    ((BaseTransientBottomBar) message.obj).hideView(message.arg1);
                    return true;
                  default:
                    return false;
                }
              }
            });
  }

同样dissmiss()方法的执行流程也是一样的。

2、方法

了解一下Snackbar还有哪些常用的方法。可以看见这些方法都返回调用对象自身,方便优美的链式调用一气呵成,完成设置,最后show出来。

①Snackbar setAction(@StringRes int resId, View.OnClickListener listener):设置点击动作

②Snackbar setAction(CharSequence text,View.OnClickListener listener)

③Snackbar setActionTextColor(ColorStateList colors)

④Snackbar setActionTextColor(@ColorInt int color)

⑤Snackbar setBackgroundTint(@ColorInt int color)

⑥Snackbar setBackgroundTintList(ColorStateList colorStateList)

⑦Snackbar setBackgroundTintMode(PorterDuff.Mode mode)

⑧Snackbar setMaxInlineActionWidth(@Dimension int width)

⑨Snackbar setText(CharSequence message)

⑩Snackbar setText(@StringRes int resId)

⑪Snackbar setTextColor(ColorStateList colors)

⑫Snackbar setTextColor(@ColorInt int color)

⑬boolean isShown():当前Snackbar是否显示

3、示例

看再多源码,不如一试。

3.1、默认Snackbar

使用Material提供的Snackbar很简单,一行代码搞定!

Snackbar.make(v, R.string.toast, 500).show();

3.2、定制Snackbar

创建Snackbar实例,先不展示

Snackbar snackbar = Snackbar.make(v, R.string.news_delete_toast, 750);

获取Snackbar中的基础BaseTransientBottomBar.SnackbarBaseLayout布局

View view = snackbar.getView();

进行设置,还可以拿到其中的TextView控件

view.setBackgroundColor(Color.GRAY);
    TextView textView = view.findViewById(R.id.snackbar_text);
    textView.setTextColor(Color.BLUE);
    snackbar.setAction("取消", new View.OnClickListener() {
        @Override
        public void onClick(View v) {
        }
    });

设置完成最后再显示即可

snackbar.show();


相关资料:

         Docs > Reference > Snackbar">Android Developers > Docs > Reference > Snackbar

        Snackbar使用方法

        Android Snackbar新控件解析

        Snackbar源码分析

相关推荐

精选
ViewPager和PagerAdapter、FragmentPagerAdapter、FragmentStatePager
Android

ViewPager和PagerAdapter、FragmentPagerAdapter、FragmentStatePager

ViewPager1、ViewPagerandroidx.viewpager.widget.ViewPager,Android中使用非常广泛的控件,可以说是APP必备:首次打开引导页、页面Banner广告等。常用方法:setAdapter() 设置适配器setOffscreenPageLimit() 设置缓存的页面个数,默认是 1setCurrentItem() 跳转到特定的页面setOnPage

1.1k
获取Android内置WebView内核版本
Android

获取Android内置WebView内核版本

获取Android内置WebView内核版本竟然能遇到这样奇葩的事情,网页用的技术过于新颖,以至于只支持高版本Chromium内核的,低版本安卓系统中内置的内核版本较低,无法加载前端页面。1、设置查看在系统设置里 > 应用 > 应用管理 > 显示系统应用,查看WebView组件:2、页面查看通过WebView发起的网络请求,都会带上浏览器的UA,通常页面都可以通过UA判断浏览器的内核版本。这里有两

1.1w
Android 14适配总结
Android

Android 14适配总结

Android 14适配总结毕业工作至今已经适配了三个Android大版本,从Android 11到Android 12、再到Android 13。2023年,Google即将推出的Android 14,上半年已经开始第一批适配。现在,第四个Android版本已经适配完,总结记录一下。1、Android 14计划Google一般会在2月份对外发布预告,同时放出开发者预览版。“拉通”各大平台、厂商以

1.1w