2

I'm maintaining an "enterprise" Android app that runs on wrist-mounted computers in an industrial environment. When we need to let the user know something of middling importance, we have the device make a noise and produce haptic feedback, and pop up a short text message in a bubble on the middle of the screen. The popup goes away on its own after a few seconds.

The design intent here is that the user can see what's up, but doesn't have to reach over with the dominant hand (which might be full of Tool or Product) and mess with the device buttons or screen to carry on working.

Originally, this was implemented using a Toast and life was good. Then Toast was Googléd into deprecation, and a new implementation (indistinguishable to the user) based on Snackbar was born. (Code in C# for Xamarin.Android for "historical reasons", but that should not matter for the purposes of the question.)

        public static Snackbar ShowErrorOverlay(Activity activity,
            int duration, int resID, params object[] args)
        {
            var coordinatorLayout = activity.Window.DecorView.RootView;
            var snackbar = Snackbar.Make(coordinatorLayout, string.Empty, Snackbar.LengthLong);
            var layout = activity.LayoutInflater.Inflate(Resource.Layout.ErrorToast,
                activity.FindViewById<ViewGroup>(Resource.Id.errortoast_layout_main), false);
            var txt = layout.FindViewById<TextView>(Resource.Id.errortoast_textview_message);
            txt.Text = string.Format(activity.Resources.GetString(resID), args);
            var snackbarLayout = (Snackbar.SnackbarLayout)snackbar.View;
            snackbarLayout.SetBackgroundColor(Color.Transparent);
            var opts = (FrameLayout.LayoutParams)snackbarLayout.LayoutParameters;
            opts.Gravity = GravityFlags.Center;
            snackbarLayout.LayoutParameters = opts;
            snackbarLayout.AddView(layout);
            snackbar.SetDuration(duration);
            snackbar.Show();
            return snackbar;
        }

where Resource.Layout.ErrorToast comes from:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/errortoast_layout_main"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:padding="8dp"
    android:background="@drawable/ErrorToast">
    <TextView
        android:id="@+id/errortoast_textview_message"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal|center_vertical"
        android:text="(error message)"
        android:paddingLeft="12dp"
        android:paddingRight="12dp"
        style="@style/TextAppearance"
        android:layout_centerInParent="true" />
</RelativeLayout>

and drawable/ErrorToast looks like:

<?xml version="1.0" encoding="utf-8" ?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
  <solid android:color="@color/errorBackground" />
  <stroke
      android:width="4dp"
      android:color="@color/errorBorder" />
  <padding
      android:bottom="5dp"
      android:left="5dp"
      android:right="5dp"
      android:top="5dp" />
  <corners android:radius="35dp" />
</shape>

End result: Text in the middle of the screen, in a bright cheerful yellow bubble, thus: bright cheerful yellow bubble with black text saying: This is the snackbar!

Alas, no useful Android API is ever long for this troubled world. Thus it is with Snackbar.SnackbarLayout:

Warning XAOBS001    'Snackbar.SnackbarLayout' is obsolete: 'While this type is 'public', Google considers it internal API and reserves the right to modify or delete it in the future. Use at your own risk.'

Take as read the usual Kübler-Ross stages of grief over gratuitous API changes.

With what am I supposed to replace this going forward? What is the "best practices" toast/snackbar-like popup thing with a custom layout? (Maybe subclass AndroidX.Fragment.App.DialogFragment?)

I want to keep the current appearance (see above screenshot) and behavior: pops up for a limited time over the top of whatever is on the screen, goes away after a specific time limit (but can be dismissed early).

I do not need to show anything on the screen when my application is not in the foreground.

Ideally, I'd also avoid having to touch every one of the (many, many) screen layouts in my app to achieve this.

1
  • I have nothing useful to add, but I'm gratified that StackOverflow is suggesting this as a "Related question": stackoverflow.com/questions/13375357 Proper use cases for Android UserManager.isUserAGoat()? Commented Apr 16 at 18:32

1 Answer 1

2

Via reflection, the class hierarchy for Snackbar.SnackbarLayout looks like this:

System.Object ->
Java.Lang.Object ->
Android.Views.View ->
Android.Views.ViewGroup ->
Android.Widget.FrameLayout ->
Google.Android.Material.Snackbar.Snackbar+SnackbarLayout

(At least for now. Remember, our Google overlords have explicitly warned us that one gets overly chummy with the Snackbar class internals at one's peril.)

So, in the original code, we can replace:

            var snackbarLayout = (Snackbar.SnackbarLayout)snackbar.View;

with:

            var snackbarLayout = (FrameLayout)snackbar.View;

and it (at least, for the moment) shuts up the warning and works as expected. It doesn't address the underlying problem: We're assuming that Snackbar.View is (or is derived from) a specific sub-subclass of View. That assumption is not supported by a published API, and just because it's true now does not constitute evidence it will stay true.

On the gripping hand, it's unclear to me how it would make sense to change Snackbar.View to anything that is not some subclass of FrameLayout.

I guess I'm prepared to file this under the (large and growing) category of "I'll worry about that when they break it.", barring some better answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.