最新消息:欢迎访问Android开发中文站!商务联系微信:loading_in

Android Snackbar简单解析

新手入门 loading 2293浏览 0评论

偶然间发现android中有Snackbar类,还是有点意思,类似于toast。与toast相比,最明显的区别是:Snackbar只能在屏幕底部显示。其他用法基本与toast相似。

先来张效果图吧,静态图:

大概的用法呢?:

Snackbar.make(btn,"Snackbar 测试",Snackbar.LENGTH_INDEFINITE).show();
//记得引入库:
compile 'com.android.support:design:xx.xx.xx'

接下来我简略翻译下部分源码:

package android.support.design.widget;

/**
 * Snackbars为用户操作提供一个轻量级的反馈,
 * 他在屏幕底部显示一个简略的消息,
 * snackbars出现在屏幕上的所有其他要素之上,
 * 同一时间只会显示一个scankbar,
 * 在一定时间后他会自动消失,
 * 如果传递给scankbar的父容器是CoordinatorLayout,则用户可右滑关闭他。
 * Snackbars可以包含一个动作,当你调用setAction(CharSequence,      android.view.View.OnClickListener)方法时(设置一个文本,并提供一个关于该文本的点击事件。如果设置了,则文本显示在scankbar的内部右侧)
 * 如果你讲关注snackbar的显示或隐藏事件,你可以设置回调函数监控addCallback(BaseCallback)
 */
public final class Snackbar extends BaseTransientBottomBar<Snackbar> {

    /**
     *      无限期的显示一个Snackbar。意思也就是说这个Snackbar在被调用show()后显示,直到被调用关闭,或者有另一个被显示时才会关闭。
     *
     */
    public static final int LENGTH_INDEFINITE = BaseTransientBottomBar.LENGTH_INDEFINITE;

    public static final int LENGTH_SHORT = BaseTransientBottomBar.LENGTH_SHORT;

    public static final int LENGTH_LONG = BaseTransientBottomBar.LENGTH_LONG;

    /**
     * Snackbar的回调类.
     *
     * @see BaseTransientBottomBar#addCallback(BaseCallback)
     */
    public static class Callback extends BaseCallback<Snackbar> {
        /** 表示Snackbar被滑动关闭.*/
        public static final int DISMISS_EVENT_SWIPE = BaseCallback.DISMISS_EVENT_SWIPE;
        /** 表示Snackbar被点击action后关闭.*/
        public static final int DISMISS_EVENT_ACTION = BaseCallback.DISMISS_EVENT_ACTION;
        /** 表示Snackbar显示到一定时间后关闭.*/
        public static final int DISMISS_EVENT_TIMEOUT = BaseCallback.DISMISS_EVENT_TIMEOUT;
        /** 表示Snackbar被调用dismiss()后关闭.*/
        public static final int DISMISS_EVENT_MANUAL = BaseCallback.DISMISS_EVENT_MANUAL;
        /** 表示Snackbar被一个新的Snackbar显示时关闭.*/
        public static final int DISMISS_EVENT_CONSECUTIVE = BaseCallback.DISMISS_EVENT_CONSECUTIVE;

        @Override
        public void onShown(Snackbar sb) {
// Stub implementation to make API check happy.
        }

        @Override
        public void onDismissed(Snackbar transientBottomBar, @DismissEvent int event) {
// Stub implementation to make API check happy.
        }
    }

    private BaseCallback<Snackbar> mCallback;

    private Snackbar(ViewGroup parent, View content, ContentViewCallback contentViewCallback) {
        super(parent, content, contentViewCallback);
    }

    /**
     * 构造一个对象
     *
     * Snackbar会尝试从给定的容器中向上寻找一个合适的父容器来托管他的view. 他的父容器会被认为是CoordinatorLayout或者是decorView.先找到了其中的某一个就结束查找。
     * 如果给定的容器中包含CoordinatorLayout布局,那么这个Scankbar将会获得更多的特性,比喻滑动删除scankbar.
     *
     * @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) {
        final ViewGroup parent = findSuitableParent(view);
        if (parent == null) {
            throw new IllegalArgumentException("No suitable parent found from the given view. "
                    + "Please provide a valid view.");
        }

        final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        final SnackbarContentLayout content =
                (SnackbarContentLayout) inflater.inflate(
                        R.layout.design_layout_snackbar_include, parent, false);
        final Snackbar snackbar = new Snackbar(parent, content, content);
        snackbar.setText(text);
        snackbar.setDuration(duration);
        return snackbar;
    }

    //查找合适的父容器
    private static ViewGroup findSuitableParent(View view) {
        ViewGroup fallback = null;
        do {

            if (view instanceof CoordinatorLayout) {//如果是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;
    }

    /**
     * 更新文本。看这意思,是可以给一个正在显示的scankbar更新文本?
     */
    @NonNull
    public Snackbar setText(@NonNull CharSequence message) {
        final SnackbarContentLayout contentLayout = (SnackbarContentLayout) mView.getChildAt(0);
        final TextView tv = contentLayout.getMessageView();
        tv.setText(message);
        return this;
    }

    /**
     * 设置一个带点击动作的文本,以及回调函数。
     * 点击文本的同时关闭scankbar。
     * 设置了文本则显示,并设置事件。如果没有设置,则隐藏。看来是已有的布局了
     * @param text     Text to display for the action
     * @param listener callback to be invoked when the action is clicked
     */
    @NonNull
    public Snackbar setAction(CharSequence text, final View.OnClickListener listener) {
        final SnackbarContentLayout contentLayout = (SnackbarContentLayout) mView.getChildAt(0);
        final TextView tv = contentLayout.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);
// Now dismiss the Snackbar
                    dispatchDismiss(BaseCallback.DISMISS_EVENT_ACTION);
                }
            });
        }
        return this;
    }

    /**
     * 设置点击动作的文本颜色
     */
    @NonNull
    public Snackbar setActionTextColor(ColorStateList colors) {
        final SnackbarContentLayout contentLayout = (SnackbarContentLayout) mView.getChildAt(0);
        final TextView tv = contentLayout.getActionView();
        tv.setTextColor(colors);
        return this;
    }

    /**
     * 设置回调函数,来监控scankbar的显示与隐藏动作.
     * 什么?这个方法过时了?
     * 请使用addCallback(BaseCallback)和removeCallback(BaseCallback)函数.
     *
     * @param callback Callback to notify when transient bottom bar events occur.
     * @deprecated Use {@link #addCallback(BaseCallback)}
     * @see Callback
     * @see #addCallback(BaseCallback)
     * @see #removeCallback(BaseCallback)
     */
    @Deprecated
    @NonNull
    public Snackbar setCallback(Callback callback) {
// The logic in this method emulates what we had before support for multiple
// registered callbacks.
        if (mCallback != null) {
            removeCallback(mCallback);
        }
        if (callback != null) {
            addCallback(callback);
        }
// Update the deprecated field so that we can remove the passed callback the next
// time we're called
        mCallback = callback;
        return this;
    }

    ...
}

搜嘎,简略的读下源码后发现这个类很简单,那么更奇葩的用法来了:

@Override
    public void onClick(View v) {
        Snackbar sb = Snackbar.make(v,"aa",Snackbar.LENGTH_LONG).setAction("是吗?", new View.OnClickListener() {
            @Override
            public void onClick(View v) {
//点击了"是吗?"字符串操作
            }
        }).setActionTextColor(Color.RED).setText("aa是不够的").addCallback(new BaseTransientBottomBar.BaseCallback<Snackbar>() {
            @Override
            public void onDismissed(Snackbar transientBottomBar, int event) {
                super.onDismissed(transientBottomBar, event);
                Log.d("MainActivity","onDismissed");
            }

            @Override
            public void onShown(Snackbar transientBottomBar) {
                super.onShown(transientBottomBar);
                Log.d("MainActivity","onShown");
            }
        });
        sb.show();
//sb.isShown();
//sb.dismiss();

注意看,Snackbar sb = make(v,”aa”,……
第一个参数我给的v,就是当前点击的按钮,什么情况?
源码中说了,他会从这个view起想上查找一个合适的父容器,直到找到CoordinatorLayout或者decorView。先找到了其中的某一个就结束查找。如果找到了CoordinatorLayout,则可以有右滑删除功能哦。

如果想设置显示的内容,和整个背景色,也很简单。自己添加布局就可以了,跟toast一样,没多大意义不说了。

这么简单,不贴demo了。动手练习。

转载请注明:Android开发中文站 » Android Snackbar简单解析

您必须 登录 才能发表评论!