我正在维护一个“企业”Android 应用程序,该应用程序在工业环境中的腕戴式计算机上运行。当我们需要让用户知道一些中等重要的信息时,我们让设备发出声音并产生触觉反馈,并在屏幕中间的气泡中弹出一条简短的文本消息。弹出窗口会在几秒钟后自行消失。
这里的设计意图是,用户可以看到发生了什么,但不必伸出惯用手(可能装满了工具或产品)并弄乱设备按钮或屏幕来继续工作。
最初,这是使用
Toast
实现的,生活很美好。然后 Toast
被 Google 淘汰,基于 Snackbar
的新实现(用户无法区分)诞生了。 (出于“历史原因”,使用 C# 为 Xamarin.Android 编写代码,但这对于问题的目的来说应该不重要。)
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;
}
Resource.Layout.ErrorToast
来自哪里:
<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>
和
drawable/ErrorToast
看起来像:
<?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>
最终结果:文本位于屏幕中间,位于明亮欢快的黄色气泡中,因此:
唉,对于这个麻烦不断的世界来说,有用的 Android API 是永远不会长久的。因此,
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.'
请阅读通常的 Kübler-Ross 对无端 API 更改的悲痛阶段。
我以后应该用什么来取代这个?具有自定义布局的“最佳实践”吐司/小吃栏之类的弹出窗口是什么? (也许子类
AndroidX.Fragment.App.DialogFragment
?)
我想保留当前的外观(参见上面的屏幕截图)和行为:在屏幕上的任何内容的顶部弹出一段有限的时间,在特定的时间限制后消失(但可以提前关闭)。
当我的应用程序不在前台时,我不需要在屏幕上显示任何内容。
理想情况下,我还可以避免触摸应用程序中的每一个(很多很多)屏幕布局来实现这一目标。
通过反射,
Snackbar.SnackbarLayout
的类层次结构如下所示:
System.Object ->
Java.Lang.Object ->
Android.Views.View ->
Android.Views.ViewGroup ->
Android.Widget.FrameLayout ->
Google.Android.Material.Snackbar.Snackbar+SnackbarLayout
(至少现在是这样。记住,我们的 Google 霸主已经明确警告我们,如果与
Snackbar
类内部过于亲密,后果自负。)
所以,在原来的代码中,我们可以替换:
var snackbarLayout = (Snackbar.SnackbarLayout)snackbar.View;
与:
var snackbarLayout = (FrameLayout)snackbar.View;
并且它(至少目前)关闭警告并按预期工作。它没有解决根本问题:我们假设
Snackbar.View
是(或派生自)View
的特定子类。已发布的 API 不支持该假设,并且仅仅因为它现在是正确的并不能构成其将保持正确的证据。
在紧握的手上,我不清楚将
Snackbar.View
更改为不属于 FrameLayout
子类的任何内容有什么意义。
我想我准备将其归档在“当他们破坏它时我会担心的”(大且不断增长的)类别下,除非有更好的答案。