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(新內容或新位置)不會顯示足額時間便消失,體驗不好。如下圖:
圖片加載失敗
也有如此實現的:


    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並不是立即生效, 要等到下一次顯示才生效,就導致本次位置改變失敗。如下圖:
圖片加載失敗
之所以出現這樣的問題,與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);

    }

圖片加載失敗
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);

                    }
                });

圖片加載失敗
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的自定義佈局
圖片加載失敗
圖片加載失敗
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 !");

            }

        });
    }

圖片加載失敗
 

 

修改佈局風格


        SmartSnackbar.init(this)

                .backgroundColorRes(R.color.colorPrimary)

                .actionColorRes(R.color.colorAccent)

                .msgTextSizeSp(16)

                .actionSizeSp(16);

圖片加載失敗

內部實現上,除了必要的Snackbar,爲了減少創建不必要的對象,SnackbarSetting、SnackbarShow均定義爲接口,兩者加上Runnable,View.OnClickListener四個接口全部由單例SmartSnackbar實現,對外需要暴露何種功能,則返回何種接口類型

完整源碼

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章