【问题标题】:Prevent Snackbar from dismissing on action click防止 Snackbar 在操作单击时关闭
【发布时间】:2017-02-27 10:24:50
【问题描述】:

如何防止 Android Snackbar 在 setAction onclick 上关闭,谢谢

Snackbar.make(rootlayout, "Hello SnackBar!", Snackbar.LENGTH_INDEFINITE)
   .setAction("Undo", new View.OnClickListener() {
       @Override
       public void onClick(View v) {
           // Snackbar should not dismiss
       }
   })
   .show();

【问题讨论】:

  • snackBar.show();在 onClick 内
  • @GaneshPokale,这行不通

标签: android android-layout android-view android-snackbar snackbar


【解决方案1】:

这里有一个更简洁的解决方案来实现这一点,它不需要反射。它基于已知 Snackbar 中按钮的视图 ID。 这适用于支持库的 27.1.1 版本,但如果视图 ID 发生更改,可能在未来的版本中不再适用!

首先,使用空的 OnClickListener 设置您的小吃吧操作:

snackbar.setAction("Save", new View.OnClickListener() {
    @Override
    public void onClick(View v) {}
});

然后,向小吃栏添加回调(在显示之前)。覆盖 onShown 函数,使用R.id.snackbar_action 找到按钮并将您自己的 OnClickListener 添加到其中。仅当手动调用snackbar.dismiss() 时,或者在小吃栏附加到 CoordinatorLayout 时滑动(如何禁用滑动是另一个 SO 问题),小吃栏才会被关闭。

snackbar.addCallback(new BaseTransientBottomBar.BaseCallback<Snackbar>() {
    @Override
    public void onShown(Snackbar transientBottomBar) {
        super.onShown(transientBottomBar);

        transientBottomBar.getView().findViewById(R.id.snackbar_action).setOnClickListener(new View.OnClickListener() {
            // your code here
        }

【讨论】:

    【解决方案2】:

    首先,根据设计Snackbar 在操作点击后不应停留在那里,这就是为什么它是不可配置的参数。

    深入研究代码,我可以找到足够的接缝,以便通过反射来做到这一点。

    public static void doNotHideSnackbar(Snackbar snackbar) throws NoSuchFieldException, NoSuchMethodException, IllegalAccessException {
        final Field sHandler = BaseTransientBottomBar.class.getDeclaredField("sHandler");
        sHandler.setAccessible(true);
        final Method handleMessage = Handler.class.getMethod("handleMessage", Message.class);
        final Handler originalHandler = (Handler) sHandler.get(snackbar);
        Handler decoratedHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
            @Override
            public boolean handleMessage(Message message) {
                switch (message.what) {
                    case 0:
                        try {
                            handleMessage.invoke(originalHandler, Message.obtain(originalHandler, 0));
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        } catch (InvocationTargetException e) {
                            e.printStackTrace();
                        }
                        return true;
                }
                return false;
            }
        });
        sHandler.set(snackbar, decoratedHandler);
    }
    

    这是经过测试的,适用于支持库版本25.3.1

    用法

    final Snackbar snackbar = Snackbar.make(root, "Hello SnackBar!", Snackbar.LENGTH_INDEFINITE).setAction("Undo", new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Toast.makeText(v.getContext(), "clicked", Toast.LENGTH_SHORT).show();
        }
    });
    
    snackbar.show();
    
    try {
        doNotHideSnackbar(snackbar);
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
    

    结果

    注意,这不是您应该坚持使用的解决方案,只要 API 可能会因版本而异。您最好考虑实现您的自定义 Snackbaralike 视图。但作为一种快速解决方法,您可以考虑使用此反射版本。

    【讨论】:

    • 我玩过这个,我发现了一些奇怪的东西...解除Snackbar部分阻止:既不调用dismiss(),也不设置持续时间(不是无限期的)有效,只能在屏幕上滑动(但在这种情况下也不会调用回调)!我还尝试在switch语句中实现处理并将另一种情况转发给原始处理程序(0:显示,1:关闭),没有结果接近原始。
    • 非常感谢,但接下来需要时如何隐藏它?
    【解决方案3】:

    迟到总比没有好 - 我就是这样做的。

    private fun showSnackbar() {
            if(snackbar == null) {
                //init snackbar
                snackbar = Snackbar.make(mainCoordinator, R.string.snackbar_no_network, Snackbar.LENGTH_INDEFINITE)
                        .setAction(R.string.snackbar_no_network_action) {
                            checkConnection()
                        } // action text on the right side
                        .setActionTextColor(ContextCompat.getColor(context, R.color.snack_green))
                //set background color
                snackbar!!.view.setBackgroundColor(ContextCompat.getColor(context, R.color.main_dark_gray))
            }
            //show
            snackbar!!.show()
        }
    
    private val handler = Handler()
    private fun checkConnection() {
        handler.postDelayed(checkConnectionRunnable, 500)
    }
    
    private val checkConnectionRunnable = Runnable {
        if (!NetworkUtil.isOnline(context)){
            showSnackbar()
        }
    }
    

    【讨论】:

    • 这将在 500 毫秒内隐藏和显示小吃栏,这不是 OP 要求的。
    猜你喜欢
    • 2019-10-07
    • 2015-07-07
    • 1970-01-01
    • 1970-01-01
    • 2016-02-10
    • 2012-11-24
    • 2018-02-20
    • 2012-04-04
    • 1970-01-01
    相关资源
    最近更新 更多