Android Snackbar控件

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/chennai1101/article/details/82348617

相关文章
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的onShownonDismissed方法。

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();
            }
        }
    }
}

猜你喜欢

转载自blog.csdn.net/chennai1101/article/details/82348617