数据绑定:InputMethodManager:startInputInner - mService.startInputOrWindowGainedFocus

Databinding : InputMethodManager: startInputInner - mService.startInputOrWindowGainedFocus

出于安全需要,我使用 setTextchar 数组 版本以避免使用 String 版本。

public final void setText (char[] text, 
                int start, 
                int len)

绑定适配器:

public class DataBindingAdapter {
    @BindingAdapter("android:text")
    public static void setCharArray(ClearableEditText view, char[] value) {
        if(value == null) return;
        Log.v("BindingAdapter", String.valueOf(value));
        view.setText(value, 0, value.length);
    }

    @InverseBindingAdapter(attribute = "android:text")
    public static char[] getArrayFromText(ClearableEditText view) {
        int length = view.getText().length();
        char[] password = new char[length];

        view.getText().getChars(0, length, password, 0);

        Log.v("BindingAdapter", String.valueOf(password));

        return password;
    }

}

模拟器行为:

但问题是当我在 EditText 中输入 ABCD 时,我得到了 DCBA 显示并且焦点始终位于EditText.

的开头

真实设备行为:

只显示第一个字符。

日志:

2020-02-04 17:37:43.760 4110-4110/com.bla V/InputMethodManager: Starting input: tba=com.bla ic=com.android.internal.widget.EditableInputConnection@3e6e221 mNaviBarColor -16750956 mIsGetNaviBarColorSuccess true , NavVisible : true , NavTrans : false 2020-02-04 17:37:43.760 4110-4110/com.bla D/InputMethodManager: startInputInner - Id : 0 2020-02-04 17:37:43.767 4110-4110/com.bla I/InputMethodManager: startInputInner - mService.startInputOrWindowGainedFocus

IMO CharArray 数据绑定很糟糕,因为当我将 CharArray 更改为 String 时没有问题,这意味着 equals 方法被生成的代码破坏了。

    @BindingAdapter("android:text")
    public static void setCharArray(EditText view, char[] value) {
        if(value == null) return;

        int length = view.length();
        char[] password = new char[length];
        view.getText().getChars(0, length, password, 0);

        if (Arrays.equals(value, password)) {
            Log.v("BindingAdapter out" + view.getId(), String.valueOf(value));
            view.setText(value, 0, value.length);
        }
    }

编辑 1:更好的解决方案

使用 custom class that implement CharSequence and hence the adapter version of setText 是基于 CharSequence 我这边不需要 BindingAdapter

class SecureString(value: CharArray?) : CharSequence {
    @Transient
    private val value: CharArray

    init {
        requireNotNull(value) { "Value must not be null" }
        this.value = value
    }

    override val length: Int
        get() = value.size

    override fun get(index: Int): Char = value[index]

    override fun subSequence(start: Int, end: Int): CharSequence {
        throw UnsupportedOperationException()
    }

    fun clear() {
        Arrays.fill(value, '\u0000')
    }

    fun toCharArray(): CharArray {
        return value
    }

}

InverseBindingAdapter调整为return SecureString:

    @InverseBindingAdapter(attribute = "android:text")
    public static SecureString getSecureString(EditText view) {
        int length = view.length();
        SecureString password = new SecureString(new char[length]);

        view.getText().getChars(0, length, password.toCharArray(), 0);

        return password;
    }