Mystery0の小站

Mystery0の小站

解决SnackBar无动画现象

2017-02-24

更新(2018年10月22日)

在新的 material-components-android 库中(版本号: 1.0.0-alpha3 ),已经修复了这个bug,support库中的Snackbar依旧存在这个问题,此篇文章仅供参考使用。感谢 Trumeet 的提醒。

以下为原文:

发现问题

突然间不知道从什么时候开始,手机上的app只要是调用了SnackBar的地方,SnackBar显示都没有效果,这两天突然开始察觉到这个问题了,开始各种google,但是都没有找到直接让SnackBar没有动画的结果(中文),甚至我还以为是SnackBar的源码改了。 但是又觉得不对,google的这个SnackBar新控件非常不错,不应该把动画去掉啊。 为此,我特意将design包的版本回退到22.2.0版本,依旧是没有动画。 这里开始我就觉得不对了,google不可能去掉并且旧版本依旧没有动画,那么应该是我的配置上的问题。 想到这里,我开始去查看CoordinatorLayout的用法,但是依旧没有任何卵用。

解决问题

突然间我想到了一个问题,会不会是国内还没有察觉到这个问题,但是国外有了呢,我是不相信全世界就我一个人有这个问题的。 抱着试一试的态度,我开始google snackbar no animation,果不其然,找到了许多和我相同的问题。

snackbar-no-animation-1

简单的查看了一下,有的人说是开发者选项中的动画缩放(?)的问题,但是我试了依旧错误,继续看才看到一个回答是辅助功能导致的。

snackbar-no-animation-2

查看我手机上的辅助功能选项,发现打开了一个服务——冰箱的后台服务。

snackbar-no-animation-3

将这个服务关闭之后,再次打开应用,SnackBar的动画显示出来了。

说在后面

继续查看了这个问题,但是没有看出什么来,只是提供了一个方法,使用AccessibilityManager.isEnabled()来判断辅助功能是否开启,但是我在代码中添加提示错误,AccessibilityManager是一个final类,这个类中使用了单例模式,一个static方法返回实例,但是我在调用这个静态方法的时候报错,提示找不到这个方法,查看源码AccessibilityManager类是公有的,静态方法也是公有的,但是报错,不知道为什么,如果有人知道为什么的请告诉我。

2017年8月3日更新:

时间过去了五个月,今天我总算是找到了解决方法。 前面我们已经说了SnackBar没有动画是因为开启了无障碍服务,在 SnackBar 的父类 BaseTransientBottomBar 中会检测无障碍服务的开启状况,偏偏 SnackBarfinal 类,看起来似乎有点无解,继承 SnackBar 重写是肯定不行的。 幸运的是,我在StackOverFlow上找到一个解决方法,参考链接。 这个方法是利用反射将 AccessibilityManager 类在app中调用的对象的 mIsEnabled 属性值改为 false ,这样就能通过 BaseTransientBottomBar 的检测,同时系统的无障碍服务并没有关闭,也不影响使用。

为了方便使用,我将这一段代码添加到了我自己的库中,这样我只需要直接在 Application 中初始化这样一句话就能够实现无视无障碍服务开启状况显示 SnackBar 的动画。以下是我的代码:

object ASnackBar
{
    private val TAG = "ASnackBar"

    @JvmStatic fun disableAccessibility(context: Context)
    {
        val contextThemeWrapper = ContextThemeWrapper(context, R.style.Theme_AppCompat)
        val view = LayoutInflater.from(contextThemeWrapper).inflate(R.layout.mystery0_snack_bar_coordinator_layout, null)
        Snackbar.make(view, "SnackBar", Snackbar.LENGTH_SHORT)
                .apply {
                    try
                    {
                        val mAccessibilityManagerField = BaseTransientBottomBar::class.java.getDeclaredField("mAccessibilityManager")
                        mAccessibilityManagerField.isAccessible = true
                        val accessibilityManager = mAccessibilityManagerField.get(this)
                        val mIsEnabledField = AccessibilityManager::class.java.getDeclaredField("mIsEnabled")
                        mIsEnabledField.isAccessible = true
                        mIsEnabledField.setBoolean(accessibilityManager, false)
                        mAccessibilityManagerField.set(this, accessibilityManager)
                    }
                    catch (e: Exception)
                    {
                        Logs.e(TAG, "disableAccessibility: $e")
                    }
                }
    }
}

2018年10月22日更新:

由于上面的代码在部分定制系统会出问题,并且已经不再更新ToolsDemo库了,所以删掉以下内容,上述代码仅供参考,直接用也行(如果不怕booooooooooooom的话~)

使用时只需要这样的一行代码即可搞定,当然,前提你需要使用我的这个ToolsDemo库。

ASnackBar.disableAccessibility(this);

参考列表

java - SnackBar appear animation - Stack Overflow

Issue 206416: Snackbar no longer slides onto screen

Snackbar and other animations stopped working on some Android devices