项目场景:
根据工信部的要求,APP 需要做适老化改造,让长辈或者视障人士在使用 app 时能更加方便,体验更好,其中一个改造点就是用户开启了手机的 talkback 模式后,app 能否正确播报相关内容,给视障人士/长辈提供正确的反馈和使用引导。
问题描述:
改造过程中,有这样一个场景,用户开启了 talkback 模式,单击数字串的文本或者输入了数字的输入框,播报的内容不正确。
例如:
1、输入框,输入了验证码:123456,talkback 播报为:“十二万三千四百五十六”;
2、文本框,例如手机号:18925012345,银行卡号等,也都会播报成金额
这样的体验明显是不好的,播报内容反馈给用户也是不正确的。
解决方案:
一、TextView
针对 TextView 文本的,我的解决方案是重写了 TextView,给 TextView 的每个字符之间设置了空格,就可以实现 talkback 播报不把数字串当成金额播报,当然 TextView 也有设置字符边距的属性:
android:letterSpacing
这个我试过了没有效果,依旧会播报成金额,大概是因为它只是增加了字符之间的距离而不是在中间插入空格,没有实现隔断。
下面是我重写的 TextView 的代码,用法和 TextView 一样。
public class LetterSpacingTextView extends TextView {
private float spacing = Spacing.NORMAL;
private CharSequence originalText = "";
public LetterSpacingTextView(Context context) {
super(context);
}
public LetterSpacingTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public LetterSpacingTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/**
* 获取字间距
*
* @return
*/
public float getSpacing() {
return this.spacing;
}
/**
* 设置间距
*
* @param spacing
*/
public void setSpacing(float spacing) {
this.spacing = spacing;
applySpacing();
}
@Override
public void setText(CharSequence text, BufferType type) {
originalText = text;
applySpacing();
}
@Override
public CharSequence getText() {
return originalText;
}
/**
* 添加应用空间
*/
private void applySpacing() {
if (this == null || this.originalText == null) return;
StringBuilder builder = new StringBuilder();
for (int i = 0; i < originalText.length(); i++) {
builder.append(originalText.charAt(i));
if (i + 1 < originalText.length()) {
// \u00A0 不间断空格
// 追加空格
builder.append("\u00A0");
}
}
// 通过这个,去设置空格
SpannableString finalText = new SpannableString(builder.toString());
if (builder.toString().length() > 1) { // 如果当前TextView内容长度大于1,则进行空格添加
for (int i = 1; i < builder.toString().length(); i += 2) { // 小demo:100 1 0 0
// 按照x轴等比例进行缩放 通过我们设置的字间距+1除以10进行等比缩放
finalText.setSpan(new ScaleXSpan((spacing + 1) / 10), i, i + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
super.setText(finalText, TextView.BufferType.SPANNABLE);
}
public class Spacing {
public final static float NORMAL = 0;
}
}
二、EditText
针对 EditText,我的解决方案也是给数字串中间插入空格,不过没有重写 EditText,直接在 TextWatcher 的 onTextChange() 方法中执行插入空格。
这样写要解决一个点,就是当用户移动光标进行输入/删除操作时,计算好整个字符数组的下标位置。废话不多说,上代码:
private void editTextMatchTalkBack(CharSequence s, int start, int before, int count) {
if (s == null || s.length() == 0) return;
StringBuilder builder = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == ' ') {
continue;
} else {
//当做删除操作,并且删除的是空格时,它前面的字符不做 append,达到删除空格相当于删除字符的目的
if (before == 1 && count == 0 && start - 1 >= 0 && s.charAt(start - 1) != ' ' && i == start - 1) {
continue;
} else {
builder.append(s.charAt(i));
if (i != s.length() - 1) {
builder.append(' ');
}
}
}
}
if (builder.charAt(builder.length() - 1) == ' ') {
builder.deleteCharAt(builder.length() - 1);
}
//让 builder 和 s 变得相等,不再触发 onTextchange(), 达到最终字符串显示的目的
if (!builder.toString().equals(s.toString())) {
edt_identifying_code.setText(builder.toString());
//如果是输入
if (before == 0) {
if (start == s.length() - 1) {
edt_identifying_code.setSelection(builder.toString().length());
} else {
edt_identifying_code.setSelection(start + 2);
}
} else {
//如果是删除
if (start - 1 < 0) {
edt_identifying_code.setSelection(0);
} else {
edt_identifying_code.setSelection(start - 1);
}
}
}
}
这样基本上就解决了数字文本播报成金额的问题,方法是笨方法,如果大家有更好的方法也希望不吝赐教。