mirror of
https://github.com/material-components/material-components-android.git
synced 2026-01-18 19:11:39 +08:00
652 lines
23 KiB
Java
652 lines
23 KiB
Java
/*
|
|
* Copyright (C) 2015 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.google.android.material.snackbar;
|
|
|
|
import com.google.android.material.R;
|
|
|
|
import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTROLS;
|
|
import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS;
|
|
import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_TEXT;
|
|
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
|
|
|
|
import android.content.Context;
|
|
import android.content.res.ColorStateList;
|
|
import android.content.res.TypedArray;
|
|
import android.graphics.PorterDuff;
|
|
import android.graphics.drawable.Drawable;
|
|
import android.os.Build.VERSION;
|
|
import android.os.Build.VERSION_CODES;
|
|
import android.text.TextUtils;
|
|
import android.util.AttributeSet;
|
|
import android.view.LayoutInflater;
|
|
import android.view.View;
|
|
import android.view.ViewGroup;
|
|
import android.view.ViewParent;
|
|
import android.view.accessibility.AccessibilityManager;
|
|
import android.widget.Button;
|
|
import android.widget.FrameLayout;
|
|
import android.widget.TextView;
|
|
import androidx.annotation.ColorInt;
|
|
import androidx.annotation.Dimension;
|
|
import androidx.annotation.DrawableRes;
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.Nullable;
|
|
import androidx.annotation.RestrictTo;
|
|
import androidx.annotation.StringRes;
|
|
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
|
import com.google.android.material.button.MaterialButton;
|
|
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
|
|
|
/**
|
|
* Snackbars provide lightweight feedback about an operation. They show a brief message at the
|
|
* bottom of the screen on mobile and lower left on larger devices. Snackbars appear above all other
|
|
* elements on screen and only one can be displayed at a time.
|
|
*
|
|
* <p>Snackbars can contain an action which is set via {@link #setAction(CharSequence,
|
|
* android.view.View.OnClickListener)}.
|
|
*
|
|
* <p>Snackbars automatically disappear after a timeout. They can also be dismissed by being swiped
|
|
* off screen, by action click, from a new snackbar being displayed, or manually via a call to
|
|
* {@link #dismiss()}.
|
|
*
|
|
* <p>To be notified when a snackbar has been shown or dismissed, you can provide a {@link Callback}
|
|
* via {@link BaseTransientBottomBar#addCallback(BaseCallback)}.
|
|
*
|
|
* <p>For more information, see the <a
|
|
* href="https://github.com/material-components/material-components-android/blob/master/docs/components/Snackbar.md">component
|
|
* developer guidance</a> and <a href="https://material.io/components/snackbar/overview">design
|
|
* guidelines</a>.
|
|
*/
|
|
public class Snackbar extends BaseTransientBottomBar<Snackbar> {
|
|
|
|
@Nullable private final AccessibilityManager accessibilityManager;
|
|
private boolean hasAction;
|
|
|
|
private static final int[] SNACKBAR_BUTTON_STYLE_ATTR = new int[] {R.attr.snackbarButtonStyle};
|
|
private static final int[] SNACKBAR_CONTENT_STYLE_ATTRS =
|
|
new int[] {R.attr.snackbarButtonStyle, R.attr.snackbarTextViewStyle};
|
|
|
|
/**
|
|
* Callback class for {@link Snackbar} instances.
|
|
*
|
|
* <p>Note: this class is here to provide backwards-compatible way for apps written before the
|
|
* existence of the base {@link BaseTransientBottomBar} class.
|
|
*
|
|
* @see BaseTransientBottomBar#addCallback(BaseCallback)
|
|
*/
|
|
public static class Callback extends BaseCallback<Snackbar> {
|
|
/** Indicates that the Snackbar was dismissed via a swipe. */
|
|
public static final int DISMISS_EVENT_SWIPE = BaseCallback.DISMISS_EVENT_SWIPE;
|
|
|
|
/** Indicates that the Snackbar was dismissed via an action click. */
|
|
public static final int DISMISS_EVENT_ACTION = BaseCallback.DISMISS_EVENT_ACTION;
|
|
|
|
/** Indicates that the Snackbar was dismissed via a timeout. */
|
|
public static final int DISMISS_EVENT_TIMEOUT = BaseCallback.DISMISS_EVENT_TIMEOUT;
|
|
|
|
/** Indicates that the Snackbar was dismissed via a call to {@link #dismiss()}. */
|
|
public static final int DISMISS_EVENT_MANUAL = BaseCallback.DISMISS_EVENT_MANUAL;
|
|
|
|
/** Indicates that the Snackbar was dismissed from a new Snackbar being shown. */
|
|
public static final int DISMISS_EVENT_CONSECUTIVE = BaseCallback.DISMISS_EVENT_CONSECUTIVE;
|
|
|
|
/**
|
|
* Called when the given {@link Snackbar} is visible.
|
|
*
|
|
* @param sb The snackbar which is now visible.
|
|
* @see Snackbar#show()
|
|
*/
|
|
@Override
|
|
public void onShown(Snackbar sb) {
|
|
// Stub implementation to make API check happy.
|
|
}
|
|
|
|
@Override
|
|
public void onDismissed(Snackbar transientBottomBar, @DismissEvent int event) {
|
|
// Stub implementation to make API check happy.
|
|
}
|
|
}
|
|
|
|
@Nullable private BaseCallback<Snackbar> callback;
|
|
|
|
private Snackbar(
|
|
@NonNull Context context,
|
|
@NonNull ViewGroup parent,
|
|
@NonNull View content,
|
|
@NonNull com.google.android.material.snackbar.ContentViewCallback contentViewCallback) {
|
|
super(context, parent, content, contentViewCallback);
|
|
accessibilityManager =
|
|
(AccessibilityManager) parent.getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
|
|
}
|
|
|
|
// TODO: Delete this once custom Robolectric shadows no longer depend on this method being present
|
|
// (and instead properly utilize BaseTransientBottomBar hierarchy).
|
|
@Override
|
|
public void show() {
|
|
super.show();
|
|
}
|
|
|
|
// TODO: Delete this once custom Robolectric shadows no longer depend on this method being present
|
|
// (and instead properly utilize BaseTransientBottomBar hierarchy).
|
|
@Override
|
|
public void dismiss() {
|
|
super.dismiss();
|
|
}
|
|
|
|
// TODO: Delete this once custom Robolectric shadows no longer depend on this method being present
|
|
// (and instead properly utilize BaseTransientBottomBar hierarchy).
|
|
@Override
|
|
public boolean isShown() {
|
|
return super.isShown();
|
|
}
|
|
|
|
/**
|
|
* Make a Snackbar to display a message
|
|
*
|
|
* <p>Snackbar will try and find a parent view to hold Snackbar's view from the value given to
|
|
* {@code view}. Snackbar will walk up the view tree trying to find a suitable parent, which is
|
|
* defined as a {@link CoordinatorLayout} or the window decor's content view, whichever comes
|
|
* first.
|
|
*
|
|
* <p>Having a {@link CoordinatorLayout} in your view hierarchy allows Snackbar to enable certain
|
|
* features, such as swipe-to-dismiss and automatically moving of widgets.
|
|
*
|
|
* @param view The view to find a parent from. This view is also used to find the anchor view when
|
|
* calling {@link Snackbar#setAnchorView(int)}.
|
|
* @param text The text to show. Can be formatted text.
|
|
* @param duration How long to display the message. Can be {@link #LENGTH_SHORT}, {@link
|
|
* #LENGTH_LONG}, {@link #LENGTH_INDEFINITE}, or a custom duration in milliseconds.
|
|
*/
|
|
@NonNull
|
|
public static Snackbar make(
|
|
@NonNull View view, @NonNull CharSequence text, @Duration int duration) {
|
|
return makeInternal(/* context= */ null, view, text, duration);
|
|
}
|
|
|
|
/**
|
|
* Make a Snackbar to display a message.
|
|
*
|
|
* <p>Snackbar will try and find a parent view to hold Snackbar's view from the value given to
|
|
* {@code view}. Snackbar will walk up the view tree trying to find a suitable parent, which is
|
|
* defined as a {@link CoordinatorLayout} or the window decor's content view, whichever comes
|
|
* first.
|
|
*
|
|
* <p>Having a {@link CoordinatorLayout} in your view hierarchy allows Snackbar to enable certain
|
|
* features, such as swipe-to-dismiss and automatically moving of widgets.
|
|
*
|
|
* @param view The view to find a parent from.
|
|
* @param resId The resource id of the string resource to use. Can be formatted text.
|
|
* @param duration How long to display the message. Can be {@link #LENGTH_SHORT}, {@link
|
|
* #LENGTH_LONG}, {@link #LENGTH_INDEFINITE}, or a custom duration in milliseconds.
|
|
*/
|
|
@NonNull
|
|
public static Snackbar make(@NonNull View view, @StringRes int resId, @Duration int duration) {
|
|
return make(view, view.getResources().getText(resId), duration);
|
|
}
|
|
|
|
/**
|
|
* Make a Snackbar to display a message
|
|
*
|
|
* <p>Snackbar will try and find a parent view to hold Snackbar's view from the value given to
|
|
* {@code view}. Snackbar will walk up the view tree trying to find a suitable parent, which is
|
|
* defined as a {@link CoordinatorLayout} or the window decor's content view, whichever comes
|
|
* first.
|
|
*
|
|
* <p>Having a {@link CoordinatorLayout} in your view hierarchy allows Snackbar to enable certain
|
|
* features, such as swipe-to-dismiss and automatically moving of widgets.
|
|
*
|
|
* @param context The context to use to create the Snackbar view.
|
|
* @param view The view to find a parent from. This view is also used to find the anchor view when
|
|
* calling {@link Snackbar#setAnchorView(int)}.
|
|
* @param text The text to show. Can be formatted text.
|
|
* @param duration How long to display the message. Can be {@link #LENGTH_SHORT}, {@link
|
|
* #LENGTH_LONG}, {@link #LENGTH_INDEFINITE}, or a custom duration in milliseconds.
|
|
*/
|
|
@NonNull
|
|
public static Snackbar make(
|
|
@NonNull Context context,
|
|
@NonNull View view,
|
|
@NonNull CharSequence text,
|
|
@Duration int duration) {
|
|
return makeInternal(context, view, text, duration);
|
|
}
|
|
|
|
/**
|
|
* Makes a Snackbar using the given context if non-null, otherwise uses the parent view context.
|
|
*/
|
|
@NonNull
|
|
private static Snackbar makeInternal(
|
|
@Nullable Context context,
|
|
@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.");
|
|
}
|
|
|
|
if (context == null) {
|
|
context = parent.getContext();
|
|
}
|
|
|
|
final LayoutInflater inflater = LayoutInflater.from(context);
|
|
final SnackbarContentLayout content =
|
|
(SnackbarContentLayout)
|
|
inflater.inflate(
|
|
hasSnackbarContentStyleAttrs(context)
|
|
? R.layout.mtrl_layout_snackbar_include
|
|
: R.layout.design_layout_snackbar_include,
|
|
parent,
|
|
false);
|
|
final Snackbar snackbar = new Snackbar(context, parent, content, content);
|
|
snackbar.setText(text);
|
|
snackbar.setDuration(duration);
|
|
return snackbar;
|
|
}
|
|
|
|
/**
|
|
* {@link Snackbar}s should still work with AppCompat themes, which don't specify a {@code
|
|
* snackbarButtonStyle}. This method helps to check if a valid {@code snackbarButtonStyle} is set
|
|
* within the current context, so that we know whether we can use the attribute.
|
|
*
|
|
* @deprecated This is for backward compatibility with AppCompat themes.
|
|
*/
|
|
@Deprecated
|
|
protected static boolean hasSnackbarButtonStyleAttr(@NonNull Context context) {
|
|
TypedArray a = context.obtainStyledAttributes(SNACKBAR_BUTTON_STYLE_ATTR);
|
|
int snackbarButtonStyleResId = a.getResourceId(0, -1);
|
|
a.recycle();
|
|
return snackbarButtonStyleResId != -1;
|
|
}
|
|
|
|
private static boolean hasSnackbarContentStyleAttrs(@NonNull Context context) {
|
|
TypedArray a = context.obtainStyledAttributes(SNACKBAR_CONTENT_STYLE_ATTRS);
|
|
int snackbarButtonStyleResId = a.getResourceId(0, -1);
|
|
int snackbarTextViewStyleResId = a.getResourceId(1, -1);
|
|
a.recycle();
|
|
return snackbarButtonStyleResId != -1 && snackbarTextViewStyleResId != -1;
|
|
}
|
|
|
|
@Nullable
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* Update the text in this {@link Snackbar}.
|
|
*
|
|
* @param message The new text for this {@link BaseTransientBottomBar}.
|
|
*/
|
|
@NonNull
|
|
@CanIgnoreReturnValue
|
|
public Snackbar setText(@NonNull CharSequence message) {
|
|
getMessageView().setText(message);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Update the text in this {@link Snackbar}.
|
|
*
|
|
* @param resId The new text for this {@link BaseTransientBottomBar}.
|
|
*/
|
|
@NonNull
|
|
@CanIgnoreReturnValue
|
|
public Snackbar setText(@StringRes int resId) {
|
|
return setText(getContext().getText(resId));
|
|
}
|
|
|
|
/**
|
|
* Set the action to be displayed in this {@link BaseTransientBottomBar}.
|
|
*
|
|
* @param resId String resource to display for the action
|
|
* @param listener callback to be invoked when the action is clicked
|
|
*/
|
|
@NonNull
|
|
@CanIgnoreReturnValue
|
|
public Snackbar setAction(@StringRes int resId, View.OnClickListener listener) {
|
|
return setAction(getContext().getText(resId), listener);
|
|
}
|
|
|
|
/**
|
|
* Set the action to be displayed in this {@link BaseTransientBottomBar}.
|
|
*
|
|
* @param text Text to display for the action
|
|
* @param listener callback to be invoked when the action is clicked
|
|
*/
|
|
@NonNull
|
|
@CanIgnoreReturnValue
|
|
public Snackbar setAction(
|
|
@Nullable CharSequence text, @Nullable final View.OnClickListener listener) {
|
|
final TextView tv = getActionView();
|
|
if (TextUtils.isEmpty(text) || listener == null) {
|
|
tv.setVisibility(View.GONE);
|
|
tv.setOnClickListener(null);
|
|
hasAction = false;
|
|
} else {
|
|
hasAction = true;
|
|
tv.setVisibility(View.VISIBLE);
|
|
tv.setText(text);
|
|
tv.setOnClickListener(
|
|
view -> {
|
|
listener.onClick(view);
|
|
// Now dismiss the Snackbar
|
|
dispatchDismiss(BaseCallback.DISMISS_EVENT_ACTION);
|
|
});
|
|
}
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Show an optional close icon at the end of the snackbar.
|
|
*
|
|
* <p>The close icon is set to {@code View.GONE} by default. Setting the icon to visible will show
|
|
* the icon at the end of the layout and automatically dismiss the snackbar when clicked. This
|
|
* method and other related closeIcon setters can only be used within the context of a Material 3
|
|
* or greater theme.
|
|
*
|
|
* <p>When using a close icon, please consider setting the snackbar's duration to {@code
|
|
* Snackbar.LENGTH_INDEFINITE}.
|
|
*
|
|
* <p>This method will automatically remove any end padding from the snackbar layout to properly
|
|
* align the close icon button. To manually set end padding, use {@link Snackbar#getView()} and
|
|
* call {@link View#setPaddingRelative(int, int, int, int)} before showing the snackbar.
|
|
*
|
|
* @param visible true to make the close icon visible
|
|
*/
|
|
@NonNull
|
|
@CanIgnoreReturnValue
|
|
public Snackbar setCloseIconVisible(boolean visible) {
|
|
final Button closeButton = getCloseViewOrThrow();
|
|
|
|
int newVisibility = visible ? View.VISIBLE : View.GONE;
|
|
closeButton.setVisibility(newVisibility);
|
|
closeButton.setOnClickListener(visible ? v -> dismiss() : null);
|
|
getSnackbarLayout().removeOrRestorePaddingEnd(/* remove= */ visible);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Set the tint color of the optional close icon.
|
|
*
|
|
* @param tint the tint color to be used for the close icon
|
|
*/
|
|
@NonNull
|
|
@CanIgnoreReturnValue
|
|
public Snackbar setCloseIconTint(@ColorInt int tint) {
|
|
return setCloseIconTintList(ColorStateList.valueOf(tint));
|
|
}
|
|
|
|
/**
|
|
* Set the tint list of the optional close icon.
|
|
*
|
|
* @param tintList the tint list to be used for the close icon
|
|
*/
|
|
@NonNull
|
|
@CanIgnoreReturnValue
|
|
public Snackbar setCloseIconTintList(@Nullable ColorStateList tintList) {
|
|
getCloseViewOrThrow().setIconTint(tintList);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Set the drawable used for the optional close icon.
|
|
*
|
|
* @param icon the drawable for the close icon
|
|
*/
|
|
@NonNull
|
|
@CanIgnoreReturnValue
|
|
public Snackbar setCloseIconDrawable(@Nullable Drawable icon) {
|
|
getCloseViewOrThrow().setIcon(icon);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Set the drawable resource used for the optional close icon.
|
|
*
|
|
* @param iconResourceId the drawable resource id for the close icon
|
|
*/
|
|
@NonNull
|
|
@CanIgnoreReturnValue
|
|
public Snackbar setCloseIconResource(@DrawableRes int iconResourceId) {
|
|
getCloseViewOrThrow().setIconResource(iconResourceId);
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
@Duration
|
|
public int getDuration() {
|
|
int userSetDuration = super.getDuration();
|
|
if (userSetDuration == LENGTH_INDEFINITE) {
|
|
return LENGTH_INDEFINITE;
|
|
}
|
|
|
|
if (VERSION.SDK_INT >= VERSION_CODES.Q) {
|
|
int controlsFlag = hasAction ? FLAG_CONTENT_CONTROLS : 0;
|
|
return accessibilityManager.getRecommendedTimeoutMillis(
|
|
userSetDuration, controlsFlag | FLAG_CONTENT_ICONS | FLAG_CONTENT_TEXT);
|
|
}
|
|
|
|
// If touch exploration is enabled override duration to give people chance to interact.
|
|
return hasAction && accessibilityManager.isTouchExplorationEnabled()
|
|
? LENGTH_INDEFINITE
|
|
: userSetDuration;
|
|
}
|
|
|
|
/**
|
|
* Sets the text color of the message specified in {@link #setText(CharSequence)} and {@link
|
|
* #setText(int)}.
|
|
*/
|
|
@NonNull
|
|
@CanIgnoreReturnValue
|
|
public Snackbar setTextColor(ColorStateList colors) {
|
|
getMessageView().setTextColor(colors);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the text color of the message specified in {@link #setText(CharSequence)} and {@link
|
|
* #setText(int)}.
|
|
*/
|
|
@NonNull
|
|
@CanIgnoreReturnValue
|
|
public Snackbar setTextColor(@ColorInt int color) {
|
|
getMessageView().setTextColor(color);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the max line count of the message specified in {@link #setText(CharSequence)} and {@link
|
|
* #setText(int)}.
|
|
*/
|
|
@NonNull
|
|
@CanIgnoreReturnValue
|
|
public Snackbar setTextMaxLines(int maxLines) {
|
|
getMessageView().setMaxLines(maxLines);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the text color of the action specified in {@link #setAction(CharSequence,
|
|
* View.OnClickListener)}.
|
|
*/
|
|
@NonNull
|
|
@CanIgnoreReturnValue
|
|
public Snackbar setActionTextColor(ColorStateList colors) {
|
|
getActionView().setTextColor(colors);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the max width of the action to be in the same line as the message. If the width is
|
|
* exceeded the action would go to the next line.
|
|
*/
|
|
@NonNull
|
|
@CanIgnoreReturnValue
|
|
public Snackbar setMaxInlineActionWidth(@Dimension int width) {
|
|
getContentLayout().setMaxInlineActionWidth(width);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the text color of the action specified in {@link #setAction(CharSequence,
|
|
* View.OnClickListener)}.
|
|
*/
|
|
@NonNull
|
|
@CanIgnoreReturnValue
|
|
public Snackbar setActionTextColor(@ColorInt int color) {
|
|
getActionView().setTextColor(color);
|
|
return this;
|
|
}
|
|
|
|
/** Sets the tint color of the background Drawable. */
|
|
@NonNull
|
|
@CanIgnoreReturnValue
|
|
public Snackbar setBackgroundTint(@ColorInt int color) {
|
|
return setBackgroundTintList(ColorStateList.valueOf(color));
|
|
}
|
|
|
|
/** Sets the tint color state list of the background Drawable. */
|
|
@NonNull
|
|
@CanIgnoreReturnValue
|
|
public Snackbar setBackgroundTintList(@Nullable ColorStateList colorStateList) {
|
|
view.setBackgroundTintList(colorStateList);
|
|
return this;
|
|
}
|
|
|
|
@NonNull
|
|
@CanIgnoreReturnValue
|
|
public Snackbar setBackgroundTintMode(@Nullable PorterDuff.Mode mode) {
|
|
view.setBackgroundTintMode(mode);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Set a callback to be called when this the visibility of this {@link Snackbar} changes. Note
|
|
* that this method is deprecated and you should use {@link #addCallback(BaseCallback)} to add a
|
|
* callback and {@link #removeCallback(BaseCallback)} to remove a registered callback.
|
|
*
|
|
* @param callback Callback to notify when transient bottom bar events occur.
|
|
* @deprecated Use {@link #addCallback(BaseCallback)}
|
|
* @see Callback
|
|
* @see #addCallback(BaseCallback)
|
|
* @see #removeCallback(BaseCallback)
|
|
*/
|
|
@Deprecated
|
|
@NonNull
|
|
@CanIgnoreReturnValue
|
|
public Snackbar setCallback(@Nullable Callback callback) {
|
|
// The logic in this method emulates what we had before support for multiple
|
|
// registered callbacks.
|
|
if (this.callback != null) {
|
|
removeCallback(this.callback);
|
|
}
|
|
if (callback != null) {
|
|
addCallback(callback);
|
|
}
|
|
// Update the deprecated field so that we can remove the passed callback the next
|
|
// time we're called
|
|
this.callback = callback;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* @hide Note: this class is here to provide backwards-compatible way for apps written before the
|
|
* existence of the base {@link BaseTransientBottomBar} class.
|
|
*/
|
|
@RestrictTo(LIBRARY_GROUP)
|
|
public static final class SnackbarLayout extends BaseTransientBottomBar.SnackbarBaseLayout {
|
|
public SnackbarLayout(Context context) {
|
|
super(context);
|
|
}
|
|
|
|
public SnackbarLayout(Context context, AttributeSet attrs) {
|
|
super(context, attrs);
|
|
}
|
|
|
|
@Override
|
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
|
// Work around our backwards-compatible refactoring of Snackbar and inner content
|
|
// being inflated against snackbar's parent (instead of against the snackbar itself).
|
|
// Every child that is width=MATCH_PARENT is remeasured again and given the full width
|
|
// minus the paddings.
|
|
int childCount = getChildCount();
|
|
int availableWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
|
|
for (int i = 0; i < childCount; i++) {
|
|
View child = getChildAt(i);
|
|
if (child.getLayoutParams().width == ViewGroup.LayoutParams.MATCH_PARENT) {
|
|
child.measure(
|
|
MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.EXACTLY),
|
|
MeasureSpec.makeMeasureSpec(child.getMeasuredHeight(), MeasureSpec.EXACTLY));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private TextView getMessageView() {
|
|
return getContentLayout().getMessageView();
|
|
}
|
|
|
|
private Button getActionView() {
|
|
return getContentLayout().getActionView();
|
|
}
|
|
|
|
private SnackbarContentLayout getContentLayout() {
|
|
return (SnackbarContentLayout) view.getChildAt(0);
|
|
}
|
|
|
|
@NonNull
|
|
private MaterialButton getCloseViewOrThrow() {
|
|
if (!(getContentLayout().getCloseView() instanceof MaterialButton)) {
|
|
throw new IllegalStateException(
|
|
"The layout of this snackbar does not include a close MaterialButton. This might be"
|
|
+ " because the context's theme is not a Material 3 or later theme or because the"
|
|
+ " Snackbar's layout has been replaced with a custom layout.");
|
|
}
|
|
return (MaterialButton) getContentLayout().getCloseView();
|
|
}
|
|
|
|
private SnackbarBaseLayout getSnackbarLayout() {
|
|
return view;
|
|
}
|
|
}
|