专注收集记录技术开发学习笔记、技术难点、解决方案
网站信息搜索 >> 请输入关键词:
您当前的位置: 首页 > 移动开发

Snackbar新版Toast 从源码视角完全解析

发布时间:2010-05-29 23:38:59 文章来源:www.iduyao.cn 采编人员:星星草
Snackbar新版Toast 从源码角度完全解析

我们将会从一下几个角度了解Snackbar。

1.什么是Snackbar

2.如何使用Snackbar

3.有哪些常用的API

4.从源码角度分析其实现

5.总结

1.什么是Snackbar

Snackbar是一个轻量级的用户操作反馈工具,类似于Toast,但是比Toast更美观和实用,当你操作它的时候,Snackbar会置顶显示一个一定时间的简要的信息在屏幕的底部,并且它还可以添加事件。

2.如何使用Snackbar

    Snackbar
          .make(parentLayout, R.string.snackbar_text,  
                                        Snackbar.LENGTH_LONG)
           .setAction(R.string.snackbar_action, myOnClickListener)
                  .show(); // Don’t forget to show!
这是官方给的一句话,是不是很简单。当然 你需要使用  Android Support Library 22.2以上的版本。

3.有哪些常用的API

由于它很简单,官方也只暴露出了几个常用的方法。我们来看一下
dismiss() 不解释
getDuration() 不解释
getView() 返回Snackbar的布局
make(View view, int resId, int duration) 这里的view代 、
表 Snackbar的父view,因为它要找一个依赖
make(View view, CharSequence text, int duration) 不解释
setAction(int resId, View.OnClickListener listener) 设置动作
setAction(CharSequence text, View.OnClickListener listener)
setActionTextColor(ColorStateList colors) 设置文本的颜色
setActionTextColor(int color) 设置动作文本的颜色
setDuration(int duration) 设置时间段
setText(int resId)
setText(CharSequence message)
show()
就这几个方法

4.从源码角度分析其实现

首先我们看Snakebar类的相关方法


static {
        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();
                        return true;
                }
                return false;
            }
        });
    }
//首先设置了一个handler

Snackbar(ViewGroup parent) {
        mParent = parent;
        mContext = parent.getContext();

        LayoutInflater inflater = LayoutInflater.from(mContext);
        mView = (SnackbarLayout) inflater.inflate(R.layout.layout_snackbar, mParent, false);
    }
    //在构造方法中获得了父控件,获得了context,初始化自己的view,由于它的构造函数是protected的,大家懂的。

//我们来看看最主要的方法make,在看make方法之前我们来看看findSuitableParent这个make方法里主要的功能。
@Nullable
    private static ViewGroup findSuitableParent(View view) {
        ViewGroup fallback = null;
        do {
            if (view instanceof CoordinatorLayout) { //判断是不是CoordinatorLayout,如果是直接用,CoordinatorLayout也是22.2包下的新的layout
                return (ViewGroup) view;
            } else if (view instanceof FrameLayout) { //如果是FrameLayout,则进入else if
                if (view.getId() == android.R.id.content) { //如果id == android.R.id.content 那么直接拿来用android.R.id.content代表根视图,至于根视图为什么是FrameLayout,我就不说了
        return (ViewGroup) view;
                } else {                            //是FrameLayout但不是根视图的话,先添加进callback再说
                    fallback = (ViewGroup) view;
                }
            }
            if (view != null) {
                final ViewParent parent = view.getParent();         //获得view的父控件,并循环执行,直到找到FrameLayout,或者找到CoordinatorLayout
                view = parent instanceof View ? (View) parent : null;
            }
        } while (view != null);
        return fallback;
    }
//排版有点乱,大家将就看,其实就是循环找到FrameLayout或者CoordinatorLayout才罢休

//好了,我们再看make方法。
      public static Snackbar make(View view, CharSequence text, @Duration int duration) {
        Snackbar snackbar = new Snackbar(findSuitableParent(view)); //初始化一个Snackbar,并传入父控件
        snackbar.setText(text);  //做一些初始化操作
        snackbar.setDuration(duration);
        return snackbar; //返回过去
    }

//我们再来看看setAction方法,没啥可说的。这里采用的是链式编程。
    public Snackbar setAction(CharSequence text, final View.OnClickListener listener) {
        final TextView tv = mView.getActionView();

        if (TextUtils.isEmpty(text) || listener == null) {
            tv.setVisibility(View.GONE);
            tv.setOnClickListener(null);
        } else {
            tv.setVisibility(View.VISIBLE);
            tv.setText(text);
            tv.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    listener.onClick(view);
                    dismiss();
                }
            });
        }
        return this;
    }
    //Snackbar内部自定义了一个继承自LinearLayout的SnackbarLayout用于显示自己的view,代码也不是很复杂,这想看的可以看看,毕竟自定义控件还是很重要的一项技能,这里就不说了。

//再来看看show方法
     public void show() { //调用了SnackbarManager来显示了,那我们就去SnackbarManager看看
        SnackbarManager.getInstance().show(mDuration, mManagerCallback);
    }

//首先看看SnackbarManager的类结构图
![image](http://img.blog.csdn.net/20150608221707832)
//我们通过看SnackbarManager类结构可以看出,SnackbarManager主要的工作是显示和隐藏Snackbar,我们主要看show和dismiss方法

 public void show(int duration, Callback callback) {
    synchronized (mLock) { //同步线程锁,一次只能有一个对象进来。
         if (isCurrentSnackbar(callback)) {
         //判断是不是当前的callback,如果是那么说明这个cakllback已经在队列里了,那么只需要
         mCurrentSnackbar.duration = duration;
         mHandler.removeCallbacksAndMesssage(mCurrentSnackbar); 
             //如果正在显示,则调用handler重新显示一遍removeCallbacksAndMessages的参数如果为null则表示移除所有的Callbacks和Messages,在activityondestory执行能有效的避免内存泄露
         scheduleTimeoutLocked(mCurrentSnackbar);
         return;
       } else if (isNextSnackbar(callback)) { 
       //如果是下一个SnackbarRecord则直接设置时间,handler会自动调用, 这里就用到了handler looper  mq的概念
         mNextSnackbar.duration = duration;
       } else {//如果都不是则重新创建一个SnackbarRecord,    SnackbarRecord  我们等会再说
        mNextSnackbar = new SnackbarRecord(duration, callback);
     }

    if (mCurrentSnackbar != null && cancelSnackbarLocked(mCurrentSnackbar)) { 
    //如果mCurrentSnackbar不为空,试图去取消它。
    return;
    } else {
                mCurrentSnackbar = null; //清空mCurrentSnackbar
                showNextSnackbarLocked(); //然后直接显示,showNextSnackbarLocked该方法会先把mNextSnackbar赋值给mCurrentSnackbar,然后让mCurrentSnackbar显示
            }
  } 


    //总结一下这个show方法,首先会判断mCurrentSnackbar是不是空,如果不是,并且它的callback和之前的不一致,则直接显示(会先把之前的隐藏再显示现在的); 如果是空或者说并且它的callback和之前的不一致,则判断NextSnackbar是不是空,如果不是空,并且它的callback和之前一致,则直接设置至,等待handler的取值;如果mNextSnackbar是空,或者说并且它的callback和之前的不一致则直接先干掉mCurrentSnackbar,然后给它重新赋值,逻辑还是有点绕的。   

//再来看看 dismiss
    public void dismiss(Callback callback) {   
     //很容易就看懂了。不解释了
        synchronized (mLock) {
            if (isCurrentSnackbar(callback)) {
                cancelSnackbarLocked(mCurrentSnackbar);
            }
            if (isNextSnackbar(callback)) {
                cancelSnackbarLocked(mNextSnackbar);
            }
        }
    }

    //而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;
        }
    }


5.总结

好了,Snackbar整体就差不多了,总结一下,所有Snackbar的初始化工作都是在Snackbar类里执行的,而最后的show和dismiss都在snackbarManager里执行的,
而snackbarManager里维持了一个SnackbarRecord,SnackbarRecord内部拥有mCurrentSnackbar, mNextSnackbar两个回调,用于维持Snackbar的显示和隐藏

友情提示:
信息收集于互联网,如果您发现错误或造成侵权,请及时通知本站更正或删除,具体联系方式见页面底部联系我们,谢谢。

其他相似内容:

热门推荐: