解决SnackBar无动画现象

2017-02-24 Mystery0 M 更多博文 » 博客 » GitHub »

Android

原文链接 https://mystery00.github.io/2017/02/24/%E8%A7%A3%E5%86%B3SnackBar%E6%97%A0%E5%8A%A8%E7%94%BB%E7%8E%B0%E8%B1%A1/
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。


发现问题

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

解决问题

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

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

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

将这个服务关闭之后,再次打开应用,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")
                    }
                }
    }
}

使用时只需要这样的一行代码即可搞定,当然,前提你需要使用我的这个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