尽管Snackbar
很漂亮,但在更改活动时它不会持续存在。在完成活动之前,我想确认使用Snackbar
发送消息的情况下,这是一个无聊的事情。我已经考虑在退出活动之前暂停代码,但发现这是一个不好的做法。
如果我所描述的不可能,是否有任何类型的材料设计干杯消息?或者制作矩形吐司消息的方法;一个半径较小的圆边?
答案 0 :(得分:25)
使用在多个活动中可见的应用程序上下文创建Snackbar:
WindowManager
作为系统服务FrameLayout
和WindowManager.LayoutParams.TYPE_TOAST
类型添加到WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
WindowManager
( rootView )FrameLayout.onAttachedToWindow()
时
FrameLayout
FrameLayout
( rootView )的窗口令牌
View.getWindowToken()
ContextThemeWrapper
@style/Theme.AppCompat
( snackbarContainer )FrameLayout
类型并标记FrameLayout
WindowManager.LayoutParams.TYPE_APPLICATION_PANEL
( snackbarContainer )
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
( snackbarContainer )View.onAttachedToWindow()
时
FrameLayout
( snackbarContainer )正常创建Snackbar FrameLayout
回调设置为Snackbar并删除FrameLayouts( rootView 和 snackbarContainer )View.onDismissed()
这是一个工作包装(注意:滑动以解除不起作用。也许其他人找到正确的修正者CoordinatorLayout):Snackbar.show()
标志来接收触摸事件
WindowManager.LayoutParams
修改强>
定义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;
}
}
}
后,您可以像这样使用它:
SnackbarWrapper
如果您没有主题,可以在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();
中快速定义一个:
styles.xml
修改强>
对于那些在Android Oreo上获得Bad Token Exception的用户,请将TYPE_TOAST更改为TYPE_APPLICATION_OVERLAY。这是因为Android Oreo实现了绘制应用程序的特殊权限。您可以使用以下方式申请此权限:
<style name="FOL_Theme_SnackbarWrapper" parent="@style/Theme.AppCompat">
<!--Insert customization here-->
</style>
答案 1 :(得分:5)
如果我理解正确,你可以这样做:
你可以使用SnackBar来使用ActivityResult(here 是一个StackOverflow帖子,如何使用它)
以下是步骤:
这允许您在活动A中显示与活动B的结果相对应的Snackar。
希望它可以帮助你解决问题
答案 2 :(得分:1)
更新:查看所选答案。
我的问题的最佳解决方案是在显示Timer
之后使用Snackbar
,然后在计时器的run()
方法中启动活动。
Snackbar.show(); // Excluded make for brevity.
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
Intent chooseVideoIntent = new Intent(Intent.ACTION_GET_CONTENT); // Any type of content/file. Song, doc, video...
chooseVideoIntent.setType("video/*");
startActivityForResult(chooseVideoIntent, CHOOSE_VIDEO_REQUEST);
}
}, 2 * 1000);
更新:我发现使用findViewById(android.R.id.content)
作为Snackbar.make()
中的视图,Snackbar在片段更改中仍然存在。
答案 3 :(得分:1)
要使用矩形Toast
,请为Toast设置矩形背景,或者为Toast设置不同的背景颜色。
请将this post作为问题发布。但在你的情况下,这是一个可能的解决方案。
答案 4 :(得分:0)
如果有人需要在Xamarin中执行此操作,我已经调整了the accepted answer,我觉得这很有帮助。
using Android.Content;
using Android.Graphics;
using Android.OS;
using Android.Runtime;
using Android.Support.Design.Widget;
using Android.Views;
using Android.Widget;
using System;
public class SnackbarWrapper
{
private readonly string text;
private readonly int duration;
private readonly IWindowManager windowManager;
private readonly Context appplicationContext;
private Snackbar.Callback externalCallback;
private SnackbarAction action { get; set; }
public static SnackbarWrapper make(Context applicationContext, string text, int duration)
{
return new SnackbarWrapper(applicationContext, text, duration);
}
private SnackbarWrapper(Context appplicationContext, string text, int duration)
{
this.appplicationContext = appplicationContext;
var wm = appplicationContext.GetSystemService(Context.WindowService);
// We have to use JavaCast instead of a normal cast
this.windowManager = wm.JavaCast<IWindowManager>();
this.text = text;
this.duration = duration;
}
public void Show()
{
WindowManagerLayoutParams layoutParams = createDefaultLayoutParams(WindowManagerTypes.Toast, null);
var frameLayout = new FrameLayout(appplicationContext);
frameLayout.ViewAttachedToWindow += delegate
{
//this.onAttachedToWindow();
onRootViewAvailable(frameLayout);
};
windowManager.AddView(frameLayout, layoutParams);
}
private void onRootViewAvailable(FrameLayout rootView)
{
var ctw = new ContextThemeWrapper(appplicationContext, Resource.Style.Base_Theme_AppCompat);
CoordinatorLayout snackbarContainer = new CoordinatorLayout(ctw);
snackbarContainer.ViewAttachedToWindow += delegate
{
onSnackbarContainerAttached(rootView, snackbarContainer);
};
windowManager.AddView(snackbarContainer, createDefaultLayoutParams(WindowManagerTypes.ApplicationPanel, rootView.WindowToken));
}
private void onSnackbarContainerAttached(View rootView, CoordinatorLayout snackbarContainer)
{
Snackbar snackbar = Snackbar.Make(snackbarContainer, text, duration);
snackbar.SetCallback(new SnackbarCallbackImpl(rootView, snackbarContainer, windowManager));
if (action != null)
{
snackbar.SetAction(action.Text, action.Listener);
}
snackbar.Show();
}
private WindowManagerLayoutParams createDefaultLayoutParams(WindowManagerTypes type, IBinder windowToken)
{
WindowManagerLayoutParams layoutParams = new WindowManagerLayoutParams();
layoutParams.Format = Format.Translucent;
layoutParams.Width = ViewGroup.LayoutParams.MatchParent;
/* Si ponemos aqui WrapContent en alguna ocasion en la que haya un action largo y el texto tambien, el snackbar puede volverse como loco
* asi que usamos MatchParent. Aun asi sucede que a veces se puede mostrar en una linea o en dos el mismo texto, pero al menos no hace el temblor loco que de la otra forma*/
layoutParams.Height = ViewGroup.LayoutParams.MatchParent;
layoutParams.Gravity = GravityFlags.CenterHorizontal | GravityFlags.Bottom;
layoutParams.Flags = WindowManagerFlags.NotTouchModal;
layoutParams.Type = type;
layoutParams.Token = windowToken;
return layoutParams;
}
public SnackbarWrapper SetCallback(Snackbar.Callback callback)
{
this.externalCallback = callback;
return this;
}
public SnackbarWrapper SetAction(string text, Action<View> listener)
{
action = new SnackbarAction(text, listener);
return this;
}
}//class
internal class SnackbarAction
{
public string Text { get; set; }
public Action<View> Listener { get; set; }
public SnackbarAction(string text, Action<View> listener)
{
Text = text;
Listener = listener;
}
}
internal class SnackbarCallbackImpl : Snackbar.Callback
{
public Snackbar.Callback externalCallback { get; set; }
View rootView;
CoordinatorLayout snackbarContainer;
IWindowManager windowManager;
public SnackbarCallbackImpl(View rootView, CoordinatorLayout snackbarContainer, IWindowManager windowManager)
{
this.rootView = rootView;
this.snackbarContainer = snackbarContainer;
this.windowManager = windowManager;
}
public override void OnShown(Snackbar snackbar)
{
base.OnShown(snackbar);
externalCallback?.OnShown(snackbar);
}
public override void OnDismissed(Snackbar snackbar, int evt)
{
base.OnDismissed(snackbar, evt);
// Clean up (NOTE! This callback can be called multiple times)
if (snackbarContainer.Parent != null && rootView.Parent != null)
{
windowManager.RemoveView(snackbarContainer);
windowManager.RemoveView(rootView);
}
externalCallback?.OnDismissed(snackbar, evt);
}
}
答案 5 :(得分:0)
实际上,我只需要显示一条消息,并且不需要onClickListener。 如果您只需要显示一条消息,请查看此主题中的“ Myke Dev” 答案:
https://stackoverflow.com/a/34640942/9993413
(不要对我投赞成票,对写答案的“ Myke Dev”投反对票)
在 user1185087 回答中,您必须通过打开设置来向用户请求权限,这对我来说,对用户交互不是一件好事,但我认为这是您要展示的唯一方法带onClickListener的小吃店。
(也许您可以使用没有背景的活动来启动类似小吃店的对话框,但它不能仅用作window_alert快餐店)