Although Snackbar
is beautiful, it doesn't persist when changing activities. This is a bummer in scenarios where I would like to confirm that a message was sent using a Snackbar
, before finishing the activity. I've considered pausing the code before exiting the activity, but have found that to be a bad practice.
If what I describe isn't possible, is there any type of material design toast message? Or a way to make a rectangular toast message; one with rounded edges of a smaller radius?
Snackbar in android is a new widget introduced with the Material Design library as a replacement of a Toast. Android Snackbar is light-weight widget and they are used to show messages in the bottom of the application with swiping enabled. Snackbar android widget may contain an optional action button.
Android Snackbar is a material design component introduced with API 22.2. 0. The functionality would resemble Android Toast, but unlike Toast, Snackbar could be dismissed by user or an action listener could be setup to respond to user actions.
A snackbar can be dismissed manually by calling the dismiss method on the MatSnackBarRef returned from the call to open . Only one snackbar can ever be opened at one time. If a new snackbar is opened while a previous message is still showing, the older message will be automatically dismissed.
To create a Snackbar with the application context which is visible across multiple activities:
WindowManager
as system serviceFrameLayout
(rootView) with type WindowManager.LayoutParams.TYPE_TOAST
and WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
to the WindowManager
FrameLayout.onAttachedToWindow()
is called in the FrameLayout
(rootView)FrameLayout
(rootView) with View.getWindowToken()
ContextThemeWrapper
with the application context and a derived @style/Theme.AppCompat
FrameLayout
(snackbarContainer)FrameLayout
(snackbarContainer) with type WindowManager.LayoutParams.TYPE_APPLICATION_PANEL
and flag WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
View.onAttachedToWindow()
is called in the FrameLayout
(snackbarContainer)FrameLayout
(snackbarContainer)View.onDismissed()
callback to the Snackbar and remove the FrameLayouts (rootView and snackbarContainer)Snackbar.show()
Here a working wrapper (NOTE: Swipe to dismiss is not working. Maybe some one else find the correct Fixed by CoordinatorLayout):WindowManager.LayoutParams
flags to receive touch events
public class SnackbarWrapper { private final CharSequence text; private final int duration; private final WindowManager windowManager; private final Context appplicationContext; @Nullable private Snackbar.Callback externalCallback; @Nullable private Action action; @NonNull public static SnackbarWrapper make(@NonNull Context applicationContext, @NonNull CharSequence text, @Snackbar.Duration int duration) { return new SnackbarWrapper(applicationContext, text, duration); } private SnackbarWrapper(@NonNull final Context appplicationContext, @NonNull CharSequence text, @Snackbar.Duration int duration) { this.appplicationContext = appplicationContext; this.windowManager = (WindowManager) appplicationContext.getSystemService(Context.WINDOW_SERVICE); this.text = text; this.duration = duration; } public void show() { WindowManager.LayoutParams layoutParams = createDefaultLayoutParams(WindowManager.LayoutParams.TYPE_TOAST, null); windowManager.addView(new FrameLayout(appplicationContext) { @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); onRootViewAvailable(this); } }, layoutParams); } private void onRootViewAvailable(final FrameLayout rootView) { final CoordinatorLayout snackbarContainer = new CoordinatorLayout(new ContextThemeWrapper(appplicationContext, R.style.FOL_Theme_SnackbarWrapper)) { @Override public void onAttachedToWindow() { super.onAttachedToWindow(); onSnackbarContainerAttached(rootView, this); } }; windowManager.addView(snackbarContainer, createDefaultLayoutParams(WindowManager.LayoutParams.TYPE_APPLICATION_PANEL, rootView.getWindowToken())); } private void onSnackbarContainerAttached(final View rootView, final CoordinatorLayout snackbarContainer) { Snackbar snackbar = Snackbar.make(snackbarContainer, text, duration); snackbar.setCallback(new Snackbar.Callback() { @Override public void onDismissed(Snackbar snackbar, int event) { super.onDismissed(snackbar, event); // Clean up (NOTE! This callback can be called multiple times) if (snackbarContainer.getParent() != null && rootView.getParent() != null) { windowManager.removeView(snackbarContainer); windowManager.removeView(rootView); } if (externalCallback != null) { externalCallback.onDismissed(snackbar, event); } } @Override public void onShown(Snackbar snackbar) { super.onShown(snackbar); if (externalCallback != null) { externalCallback.onShown(snackbar); } } }); if (action != null) { snackbar.setAction(action.text, action.listener); } snackbar.show(); } private WindowManager.LayoutParams createDefaultLayoutParams(int type, @Nullable IBinder windowToken) { WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(); layoutParams.format = PixelFormat.TRANSLUCENT; layoutParams.width = WindowManager.LayoutParams.MATCH_PARENT; layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT; layoutParams.gravity = GravityCompat.getAbsoluteGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM, ViewCompat.LAYOUT_DIRECTION_LTR); layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; layoutParams.type = type; layoutParams.token = windowToken; return layoutParams; } @NonNull public SnackbarWrapper setCallback(@Nullable Snackbar.Callback callback) { this.externalCallback = callback; return this; } @NonNull public SnackbarWrapper setAction(CharSequence text, final View.OnClickListener listener) { action = new Action(text, listener); return this; } private static class Action { private final CharSequence text; private final View.OnClickListener listener; public Action(CharSequence text, View.OnClickListener listener) { this.text = text; this.listener = listener; } } }
EDIT
Once SnackbarWrapper
is defined you can use it like this:
final SnackbarWrapper snackbarWrapper = SnackbarWrapper.make(getApplicationContext(), "Test snackbarWrapper", Snackbar.LENGTH_LONG); snackbarWrapper.setAction(R.string.snackbar_text, new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(getApplicationContext(), "Action", Toast.LENGTH_SHORT).show(); } }); snackbarWrapper.show();
If you don't have a theme, you can quickly define one in styles.xml
:
<style name="FOL_Theme_SnackbarWrapper" parent="@style/Theme.AppCompat"> <!--Insert customization here--> </style>
EDIT
For those on Android Oreo getting Bad Token Exception, change TYPE_TOAST to TYPE_APPLICATION_OVERLAY. This is due to Android Oreo implementing special permissions to draw over applications. You can ask for this permissions using:
if(!Settings.canDrawOverlays(Activity.this){ Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, URI.parse("package:" + getPackageName())); startActivityForResult(intent, REQ_CODE); }
If I understand correctly, you do this:
You can use SnackBar to do that by using an ActivityResult (here is a StackOverflow post with how to use it)
Here are the steps:
This allow you do display a Snackar in Activity A corresponding to result of Activity B.
Hopes it can helps your problem
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With