SnackBar源码分析(来自design包)
来源:互联网 发布:婚礼入场音乐知乎 编辑:程序博客网 时间:2024/05/01 20:14
一.简介.
SnackBar的提出实际上是界于Toast和Dialog的中间产物。
Toast: 用户无法交互;Dialog:用户可以交互,但是体验会打折扣,会阻断用户的连贯性操作;
Snackbar既可以做到轻量级的用户提醒效果,又可以有交互的功能(是一种非必须的操作)
二.SnackBar 源码分析.
/** * Make a Snackbar to display a message * * <p>Snackbar will try and find a parent view to hold Snackbar's view from the value given * to {@code view}. Snackbar will walk up the view tree trying to find a suitable parent, * which is defined as a {@link CoordinatorLayout} or the window decor's content view, * whichever comes first. * * <p>Having a {@link CoordinatorLayout} in your view hierarchy allows Snackbar to enable * certain features, such as swipe-to-dismiss and automatically moving of widgets like * {@link FloatingActionButton}. * * @param view The view to find a parent from. * @param text The text to show. Can be formatted text. * @param duration How long to display the message. Either {@link #LENGTH_SHORT} or {@link * #LENGTH_LONG} */ @NonNull public static Snackbar make(@NonNull View view, @NonNull CharSequence text, @Duration int duration) { Snackbar snackbar = new Snackbar(findSuitableParent(view)); snackbar.setText(text); snackbar.setDuration(duration); return snackbar; }这里就是说 SnackBar 会根据我们传入的View作为锚点一层一层的寻找特定View作为SnackBar的父容器 直到找到CoordinatorLayout或者the window decor's content view,做为SnackBar的父容器,代码如下:
private static ViewGroup findSuitableParent(View view) { ViewGroup fallback = null; do { if (view instanceof CoordinatorLayout) { // We've found a CoordinatorLayout, use it return (ViewGroup) view; } else if (view instanceof FrameLayout) { if (view.getId() == android.R.id.content) { // If we've hit the decor content view, then we didn't find a CoL in the // hierarchy, so use it. return (ViewGroup) view; } else { // It's not the content view but we'll use it as our fallback fallback = (ViewGroup) view; } } if (view != null) { // Else, we will loop and crawl up the view hierarchy and try to find a parent final ViewParent parent = view.getParent(); view = parent instanceof View ? (View) parent : null; } } while (view != null); // If we reach here then we didn't find a CoL or a suitable content view so we'll fallback return fallback; }为SnackBar找到父容器以后创建布局:
private Snackbar(ViewGroup parent) {mParent = parent;mContext = parent.getContext();LayoutInflater inflater = LayoutInflater.from(mContext);mView = (SnackbarLayout) inflater.inflate(R.layout.design_layout_snackbar, mParent, false); }这里R.layout.design_layout_snackbar该布局文件应该就是我们SnackBar的布局文件,我们可以在design包的Layout文件夹中找到:
<view xmlns:android="http://schemas.android.com/apk/res/android" class="android.support.design.widget.Snackbar$SnackbarLayout" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="bottom" style="@style/Widget.Design.Snackbar" />但是发现并不是,继续找,一看就知道 这个布局是我们SnackBar的一个内部类,我们找到这个内部类,这里面我们真正的找到了我们布局文件:
public SnackbarLayout(Context context, AttributeSet attrs) { super(context, attrs); // Now inflate our content. We need to do this manually rather than using an <include> // in the layout since older versions of the Android do not inflate includes with // the correct Context. LayoutInflater.from(context).inflate(R.layout.design_layout_snackbar_include, this); }查看R.layout.design_layout_snackbar_include:
<merge xmlns:android="http://schemas.android.com/apk/res/android"> <TextView android:id="@+id/snackbar_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:paddingTop="@dimen/design_snackbar_padding_vertical" android:paddingBottom="@dimen/design_snackbar_padding_vertical" android:paddingLeft="@dimen/design_snackbar_padding_horizontal" android:paddingRight="@dimen/design_snackbar_padding_horizontal" android:textAppearance="@style/TextAppearance.Design.Snackbar.Message" android:maxLines="@integer/design_snackbar_text_max_lines" android:layout_gravity="center_vertical|left|start" android:ellipsize="end" android:textAlignment="viewStart"/> <Button android:id="@+id/snackbar_action" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/design_snackbar_extra_spacing_horizontal" android:layout_marginStart="@dimen/design_snackbar_extra_spacing_horizontal" android:layout_gravity="center_vertical|right|end" android:paddingTop="@dimen/design_snackbar_padding_vertical" android:paddingBottom="@dimen/design_snackbar_padding_vertical" android:paddingLeft="@dimen/design_snackbar_padding_horizontal" android:paddingRight="@dimen/design_snackbar_padding_horizontal" android:visibility="gone" android:textColor="?attr/colorAccent" style="?attr/borderlessButtonStyle"/> </merge>这里面用了merge标签减少视图层级(这里涉及到merge、include、ViewStub的区别大家可以看Google的文档或者百度一大堆)如果我们要改变我们SnackBar text的颜色 你就可以在你的主题中重写该主题:
<style name="TextAppearance.Design.Snackbar.Message" parent="android:TextAppearance"> <item name="android:textSize">@dimen/design_snackbar_text_size</item> <item name="android:textColor">?android:textColorPrimary</item> </style>最后mParent.addView(mView);将SnackBar的view添加到父容器中:
final void showView() { //................... mParent.addView(mView); // 动画的加载 if (ViewCompat.isLaidOut(mView)) { // If the view is already laid out, animate it now animateViewIn(); } else { // Otherwise, add one of our layout change listeners and animate it in when laid out mView.setOnLayoutChangeListener(new SnackbarLayout.OnLayoutChangeListener() { @Override public void onLayoutChange(View view, int left, int top, int right, int bottom) { animateViewIn(); mView.setOnLayoutChangeListener(null); } }); } }哪里调用了这个方法呢我们一步一步的看:
sHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() { @Override public boolean handleMessage(Message message) { switch (message.what) { case MSG_SHOW: ((Snackbar) message.obj).showView();//在这里 return true; case MSG_DISMISS: ((Snackbar) message.obj).hideView(message.arg1); return true; } return false; } });再继续:
private final SnackbarManager.Callback mManagerCallback = new SnackbarManager.Callback() { @Override public void show() { sHandler.sendMessage(sHandler.obtainMessage(MSG_SHOW, Snackbar.this)); } @Override public void dismiss(int event) { sHandler.sendMessage(sHandler.obtainMessage(MSG_DISMISS, event, 0, Snackbar.this)); } };这里结合SnackBar.show()方法;
public void show() { SnackbarManager.getInstance().show(mDuration, mManagerCallback); }看到这里我们可以猜想我们SnackManger作为我们的SackBar控制器通过SnackbarManager.Callback()进行通信,控制SnackBar的show 和 dismiss
那我们看看SnackBarManager里做了什么事情?
我们直接看SnackBarManger的show方法:
public void show(int duration, Callback callback) { synchronized (mLock) { if (isCurrentSnackbar(callback)) { // Means that the callback is already in the queue. We'll just update the duration mCurrentSnackbar.duration = duration; // If this is the Snackbar currently being shown, call re-schedule it's // timeout mHandler.removeCallbacksAndMessages(mCurrentSnackbar); scheduleTimeoutLocked(mCurrentSnackbar); return; } else if (isNextSnackbar(callback)) { // We'll just update the duration mNextSnackbar.duration = duration; } else { // Else, we need to create a new record and queue it mNextSnackbar = new SnackbarRecord(duration, callback); } if (mCurrentSnackbar != null && cancelSnackbarLocked(mCurrentSnackbar, 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 mCurrentSnackbar = null; // Otherwise, just show it now showNextSnackbarLocked(); } } }
这里差不多就是Manger的心脏了,一些复杂的逻辑处理,比如你的SnackBar正在显示 显示其他 或者再次显示自己的处理 及更新,
我们传进来的cSnackBarManger.Callback 保存进了SnackbarRecord中,这个SnackBarRecord是干的呢?
private static class SnackbarRecord {//值得学习的地方 用于优化 private final WeakReference<Callback> callback; private int duration; SnackbarRecord(int duration, Callback callback) { this.callback = new WeakReference<>(callback); this.duration = duration; } boolean isSnackbar(Callback callback) { return callback != null && this.callback.get() == callback; } }看到这里我们应该对SnackBarManger有了解 就是对我们的SnackBar.Callback做了管理保存在了SnackbarRecord,做了一个管理和控制
到此我们就分析完了,相信大家也大概了解了SnackBar的原理了。
1 0
- SnackBar源码分析(来自design包)
- Android Support Design 库 之 Snackbar使用及源码分析
- Android Support Design 库 之 Snackbar使用及源码分析
- Android Support Design 库 之 Snackbar使用及源码分析
- Android Support Design 库 之 Snackbar使用及源码分析
- Android Support Design 库 之 Snackbar使用及源码分析
- (三十)Snackbar 使用及其源码分析
- Snackbar源码分析
- Android Study Material Design 五 番外篇 之:深入分析SnackBar源码
- 咱们来看看谷歌Design包里的SnackBar
- Android design library(二)----------FloatingActionButton + Snackbar
- Material Design (三),Snackbar的使用
- android material design之Snackbar(二)
- (4.1.43.2)design support library:Snackbar
- android.support.design库组件(Snackbar )
- 轻量级控件SnackBar使用以及源码分析
- Android-Snackbar用法及源码分析
- 轻量级控件SnackBar应用&源码分析
- 从苏宁电器到卡巴斯基第06篇:我在佳木斯的日子(中)
- android 6.26 apache 第一行代码 HttpHostConnectException: Connection to
- DirectShow之接口实战篇、
- java基础(八)Math/代码块/继承成员方法指南的关系/继承中成员变量之间的关系/方法的重写/继承中构造方法之间的关系/this和super的区别
- HDU 5119 Happy Matt Friends
- SnackBar源码分析(来自design包)
- 1006
- 皮尔逊系数皮尔逊系数---K近算法之皮尔逊系数
- 按之字形顺序打印二叉树
- Unity性能优化---内存上
- 从苏宁电器到卡巴斯基第07篇:我在佳木斯的日子(下)
- HHTP基础之状态码
- spss四分位数
- Linux spi驱动 (三)