8

如何防止 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();
4

3 回答 3

9

这是实现此目的的一种更清洁的解决方案,它不需要反射。它基于已知 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
        }
于 2018-07-02T13:42:06.103 回答
1

首先,按设计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 可能会因版本而异。您最好考虑实现您的自定义Snackbar相似视图。但作为一种快速解决方法,您可以考虑使用此反射版本。

于 2017-04-11T12:56:32.870 回答
-1

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

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()
    }
}
于 2017-04-11T11:58:07.337 回答