关于手机设置高对比度文字对app的影响及解决方案

在安卓手机无障碍化设置中,有一项高对比度文案设置,旨在提高页面的可阅读性,就是让用户更容易看清屏幕上的文字。但是不同的手机对这个功能实现也不同,这就导致当打开这个功能的时候,反而会出现文字消失的现象。

打开此项设置后,所有的五颜六色的文字会全部根据颜色的深浅变成白色或黑色两种。有的手机除此之外,还会将所有的白色文字加一个黑色描边,所有的黑色文字加一个白色描边。这种处理就比较好,不会出现白色背景下文字也是白色情况。

问题出现在华为的部分手机,只会将字体颜色要么变成白色要么变成黑色,就会出现白色背景上浅灰色文字变成了白色文字,什么也看不见了。

解决这个问题的方式有两种方式。

第一种是优化UI设计方案,不要设计过于浅色的文字颜色,这就需要知道高对比度文字功能启用时,到底会将什么颜色变成白色,什么颜色变成黑色。

第二种方案,直接禁止 app 响应 高对比度文字 设置。从用户交互上来说这是不友好的,也是不合适的,毕竟启用这个功能的人肯定是阅读能力不够好的,本来无障碍化就是起到帮助作用,如果禁止了,太不地道了。但是如果真的要禁止,需确定实现方案是否可行。

方案一

结论:通过对比小米手机和华为手机发现,不同手机对黑白色值的分界点是不一致的。可以确定大概范围,无法准确给出分界点。

首先通过将字体颜色设置成纯黑白来测试,以下截图中,背景颜色为字体本来的颜色,字体颜色为开启高对比度文字时字体被修改后的颜色。

for (i in 16..255 step 1) {
    val v = TextView(this)
    v.layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)
    v.gravity = Gravity.CENTER
    val six1 = i.toString(16)
    val str = "#${six1}${six1}${six1}"
    v.text = str
    v.setTextColor(Color.parseColor(str))
    v.setBackgroundColor(Color.parseColor(str))

    parentLl.addView(v)
}

小米10s 11系统手机:分界点是#7F7F7F

华为手机 P40手机:分界点是#C5C5C5

修改代码,将字体颜色设置为红色,查看色值的分界点

for (i in 16..255 step 1) {
    val v = TextView(this)
    v.layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)
    v.gravity = Gravity.CENTER
    val six1 = i.toString(16)
    val str = "#FF${six1}${six1}"
    v.text = str
    v.setTextColor(Color.parseColor(str))
    v.setBackgroundColor(Color.parseColor(str))

    parentLl.addView(v)
}

小米10s 11系统手机:分界点是#FF4040

华为手机 P40手机:分界点是#FFA9A9

通过对比发现,小米手机颜色会被处理成白色的范围更大,但是它会加一个白色描边,不影响阅读,华为手机处理成白色的颜色区域较小,基本就是取色器的左上角一小部分,所以需要UI设计时避开这一部分就好。

我们可以大概给出UI的建议范围是:

1、灰色值参考点,不要给比 #C5C5C5 浅的灰色

2、无论什么字体颜色,避免取色器左上角大概红框处的色值

无法找出具体的黑白变化逻辑,通过对比也可以看到,不同的手机对高对比度的计算逻辑也是不同的,所以只是通过灰度和红色在两部手机上的变化来大概了解一下高对比度文字的基本原理。

方案二:

结论:逻辑上可行,但是被系统禁止了。

查询资料时发现一篇博客 安卓5.0新加辅助功能(高对比性文字/色彩校正/颜色反转)学习_默默9518的博客-CSDN博客_android 高对比度

根据其中的时序图,发现app对高对比度文字的处理是在红框处。

最终在 ViewRootImpl 找到关键逻辑

final AccessibilityManager mAccessibilityManager;

mHighContrastTextManager = new HighContrastTextManager();
mAccessibilityManager.addHighTextContrastStateChangeListener(
        mHighContrastTextManager, mHandler);

其中关键类是无障碍化的管理器 AccessibilityManager,在 app 中通过 getSystemService可以获取到,因此设想可以获取到这个对象,通过 addHighTextContrastStateChangeListener 方法添加一个空实现的监听器,进而实现 app 屏蔽调手机的 高对比度文字设置。

因为 addHighTextContrastStateChangeListener 方法被 @hide 注解,所以通过反射的方式调用:

val a: AccessibilityManager =
    getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
a.addAccessibilityStateChangeListener {
    Toast.makeText(this, "hello", Toast.LENGTH_SHORT)
    Log.i("xiaxiao", "hello aaaaa")
}
val classes = a.javaClass.declaredClasses
var highTextContrastChangeListenerClass: Class<*>? = null
classes.forEach {
    if (it.simpleName.contains("HighTextContrastChangeListener")) {
        highTextContrastChangeListenerClass = it
    }
}
val m: Method = a.javaClass.getMethod(
    "addHighTextContrastStateChangeListener",
    highTextContrastChangeListenerClass,
    Handler::class.java
)
m.invoke(a, null, Handler(mainLooper))

但是报 NoSuchMethodException 异常。

后来发现android9.0后对hide方法反射进行了限制,某些类和方法加载黑名单后,将无法反射到。参考:https://developer.android.com/guide/app-compatibility/restrictions-non-sdk-interfaces#determine-list

在表格中查找 addHighTextContrastStateChangeListener,果然发现被添加在了黑名单,无法反射,所以无法通过设置空监听器使app屏蔽高对比度文字功能了。

对高对比度文字的基本探究到此结束,希望能为你提供一个大致的了解。

猜你喜欢

转载自blog.csdn.net/xx23x/article/details/124317711