是否有任何方法使Snackbar在活动变化中持续存在?

You*_*ode 23 android android-studio

虽然Snackbar很漂亮,但在改变活动时它并不存在.Snackbar在完成活动之前我想确认使用a发送消息的情况下,这是一个无聊的事情.我已经考虑在退出活动之前暂停代码,但发现这是一个不好的做法.

如果我所描述的是不可能的,是否有任何类型的材料设计敬酒消息?或者制作矩形吐司消息的方法; 一个半径较小的圆边?

use*_*087 24

要创建具有跨多个活动可见的应用程序上下文的Snackbar:

  1. 获取WindowManageras系统服务
  2. 创建并添加FrameLayout(rootView用型)WindowManager.LayoutParams.TYPE_TOAST,并WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODALWindowManager
  3. 等到(rootView)中FrameLayout.onAttachedToWindow()调用onFrameLayout
  4. 获取FrameLayout(rootView)的窗口标记View.getWindowToken()
  5. ContextThemeWrapper使用应用程序上下文和派生创建一个@style/Theme.AppCompat
  6. 使用新上下文创建一个额外的FrameLayout(snackbarContainer)
  7. 添加这个FrameLayout(snackbarContainer)类型WindowManager.LayoutParams.TYPE_APPLICATION_PANEL和标志WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
  8. 等到(snackbarContainer)中View.onAttachedToWindow()调用onFrameLayout
  9. 使用FrameLayout(snackbarContainer)正常创建Snackbar
  10. 设置View.onDismissed()回调到Snackbar并删除FrameLayouts(rootViewsnackbarContainer)
  11. 显示小吃吧 Snackbar.show()

这里有一个工作包装器(注意:轻扫以解除不起作用.也许其他人找到正确的WindowManager.LayoutParams标志来接收由CoordinatorLayout修复的触摸事件):

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑
一旦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();
Run Code Online (Sandbox Code Playgroud)

如果您没有主题,可以快速定义一个styles.xml:

<style name="FOL_Theme_SnackbarWrapper" parent="@style/Theme.AppCompat">
    <!--Insert customization here-->
</style>
Run Code Online (Sandbox Code Playgroud)

编辑
对于那些在Android Oreo上获得Bad Token Exception的人,请将TYPE_TOAST更改为TYPE_APPLICATION_OVERLAY.这是因为Android Oreo实现了绘制应用程序的特殊权限.您可以使用以下方式请求此权限:

    if(!Settings.canDrawOverlays(Activity.this){
        Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, URI.parse("package:" + getPackageName()));
        startActivityForResult(intent, REQ_CODE);
    }
Run Code Online (Sandbox Code Playgroud)

  • 我得到"android.view.WindowManager $ BadTokenException:无法添加窗口 - 令牌null无效;你的活动是否正在运行?" 它在Android Oreo中使用windowManager.addView的show方法,任何人都知道如何解决这个问题? (6认同)
  • 我赞成你的答案,因为它很好,但对我来说不好。我不想添加另一个打开设置的权限请求...这对我来说对用户界面来说真的很糟糕:/无论如何,这是一个好的答案 (2认同)

小智 7

如果我理解正确,你这样做:

  1. 活动A启动活动B以发送消息
  2. 发送消息后,将显示确认消息
  3. 你回到活动A.

您可以使用SnackBar通过使用ActivityResult(是一个StackOverflow帖子以及如何使用它)来实现这一点

以下是步骤:

  1. 活动A使用startActivityForResult启动活动B.
  2. 在活动B上做你的东西
  3. 设置结果(查看上面的链接以了解)
  4. 完成活动
  5. 在活动A中,在OnActivityResult中获取该代码并使用正确的消息显示您的SnackBar

这允许您在活动A中显示与活动B的结果相对应的Snackar.

希望它可以帮助你解决问题