Material Design 控制元件知識梳理(1) - Android Design Support Library 是什麼 Material Design 控制元件知識梳理(2) - AppBarLayout & CollapsingToolbarLayout Material Design 控制元件知識梳理(3) - BottomSheet && BottomSheetDialog && BottomSheetDialogFragment Material Design 控制元件知識梳理(4) - FloatingActionButton Material Design 控制元件知識梳理(5) - DrawerLayout && NavigationView Material Design 控制元件知識梳理(6) - Snackbar Material Design 控制元件知識梳理(7) - BottomNavigationBar Material Design 控制元件知識梳理(8) - TabLayout Material Design 控制元件知識梳理(9) - TextInputLayout
一、概述
Snackbar
的作用和之前使用的Toast
類似,都是作為一種輕量級的使用者提示:
Toast
相比,它又增加了一些額外的互動操作,今天我們就一起來學習一下有關Snackbar
的知識。
二、Snackbar
的基礎使用
當我們需要使用Snackbar
時,首先需要呼叫它的make
靜態方法來獲得一個Snackbar
物件,之後我們對於Snackbar
的操作都是通過這個物件:
public void showSnackBar(View view) {
mSnackBarRootView = Snackbar.make(mCoordinatorLayout, "MessageView", Snackbar.LENGTH_INDEFINITE);
mSnackBarRootView.setActionTextColor(getResources().getColor(android.R.color.holo_orange_dark));
mSnackBarRootView.setAction("ActionView", new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d("mSnackBarRootView", "click ActionView");
}
});
mSnackBarRootView.show();
}
複製程式碼
外觀設定
對於Snackbar
,可以分為兩個區域,MessageView
和ActionView
,其中MessageView
只支援設定文案,而ActionView
不僅支援設定文案,還支援設定文案的顏色以及監聽點選事件,具體的方法大家可以查閱API
:
操作方式
- 顯示
Snackbar
時,需要像上面一樣主動呼叫show()
方法。 - 隱藏
Snackbar
時,有以下幾種操作方式: - 主動呼叫
dismiss
方法 - 點選
ActionView
- 從左向右滑動
Snackbar
- 通過
setDuration
方法,讓Snackbar
在經過指定的時間之後自動隱藏
三、Snackbar
進階
3.1 改變Snackbar
的外觀
從上面可以看出,Snackbar
對於外觀的支援不夠充分,比如不能定義MessageView
的顏色、以及整個Snackbar
的背景等等,下面,我們就來看一下如何對它的外觀進行進一步的定製。
原始碼當中對Snackbar
物件的初始化分為了下面三步操作:
public static Snackbar make(@NonNull View view, @NonNull CharSequence text,
@Duration int duration) {
//1.尋找Snackbar的父容器
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());
//2.例項化出Snackbar的佈局
final SnackbarContentLayout content =
(SnackbarContentLayout) inflater.inflate(
R.layout.design_layout_snackbar_include, parent, false);
//3.利用父容器和Snackbar的佈局,構造Snackbar物件
final Snackbar snackbar = new Snackbar(parent, content, content);
snackbar.setText(text);
snackbar.setDuration(duration);
return snackbar;
}
複製程式碼
通過檢視佈局,可以發現SnackbarContentLayout
就是包含了MessageView
和ActionView
的父容器,也就是例子當中的黑色背景:
SnackbarContentLayout
呢,我們看一下Snackbar
的建構函式:
protected BaseTransientBottomBar(@NonNull ViewGroup parent,
@NonNull View content,
@NonNull ContentViewCallback contentViewCallback) {
//這個是我們傳入的mCoordinatorLayout.
mTargetParent = parent;
//mView是mCoordinatorLayout的子View,同時又是SnackbarContentLayout的父容器
mView = (SnackbarBaseLayout) inflater.inflate(
R.layout.design_layout_snackbar, mTargetParent, false);
mView.addView(content);
}
複製程式碼
而Snackbar
提供了getView
方法來得到mView
物件,也就是佈局中的Snackbar$SnackbarLayout
:
public View getView() {
return mView;
}
複製程式碼
那麼整個邏輯就很清楚了,我們可以通過通過mView
獲得SnackbarContentLayout
,然後進行一系列的定製:
- 改變
Snackbar
的背景:
private void changeSnackBarBackgroundColor(Snackbar snackbar) {
View view = snackbar.getView();
view.setBackgroundColor(getResources().getColor(android.R.color.holo_purple));
}
複製程式碼
- 改變
MessageView
字型的顏色和大小:
private void changeSnackBarMessageViewTextColor(Snackbar snackbar) {
ViewGroup viewGroup = (ViewGroup) snackbar.getView();
SnackbarContentLayout contentLayout = (SnackbarContentLayout) viewGroup.getChildAt(0);
TextView textView = (TextView) contentLayout.getChildAt(0);
textView.setTextColor(getResources().getColor(android.R.color.darker_gray));
}
複製程式碼
3.2 Snackbar
彈出位置分析
Snackbar
會彈出在父容器的底部,也就是上面findSuitableParent
的過程,我們來分析一下這一尋找的過程,就可以知道Snackbar
彈出的位置:
private static ViewGroup findSuitableParent(View view) {
ViewGroup fallback = null;
do {
if (view instanceof 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;
}
複製程式碼
它其實是從我們在make
方法中傳入的View
作為起點,沿著整個View
樹向上尋找,如果發現是CoordinatorLayout
或者到達了R.id.content
,那麼就停止尋找,否則將一直到達View
樹的根節點為止,所以,如果我們的CoordinatorLayout
不是全屏的話,那麼Snackbar
有可能不是彈出在整個螢幕的底部,例如下面這樣,我們給Snackbar
新增了一個marginBottom
:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/cl_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="200dp"
tools:context="com.demo.lizejun.repotransition.SnackBarActivity">
<TextView
android:id="@+id/show_snack_bar"
android:text="showSnackBar"
android:layout_width="match_parent"
android:layout_height="66dp"
android:gravity="center"
android:layout_margin="5dp"
android:textColor="@android:color/white"
android:background="@android:color/holo_orange_dark"
android:onClick="showSnackBar"/>
</android.support.design.widget.CoordinatorLayout>
複製程式碼
那麼彈出的效果為:
3.3 Snackbar
和FloatingActionButton
的結合
當Snackbar
彈出的時候,有可能會遮擋底部的FloatingActionButton
,此時就需要在make
方法中傳入CoordinatorLayout
,讓Snackbar
彈出的時候,讓Fab
上移一定的高度,可以參考之前的這篇文章:MD控制元件 - FloatingActionButton
四、總結
Snackbar
使用起來很簡單,它比Toast
增加了更多的操作性,也是官方推薦的替換Toast
的控制元件。
更多文章,歡迎訪問我的 Android 知識梳理系列:
- Android 知識梳理目錄:www.jianshu.com/p/fd82d1899…
- 個人主頁:lizejun.cn
- 個人知識總結目錄:lizejun.cn/categories/