Smart Toast and Snackbar:简化调用,并提高性能和用户体验!

 

 * 本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布:

https://mp.weixin.qq.com/s/l62PtbmrIOkVKfJ2r0JwEw

App的界面消息提示中,Toast和Snackbar是咱们经常打交道的哥俩,在使用的过程中,如果不加以封装和处理,调用的简易性以及性能和用户体验上就会存在诸多问题。下面给大家介绍一个我封装的库,SmartShow的使用和实现。

为了让SmartShow库更加健壮,如果您在使用过程中发现任何问题,请联系我,我会立即跟进修复和维护。

感谢您的支持!

微信:w361281607

邮箱:[email protected]
 

添加依赖

 

1.在Project的gradle文件中


allprojects {

    repositories {

        ...

        maven { url 'https://jitpack.io' }

    }

}

2.在Module的grable文件中

    compile ( 'com.github.the-pig-of-jungle:SmartShow:v1.0.7' ){

        exclude group: 'com.android.support'

    }

    //添加适合你项目的design库版本

    compile 'com.android.support:design:x.y.z'

SmartToast部分

特点:

1.全局始终使用一个Toast实例,节省内存
2.如果Toast正在显示,多次触发同一内容的Toast,不会重复弹出
3.新的Toast(内容或位置发生了变化)来临时,会立即弹出,不会等到当前显示的Toast的duration耗尽再弹出,虽不会创建新的Toast实例,但具有切换效果(与你手机系统原生Toast的切换动画一致)
4.可修改Toast默认布局的风格,如背景颜色,文字大小和颜色等
5.可为Toast设置自定义布局,并进行代码处理

 

注意

 

关闭app的系统通知权限,将导致SmartToast无法显示,原因如下:
Toast的内部原理使用NotificationManagerService,关闭通知权限后,无法显示。
这是原生Toast本身的特性,而不是SmartShow开源库的bug。
以淘宝app和优酷app的"再按一次退出程序"的Toast提示为例,关闭他们的通知权限,
也会导致Toast不显示,感兴趣的话可以去试一试。

使用:

第一步,必须初始化,在Application的onCreate()方法中初始化
方式 1:


        //使用默认布局的普通Toast

        SmartToast.plainToast(this);

若想修改默认布局的风格,可继续链式调用,不过这并不是必须的


        //返回PlainToastSetting对象,对布局进行风格设置

        SmartToast.plainToast(this)

                /*

                设置背景颜色,有可选方法,直接以颜色值为参数。Toast的默认背景是一个圆角图片,当你设置了

                背景颜色时,原有背景失效。SmartToast内部用GradientDrawable实现背景,可以保证大小与你

                手机系统的Toast一致,但是不同品牌手机的Toast的圆角半径不尽相同,将统一使用2.5dp

                */

                .backgroundColorRes(R.color.colorPrimary)

                //设置文本颜色,有可选方法,直接以颜色值为参数

                .textColorRes(R.color.colorAccent)

                //设置文本字体大小,单位为sp

                .textSizeSp(18)

                //设置是否加粗文本

                .textBold(true)

                //如果以上还不够,可调用此方法

                .processPlainView(new ProcessViewCallback() {

                    //outParent为显示文本的TextView的父布局,msgView为显示文本的TextView

                    @Override
                    public void processPlainView(LinearLayout outParent, TextView msgView) {

                        //处理代码

                        ...

                    }
                });

方式 2:


        使用自定义布局的Toast

        SmartToast.customToast(this)

                        /*

                        设置自定义布局,有重载方法,直接以View为参数。在自定义布局中,一定要设置显示

                        文本提示的TextView的Id为android:id="@id/custom_toast_msg"。如果不调用下面

                        的方法,那么上面的调用与SmartToast.plainToast(this)等效

                        */

                        .view(R.layout.custom_toast);

若想对自定义的布局进行代码处理,可继续链式调用,不过这并不是必须的


        返回CustomToastSetting对象

        SmartToast.customToast(this)

                //填充布局

                .view(R.layout.custom_toast)

                //对自定义布局进行代码处理

                .processCustomView(new ProcessViewCallback() {

                    @Override
                    public void processCustomView(View view) {

                        //处理代码

                        ...

                    }
                });

第二步,调用show方法显示Toast,duration和常用的显示位置体现在方法名上,而不是传参,使得调用非常简易
Short Toast


        //在默认位置显示

        SmartToast.show("我是朱志强!");

        //在屏幕顶部显示,距离顶部位置为Toast在Y方向默认的偏移距离

        SmartToast.showAtTop("我是朱志强!");

        //在屏幕中央显示

        SmartToast.showInCenter("我是朱志强!");

        //在指定位置显示,x,y方向偏移量单位为dp

        SmartToast.showAtLocation("我是朱志强",Gravity.LEFT | Gravity.TOP,10,10);

Long Toast


        //在默认位置显示

        SmartToast.showLong("我是朱志强!");

        //在屏幕顶部显示,距离顶部位置为Toast在Y方向默认的偏移距离

        SmartToast.showLongAtTop("我是朱志强!");

        //在屏幕中央显示

        SmartToast.showLongInCenter("我是朱志强!");

        //在指定位置显示,x,y方向偏移量单位为dp

        SmartToast.showLongAtLocation("我是朱志强",Gravity.LEFT | Gravity.TOP,10,10);

         //隐藏Toast

         SmartToast.dismiss();

实现

1.全局使用一个Toast实例。
Toast的视图是通过独立的Window来显示的,并不依赖于任何Activity,在任何Activity未启动的情况下,依然可以显示Toast。所以用应用级的Context——Application创建Toast,就可以全局使用一个Toast实例了。
2.如果Toast正在显示,多次触发同一内容的Toast,不会重复弹出。
全局使用一个Toast实例即可解决此问题。同一Toast实例,如果当前正在显示,再次调用show方法,并不会重复弹出。
3.新的Toast(内容或位置发生了变化)来临时,会立即弹出,不会等到当前显示的Toast的duration耗尽再弹出,虽不会创建新的Toast实例,但具有切换效果。
很容易会想到这么做,全局使用一个Toast实例的基础上,先隐藏再显示,达到切换效果:

	
    //此辅助方法用于先正常显示一个Toast

    public void onNormalShowFirstClick(View view) {

        mToast.setText("Showing!");

        mToast.show();

    }


    //先隐藏再显示的方式显示一个Toast

    public void onShowClick(View view) {

        mToast.cancel();

        mToast.setText("苹果!");

        mToast.show();

    }

    //先隐藏再显示的方式显示另一个Toast

    public void onAnotherShow(View view) {

        mToast.cancel();

        mToast.setText("香蕉!");

        mToast.show();

    }

然而很遗憾,如此无济于事。没有Toast显示时,先后调用cancel和show不会显示Toast。当前正在显示Toast时,先后调用cancel和 show,虽会显示,但新的Toast(新内容或新位置)不会显示足额时间便消失,体验不好。如下图:
Smart Toast and Snackbar:简化调用,并提高性能和用户体验!_第1张图片
也有如此实现的:


    public void onShowClick(View view) {

        mToast.setText("苹果!");

        mToast.show();

    }

    public void onAnotherShow(View view) {

        mToast.setText("香蕉!");

        mToast.show();

    }


    public void onShowInCenterClick(View view) {

        mToast.setGravity(Gravity.CENTER,0,0);

        mToast.setText("桔子!");

        mToast.show();

    }

如此会有两个问题,内容改变后没有切换效果,如果Toast正在显示,调用setText可以立即生效,但setGravity并不是立即生效, 要等到下一次显示才生效,就导致本次位置改变失败。如下图:
Smart Toast and Snackbar:简化调用,并提高性能和用户体验!_第2张图片
之所以出现这样的问题,与Toast的显示原理有关。
调用Toast的show方法时,客户端程序只是向系统服务发起一个Toast显示请求,然后被动等待回调显示Toast,并不会立即显示。此时紧接着调用cancel方法时,会向Tn的handler发送一个隐藏Toast的消息。 Toast被动回调时,视图得以显示,然后紧接着执行隐藏代码,Toast便很快消失。你可能想过cancel完后延迟若干时间后调用show方法,但是这个延时的长短不好控制,短了起不到效果,长了会导致新的Toast不会立即替换掉旧的,体验不好。
Toast视图的显示和消失是交给内部类TN管理的,并保存为成员变量,我们通过反射拿到TN,然后每次都只隐藏视图,不发起队列移除操作,问题就解决了。


   public static void dismiss() {

        if (sSmartToast != null && sSmartToast.mToast != null) {

            try {

                Field tnField = Toast.class.getDeclaredField("mTN");

                tnField.setAccessible(true);

                Object tn = tnField.get(sSmartToast.mToast);

                Method hideMethod = tn.getClass().getDeclaredMethod("hide");

                hideMethod.setAccessible(true);

                hideMethod.invoke(tn);

            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }

show方法逻辑


 private static void showHelper(CharSequence msg, int gravity, int xOffset, int yOffset, int duration) {

        if (sSmartToast.mCustomView != null && sSmartToast.mCustomMsgView == null) {

            return;

        }

        msg = msg == null ? "" : msg;

        getToast().setDuration(duration);

        //位置是否改变

        boolean locationChanged = sSmartToast.locationChanged(gravity, xOffset, yOffset);

        //内容是否改变

        boolean contentChanged = !sSmartToast.mCurMsg.equals(msg);

        sSmartToast.mCurMsg = msg;

        sSmartToast.mGravity = gravity;

        sSmartToast.mXOffset = xOffset;

        sSmartToast.mYOffset = yOffset;

        //如果Toast正在显示,且内容或位置发生了变化

        if (ViewCompat.isAttachedToWindow(getToast().getView()) 

             && (contentChanged || locationChanged)) {

            //先隐藏

            SmartToast.dismiss();

            //再显示,为了体验更好,延时150毫秒发送Runnable执行

            getToast().getView().postDelayed(sSmartToast, 150);

        } else {

            //否则更新Toast的内容并正常显示即可

            sSmartToast.updateToast();

            getToast().show();

        }

    }
    //SmartToast自身作为延时显示所发送的Runnable
    @Override
    public void run() {

        updateToast();

        getToast().show();

    }

    private void updateToast() {

        if (mCustomMsgView != null) {

            mCustomMsgView.setText(mCurMsg);

        } else {

            getToast().setText(mCurMsg);

        }

        getToast().setGravity(mGravity, mXOffset, mYOffset);

    }

用SmartToast执行同样的逻辑,试一下


    public void onShowClick(View view) {

        //默认位置

        SmartToast.show("苹果!");

    }

    public void onAnotherShow(View view) {

        //默认位置

        SmartToast.show("香蕉!");

    }


    public void onShowInCenterClick(View view) {

        SmartToast.showInCenter("桔子!");

    }

    public void onShowAtTopClick(View view) {

        SmartToast.showAtTop("芒果!");

    }


    public void onShowAtSomeLocationClick(View view) {

        //左上角,x,y偏移量均为10dp

        SmartToast.showAtLocation("荔枝", Gravity.LEFT | Gravity.TOP,10,10);

    }

Smart Toast and Snackbar:简化调用,并提高性能和用户体验!_第3张图片
4.修改Toast默认布局风格,如背景颜色,文字大小和颜色等


   private void setupPlainToast() {

        //获取父布局

        LinearLayout outParent = (LinearLayout) mToast.getView();

        //获取显示消息的View

        TextView msgView = (TextView) outParent.findViewById(android.R.id.message);

        //如果设置了背景色

        if (mBgColor != -1) {

            /*

            Toast视图的背景是一个.9圆角图片,不同的手机系统圆角半径不同,而且.9图有的有padding,有的

            没padding,当你设置了背景颜色,我们将创建GradientDrawable作为背景,为了保持与你手机系统

            的Toast大小一致,如果原来的.9图有padding我们将追加到显示消息的TextView上。圆角半径统一

            为2.5dp。

            */

            NinePatchDrawable ninePatchDrawable = (NinePatchDrawable)

             ContextCompat.getDrawable(mAppContext, android.R.drawable.toast_frame);

            Rect rect = new Rect();

            ninePatchDrawable.getPadding(rect);

            msgView.setPadding(msgView.getPaddingLeft() + rect.left, msgView.getPaddingTop(),  

            msgView.getPaddingRight() + rect.right, msgView.getPaddingBottom());

            GradientDrawable gradientDrawable = new GradientDrawable();

            gradientDrawable.setColor(mBgColor);

            gradientDrawable.setCornerRadius(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,

             2.5f, mAppContext.getResources().getDisplayMetrics()));

            ViewCompat.setBackground(outParent, gradientDrawable);

        }

        if (mTextColor != -1) {

            msgView.setTextColor(mTextColor);

        }

        if (mTextSizeSp != -1) {

            msgView.setTextSize(TypedValue.COMPLEX_UNIT_SP, mTextSizeSp);

        }
        msgView.setGravity(Gravity.CENTER);

        msgView.getPaint().setFakeBoldText(mTextBold);

        if (mProcessViewCallback != null) {

            mProcessViewCallback.processPlainView(outParent, msgView);

        }

    }

测试代码如下:


        SmartToast.plainToast(this)

                .backgroundColorRes(R.color.colorPrimary)

                .textColorRes(R.color.colorAccent)

                .textSizeSp(18)

                .textBold(true)

                .processPlainView(new ProcessViewCallback() {

                    @Override
                    public void processPlainView(LinearLayout outParent, TextView msgView) {

                        //添加左图标

                        Drawable d = ContextCompat.getDrawable(msgView.getContext(),

                        android.R.drawable.ic_menu_add);

                        d.setBounds(0,0,d.getIntrinsicWidth(),d.getIntrinsicHeight());

                        msgView.setCompoundDrawables(d,null,null,null);

                    }
                });

Smart Toast and Snackbar:简化调用,并提高性能和用户体验!_第4张图片
5.为Toast设置自定义布局,并进行代码处理


    private void setupCustomToast() {

        if (mProcessViewCallback != null) {

            mProcessViewCallback.processCustomView(mCustomView);

        }

        //将你自定义布局中显示消息的TextView的id设为R.id.custom_toast_msg

        mCustomMsgView = (TextView) mCustomView.findViewById(R.id.custom_toast_msg);

        mToast.setView(mCustomView);

        mCurMsg = "";

    }

测试代码如下:


        SmartToast.customToast(this)

                .view(R.layout.custom_toast)

                //下面的方法不是必须调用的

                .processCustomView(new ProcessViewCallback() {

                    @Override
                    public void processCustomView(View view) {

                        ((TextView) view.findViewById(R.id.custom_toast_msg))

                           .setTextColor(Color.WHITE);

                    }
                });

Toast的自定义布局
Smart Toast and Snackbar:简化调用,并提高性能和用户体验!_第5张图片
Smart Toast and Snackbar:简化调用,并提高性能和用户体验!_第6张图片
6.内部实现上,除了所必须的Toast单例(懒加载模式)外,为了减少创建不必要的对象,PlainToastSetting、CustomToastSetting均定义为接口,两者再加上Runnalbe全部由单例SmartToast实现,对外需要暴露何种功能,则返回何种接口类型。

SmartSnackbar部分

特点:

1.Snackbar的显示原理与Toast不同,Toast通过Window展示视图,全局可复用一个实例。Snackbar则是把视图内嵌到当前Activity的android.R.id.content容器或某个CoordinatorLayout中。在获取方式不变(容器不变)的情况下,同一页面可复用一个Snackbar实例,节省内存
2.同一页面,如果Snackbar正在显示,多次触发同一内容的Snackbar,不会重复弹出
3.同一页面,如果Snackbar正在显示,再次触发Snackbar,如果内容(msg或actionText)发生了变化(不会重建Snackbar实例)或内嵌的容器发生了变化(会重建Snackbar实例),会重新弹出,具有切换效果(与你手机系统原生Snackbar的切换动画一致)
4.可修改布局风格,如背景颜色,文字大小和颜色等

使用:

第一步,初始化。这不是必须的,若想修改Snackbar布局的默认风格,则在Application的onCreate()方法中初始化


        //返回SnackbarSetting对象,对布局进行各种风格设置

        SmartSnackbar.init(this)

                //设置背景颜色,有可选方法,直接以颜色值为参数

                .backgroundColorRes(R.color.colorPrimary)

                //设置消息文本颜色,有可选方法,直接以颜色值为参数

                .msgTextColorRes(R.color.white)

                //设置动作文本颜色,有可选方法,直接以颜色值为参数

                .actionColorRes(R.color.colorAccent)

                //设置消息文本字体大小,单位为sp

                .msgTextSizeSp(18)

                //设置动作文本字体大小,单位为sp

                .actionSizeSp(18)

                //如果以上还不够,可调用该方法

                .processView(new ProcessViewCallback() {

                    @Override
                    public void processSnackbarView(Snackbar.SnackbarLayout layout,

                     TextView msgView, TextView actionView) {

                        //处理代码

                        ...

                    }
                });

第二步,在你的BaseActivity的onDestroy()里调用SmartSnackbar.destroy(this)方法


    @Override
    protected void onDestroy() {

        super.onDestroy();

        /*

        如果当前页面创建过Snackbar,则退出页面时,会回收资源。如果没有,则不会回收资源,比如Activity A 显示过

        Snackbar,然后启动了B,B没有显示过Snackbar,当B销毁时,不会回收资源,回到A再次显示Snackbar,可复用,

        不必重建Snackbar实例,提高效率

        */

        SmartSnackbar.destroy(this);
    }

第三步,获取当前页面的Snackbar,调用show方法显示,三种duration体现在方法名上,而不是传参,尽可能简化调用

Short Snackbar


        //传入Activity,获取当前页面的Snackbar,显示消息

        SmartSnackbar.get(this).show("我是朱志强");

Long Snackbar


        //传入Activity,获取当前页面的Snackbar,显示消息

        SmartSnackbar.get(this).showLong("我是朱志强");

Indefinite Snackbar


        //传入Activity,获取当前页面的Snackbar,显示消息和动作文本,传入点击动作文本的回调代码

        SmartSnackbar.get(this).showIndefinite("我是朱志强", "打赏", new View.OnClickListener() {

            @Override
            public void onClick(View v) {

                Log.d("SmartShow","Thank you !");

            }
        });

        //传入Activity,获取当前页面的Snackbar,显示消息和动作文本,不传第三个参数,默认行为为Snackbar消失

        SmartSnackbar.get(this).showIndefinite("我是朱志强","打赏");

显示Short和Long类型的Snackbar时,通常不会显示动作文本,而Indefinite Snackbar通常不会只显示消息文本,但实际上该库为三种Snackbar均提供了以上参数个数为1,2和3的方法。



        //隐藏当前Snackbar

        SmartSnackbar.dismiss();

一般情况下,我们不会监听Snackbar的显示和消失,但如有此需要,将当前页面的Activity实现SnackbarCallback接口,然后重写方法

即可。在SmartSnackbar显示时,会检测当前页面是否实现该接口,是则进行回调。


public class SnackbarActivity extends BaseActivity implements SnackbarCallback {


    @Override
    protected int contentLayout() {
        return R.layout.activity_smart_show;
    }

    @Override
    public void onSnackbarShown(Snackbar sb) {
        Log.d("Main", "shown");
    }

    @Override
    public void onSnackbarDismissed(Snackbar sb, int event) {
        Log.d("Main", "dismiss");
    }

}

SmartSnackbar获取方式的说明:

以上示例获取SmartSnackbar使用的是public static SnackbarShow get(Activity activity),
还可使用public static SnackbarShow get(CoordinatorLayout view)。
根据谷歌源码,我们知道创建Snackbar时需传入一个当前页面的某个View。
实际上,Snackar会以该View为基点,沿着整个View Tree上溯,直到找到CoordinatorLayout容器或android.R.id.content 容器,哪个先找到,就将视图嵌入其中。
为了提高效率,直接将android.R.id.content或者CoordinatorLayout传入会更好。
以CoordinatorLayout为内嵌容器时,Snackbar会有一些特殊的行为,如可以用手指手动滑动移除,显示时会导致FloatingActionButton升高等。
所以建议,在使用SmartSnackbar时,如果你的页面想以某个具体CoordinatorLayout作为容器,则调用public static SnackbarShow get(CoordinatorLayout view)。
否则调用public static SnackbarShow get(Activity activity),内部会自动将 android.R.id.content作为容器。

实现


    public static SnackbarShow get(Activity activity){

        //保存当前页面的Context

        getSmartSnackbar(activity).mPageContext = activity;

        //取出android.R.id.content

        View view = activity.findViewById(android.R.id.content);

        return getFromView(view);

    }

    public static SnackbarShow get(CoordinatorLayout view){

        //保存当前页面的Context

        getSmartSnackbar(view.getContext()).mPageContext = view.getContext();

        return getFromView(view);

    }

    private static SnackbarShow getFromView(View view){
        /*

        如果Snackbar尚未创建,则创建之

        mBaseTraceView 用来保存创建Snackbar时传入的View,由于入口做了控制,所以只可能是android.R.id.content

        或者CoordinatorLayout。它发生变化包括两种情形:1.同一页面,本次获取方式与上次不同,导致

        Snackbar的容器改变 2.进入新的页面,两种情况都需要重建Snackbar实例

        第三个条件,判断是否通过手指滑动移除了Snackbar,同一Snackbar实例,手指滑动移除后,再次调用

        show方法无法显示,且Snackbar视图的Visibility属性变为View.GONE,也需要重建Snackbar

        尽管这里判断的条件比较多,但实际上,同一页面重建Snackbar实例的可能性很小。首先,同一页面不会
    
        先后采用不用的容器,因为没有意义。另外,如果不想要Snackbar在CoordinatorLayout中的特殊行为,

        一般页面的容器都是android.R.id.content。手指滑动移除发生的机率也很小。

         */
        if (sSmartSnackbar.mSnackbar == null || sSmartSnackbar.mBaseTraceView != view

              || sSmartSnackbar.mSnackbar.getView().getVisibility() != View.VISIBLE){

            sSmartSnackbar.rebuildSnackbar(view);

        }

        return sSmartSnackbar;

    }

    private void rebuildSnackbar(View view) {

        mCurMsg = "";

        mCurActionText = "";

        mBaseTraceView = view;

        sSmartSnackbar.mSnackbar = Snackbar.make(mBaseTraceView,mCurMsg,Snackbar.LENGTH_SHORT);

        //若当前页面实现了SmartSnackbar接口,则添加回调

        if (mPageContext instanceof SnackbarCallback){

            sSmartSnackbar.mSnackbar.addCallback(this);

        }

        if (mBgColor != -1){

            sSmartSnackbar.mSnackbar.getView().setBackgroundColor(mBgColor);

        }

        TextView msgView = (TextView) 

                         sSmartSnackbar.mSnackbar.getView()
 
                          .findViewById(android.support.design.R.id.snackbar_text);

        if (mMsgColor != -1){

            msgView.setTextColor(mMsgColor);

        }

        if (mMsgTextSizeSp != -1){

            msgView.setTextSize(TypedValue.COMPLEX_UNIT_SP,mMsgTextSizeSp);

        }

        TextView actionView = (TextView) 

                       sSmartSnackbar.mSnackbar.getView()
            
                        .findViewById(android.support.design.R.id.snackbar_action);

        if (mActionColor != -1){

            actionView.setTextColor(mActionColor);

        }
        if (mActionSizeSp != -1){

            actionView.setTextSize(TypedValue.COMPLEX_UNIT_SP,mActionSizeSp);

        }

        if (mProcessViewCallback != null){

            mProcessViewCallback.processSnackbarView(

           (Snackbar.SnackbarLayout) mSnackbar.getView(),msgView,actionView);

        }

    }

如此,在同一页面获取方式不变的情况下,始终复用同一个实例。
若启动新的页面,新页面销毁回到旧页面的情形是怎样的呢。


    public static void destroy(Activity activity){
        /*

        若mPageContext 与当前页面相等,则表示在当前页面显示过Snackbar,则回收资源,回到上一页面,再次

        显示Snackbar时必然重建,之后继续复用(获取方式不变的话);若不等,则表示在当前页面未显示过Snackbar,

        则不必回收资源,回到上一页面可继续复用(获取方式不变的话)。

        */

        if (sSmartSnackbar != null && sSmartSnackbar.mPageContext == activity){

            sSmartSnackbar.mCurMsg = "";
            sSmartSnackbar.mCurActionText = "";
            sSmartSnackbar.mOnActionClickListener = null;

            sSmartSnackbar.mSnackbar = null;
            sSmartSnackbar.mPageContext = null;
            sSmartSnackbar.mBaseTraceView = null;
        }
    }

Snackbar的msg和actionText未发生改变且Snackbar正在显示,多次触发不会重复弹出。 若发生改变,则有切换效果,但不会新建Snackbar实例(获取方式不变的话)


    private void showHelper(CharSequence msg, CharSequence actionText, View.OnClickListener 

                            onActionClickListener,int duration){

        msg = msg == null ? "" : msg;

        actionText = actionText == null ? "" : actionText;

        onActionClickListener = onActionClickListener == null ? this : onActionClickListener;

        //样貌是否发生变化

        boolean appearanceChanged = appearanceChanged(msg,actionText);

        //重新设置Snackbar

        setting(msg, actionText, onActionClickListener, duration);

        //如果样貌发生变化且Snackbar正在显示

        if (appearanceChanged && mSnackbar.isShown()){

            先隐藏掉再显示,具有切换效果

            dismissAndShowAgain();

        }else {

            //如果Snackbar没有显示或者“样貌”没有发生改变,正常显示即可

            normalShow();

        }
    }
 
 //SmartSnackbar自身实现延时显示发送的Runnable

    @Override
    public void run() {

        if (mSnackbar != null) {

            mSnackbar.setText(mCurMsg).setAction(mCurActionText, mOnActionClickListener)

                     .setDuration(mDuration)

                     .show();

        }
    }

   private boolean appearanceChanged(CharSequence msg, CharSequence actionText) {

        return !mCurMsg.equals(msg) || !mCurActionText.equals(actionText);

    }

    //先隐藏再显示

    private void dismissAndShowAgain() {

        mSnackbar.dismiss();

        mBaseTraceView.postDelayed(this, 400);

    }

    //正常显示Snackbar

    private void normalShow() {

        mSnackbar.setText(mCurMsg).setAction(mCurActionText, mOnActionClickListener)

        .setDuration(mDuration).show();

    }

测试代码如下:


    public void onAppleClick(View view) {

        SmartSnackbar.get(this).show("苹果");

    }

    public void onBananaClick(View view) {

        SmartSnackbar.get(this).showLong("香蕉");

    }


    public void onOrangeClick(View view) {

        SmartSnackbar.get(this).showIndefinite("桔子","好吃");

    }

    public void onNameClick(View view) {

        SmartSnackbar.get(this).show("我是朱志强", "打赏", new View.OnClickListener() {

            @Override
            public void onClick(View v) {

                SmartToast.showInCenter("Thank you !");

            }

        });
    }

Smart Toast and Snackbar:简化调用,并提高性能和用户体验!_第7张图片
 

 

修改布局风格


        SmartSnackbar.init(this)

                .backgroundColorRes(R.color.colorPrimary)

                .actionColorRes(R.color.colorAccent)

                .msgTextSizeSp(16)

                .actionSizeSp(16);

Smart Toast and Snackbar:简化调用,并提高性能和用户体验!_第8张图片

内部实现上,除了必要的Snackbar,为了减少创建不必要的对象,SnackbarSetting、SnackbarShow均定义为接口,两者加上Runnable,View.OnClickListener四个接口全部由单例SmartSnackbar实现,对外需要暴露何种功能,则返回何种接口类型

完整源码

github地址:https://github.com/the-pig-of-jungle/SmartShow

 

你可能感兴趣的:(Android,开源库,性能优化,&,用户体验)