防止 Snackbar 在操作单击时关闭

Posted

技术标签:

【中文标题】防止 Snackbar 在操作单击时关闭【英文标题】:Prevent Snackbar from dismissing on action click 【发布时间】: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,这行不通 【参考方案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 要求的。

以上是关于防止 Snackbar 在操作单击时关闭的主要内容,如果未能解决你的问题,请参考以下文章

(android) 小吃吧结束后如何关闭活动?

使用 SnackBar 实例再次显示

Snackbar 在操作点击时不会被关闭

当用户执行任何操作时如何关闭 Snackbar?

MudBlazor:单击“确定”按钮时防止关闭 MudDialog

当我单击搜索按钮时,防止 ProgressDialog 被关闭(Android)