相关文章
Android Snackbar控件
Android FloatingActionButton控件
Android Toolbar控件
Android AppBarLayout控件
Android CollapsingToolbarLayout控件
1. Snackbar类
Snackbar是5.0版本出现的控件,类似于Toast,显示在屏幕的底部,包含文字信息与一个可选的操作按钮。需要添加Design依赖库,并且使用Theme.AppCompat主题。
2. 创建Snackbar类
Snackbar利用静态方法make()
来创建实例
public static Snackbar make(@NonNull View view, @StringRes int resId, @Duration int duration) {
return make(view, view.getResources().getText(resId), duration);
}
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;
}
创建Snackbar实例,需要寻找合适的父视图,优先选择CoordinatorLayout作为父视图。
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;
}
3. Snackbar设置
Snackbar可以设置文本和按钮
public Snackbar setText(@NonNull CharSequence message)
public Snackbar setText(@StringRes int resId)
public Snackbar setAction(@StringRes int resId, View.OnClickListener listener)
public Snackbar setAction(CharSequence text, final View.OnClickListener listener)
public Snackbar setActionTextColor(ColorStateList colors)
public Snackbar setActionTextColor(@ColorInt int color)
Snackbar可以监听视图
addCallback(new Snackbar.Callback(){
@Override
public void onShown(Snackbar sb) {
}
@Override
public void onDismissed(Snackbar transientBottomBar, int event) {
}
})
Dismiss的event有下面五种情况
- DISMISS_ EVENT_ SWIPE,向右滑动消失,只有父视图是CoordinatorLayout情况下才会发生
- DISMISS_ EVENT_ ACTION,点击右侧按钮消失
- DISMISS_ EVENT_ TIMEOUT,设置的显示时间到了消失
- DISMISS_ EVENT_ MANUAL,调用Snackbar的dismiss方法消失
- DISMISS_ EVENT_ CONSECUTIVE,新的Snackbar出现导致旧的消失
4. SnackbarManager类
SnackbarManager用来管理Snackbar控件的状态。
Snackbar的show()
方法,会调用SnackbarManager的show()
方法,而mManagerCallback会回调Snackbar的showView()
和hideView()
方法。
static {
sHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(Message message) {
switch (message.what) {
case MSG_SHOW:
((BaseTransientBottomBar) message.obj).showView();
return true;
case MSG_DISMISS:
((BaseTransientBottomBar) message.obj).hideView(message.arg1);
return true;
}
return false;
}
});
}
final SnackbarManager.Callback mManagerCallback = new SnackbarManager.Callback() {
@Override
public void show() {
sHandler.sendMessage(sHandler.obtainMessage(MSG_SHOW, BaseTransientBottomBar.this));
}
@Override
public void dismiss(int event) {
sHandler.sendMessage(sHandler.obtainMessage(MSG_DISMISS, event, 0,
BaseTransientBottomBar.this));
}
};
public void show() {
SnackbarManager.getInstance().show(mDuration, mManagerCallback);
}
SnackbarManager内部包含两个记录mCurrentSnackbar和mNextSnackbar。在SnackbarManager的show()
方法中,
(1) 查看是否是当前Snackbar,如果是,更新超时时间,结束。
(2) 查看是否是NextSnackbar,如果是,更新数据,如果不是创建新的NextSnackbar。
(3) 取消当前Snackbar或者显示NextSnackbar。
public void show(int duration, Callback callback) {
synchronized (mLock) {
if (isCurrentSnackbarLocked(callback)) {
// 如果是当前Snackbar,更新duration和超时提示
mCurrentSnackbar.duration = duration;
mHandler.removeCallbacksAndMessages(mCurrentSnackbar);
scheduleTimeoutLocked(mCurrentSnackbar);
return;
} else if (isNextSnackbarLocked(callback)) {
// 如果是NextSnackbar,更新duration
mNextSnackbar.duration = duration;
} else {
// 否则就创建新的NextSnackbar
mNextSnackbar = new SnackbarRecord(duration, callback);
}
if (mCurrentSnackbar != null && cancelSnackbarLocked(mCurrentSnackbar,
Snackbar.Callback.DISMISS_EVENT_CONSECUTIVE)) {
// 如果当前Snackbar存在,取消显示当前Snackbar
return;
} else {
mCurrentSnackbar = null;
// 如果当前Snackbar不存在,显示NextSnackbar
showNextSnackbarLocked();
}
}
}
// 取消显示当前Snackbar,调用callback的dismiss(DISMISS_EVENT_CONSECUTIVE)方法
private boolean cancelSnackbarLocked(SnackbarRecord record, int event) {
final Callback callback = record.callback.get();
if (callback != null) {
// Make sure we remove any timeouts for the SnackbarRecord
mHandler.removeCallbacksAndMessages(record);
callback.dismiss(event);
return true;
}
return false;
}
private boolean isCurrentSnackbarLocked(Callback callback) {
return mCurrentSnackbar != null && mCurrentSnackbar.isSnackbar(callback);
}
private boolean isNextSnackbarLocked(Callback callback) {
return mNextSnackbar != null && mNextSnackbar.isSnackbar(callback);
}
// 更新超时提示
private void scheduleTimeoutLocked(SnackbarRecord r) {
if (r.duration == Snackbar.LENGTH_INDEFINITE) {
// If we're set to indefinite, we don't want to set a timeout
return;
}
int durationMs = LONG_DURATION_MS;
if (r.duration > 0) {
durationMs = r.duration;
} else if (r.duration == Snackbar.LENGTH_SHORT) {
durationMs = SHORT_DURATION_MS;
}
mHandler.removeCallbacksAndMessages(r);
mHandler.sendMessageDelayed(Message.obtain(mHandler, MSG_TIMEOUT, r), durationMs);
}
// 显示NextSnackbar,调用callback的show方法
private void showNextSnackbarLocked() {
if (mNextSnackbar != null) {
mCurrentSnackbar = mNextSnackbar;
mNextSnackbar = null;
final Callback callback = mCurrentSnackbar.callback.get();
if (callback != null) {
callback.show();
} else {
// The callback doesn't exist any more, clear out the Snackbar
mCurrentSnackbar = null;
}
}
}
Snackbar的showView()
会调用onViewShown()
,hideView()
会调用onViewHidden()
。
void onViewShown() {
SnackbarManager.getInstance().onShown(mManagerCallback);
}
void onViewHidden(int event) {
SnackbarManager.getInstance().onDismissed(mManagerCallback);
}
SnackbarManager的onShown
和onDismissed
方法。
public void onShown(Callback callback) {
synchronized (mLock) {
if (isCurrentSnackbarLocked(callback)) {
scheduleTimeoutLocked(mCurrentSnackbar);
}
}
}
public void onDismissed(Callback callback) {
synchronized (mLock) {
if (isCurrentSnackbarLocked(callback)) {
// If the callback is from a Snackbar currently show, remove it and show a new one
mCurrentSnackbar = null;
if (mNextSnackbar != null) {
showNextSnackbarLocked();
}
}
}
}