* 本篇文章已授權微信公衆號 guolin_blog (郭霖)獨家發佈:
https://mp.weixin.qq.com/s/l62PtbmrIOkVKfJ2r0JwEw
App的界面消息提示中,Toast和Snackbar是咱們經常打交道的哥倆,在使用的過程中,如果不加以封裝和處理,調用的簡易性以及性能和用戶體驗上就會存在諸多問題。下面給大家介紹一個我封裝的庫,SmartShow的使用和實現。
爲了讓SmartShow庫更加健壯,如果您在使用過程中發現任何問題,請聯繫我,我會立即跟進修復和維護。
感謝您的支持!
微信:w361281607
添加依賴
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);
}
});
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