9

我在 android 中有一个组合视图,其中包含几个 textView 和一个 EditText。我为我的自定义视图定义了一个名为textand方法getText的属性。setText现在我想以绑定到内部编辑文本的方式为我的自定义视图添加 2 路数据绑定,因此如果我的数据得到更新,编辑文本也应该更新(现在可以使用),当我的编辑文本得到更新时,我的数据也应该更新。

我的绑定类看起来像这样

@InverseBindingMethods({
        @InverseBindingMethod(type = ErrorInputLayout.class, attribute = "text"),
})
public class ErrorInputBinding {
    @BindingAdapter(value = "text")
    public static void setListener(ErrorInputLayout errorInputLayout, final InverseBindingListener textAttrChanged) {
        if (textAttrChanged != null) {
            errorInputLayout.getInputET().addTextChangedListener(new TextWatcher() {
                @Override
                public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

                }

                @Override
                public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {

                }

                @Override
                public void afterTextChanged(Editable editable) {
                    textAttrChanged.onChange();
                }
            });
        }
    }
}

我试图用下面的代码绑定文本。userInfo是一个可观察的类。

            <ir.avalinejad.pasargadinsurance.component.ErrorInputLayout
                android:id="@+id/one_first_name"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:title="@string/first_name"
                app:text="@={vm.userInfo.firstName}"
                />

当我运行项目时出现此错误

错误:(20, 13) 在视图类型“ir.avalinejad.pasargadinsurance.component.ErrorInputLayout”上找不到事件“textAttrChanged”

我的自定义视图看起来像这样

public class ErrorInputLayout extends LinearLayoutCompat implements TextWatcher {
    protected EditText inputET;
    protected TextView errorTV;
    protected TextView titleTV;
    protected TextView descriptionTV;

    private int defaultGravity;

    private String title;
    private String description;
    private String hint;
    private int inputType = -1;
    private int lines;
    private String text;

    private Subject<Boolean> isValidObservable = PublishSubject.create();

    private Map<Validation, String> validationMap;

    public ErrorInputLayout(Context context) {
        super(context);
        init();
    }

    public ErrorInputLayout(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        readAttrs(attrs);
        init();
    }

    public ErrorInputLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        readAttrs(attrs);
        init();
    }

    private void readAttrs(AttributeSet attrs){
        TypedArray a = getContext().getTheme().obtainStyledAttributes(
                attrs,
                R.styleable.ErrorInputLayout,
                0, 0);

        try {
            title = a.getString(R.styleable.ErrorInputLayout_title);
            description = a.getString(R.styleable.ErrorInputLayout_description);
            hint = a.getString(R.styleable.ErrorInputLayout_hint);
            inputType = a.getInt(R.styleable.ErrorInputLayout_android_inputType, -1);
            lines = a.getInt(R.styleable.ErrorInputLayout_android_lines, 1);
            text = a.getString(R.styleable.ErrorInputLayout_text);

        } finally {
            a.recycle();
        }
    }


    private void init(){
        validationMap = new HashMap<>();
        setOrientation(VERTICAL);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();

        titleTV = (TextView) LayoutInflater.from(getContext()).inflate(R.layout.error_layout_default_title_textview, null, false);
        addView(titleTV);

        descriptionTV = (TextView) LayoutInflater.from(getContext()).inflate(R.layout.error_layout_default_description_textview, null, false);
        addView(descriptionTV);

        readInputFromLayout();

        if(inputET == null) {
            inputET = (EditText) LayoutInflater.from(getContext()).inflate(R.layout.error_layout_defult_edittext, this, false);
            addView(inputET);
        }

        errorTV = (TextView) LayoutInflater.from(getContext()).inflate(R.layout.error_layout_default_error_textview, null, false);
        addView(errorTV);

        inputET.addTextChangedListener(this);
        defaultGravity = inputET.getGravity();

        //set values
        titleTV.setText(title);

        if(description != null && !description.trim().isEmpty()){
            descriptionTV.setVisibility(VISIBLE);
            descriptionTV.setText(description);
        }

        if(inputType != -1)
            inputET.setInputType(inputType);

        if(hint != null)
            inputET.setHint(hint);

        else
            inputET.setHint(title);

        inputET.setLines(lines);

        inputET.setText(text);
    }

    private void readInputFromLayout() {
        if(getChildCount() > 3){
            throw new IllegalStateException("Only one or zero view is allow in layout");
        }

        if(getChildCount() == 3){
            View view = getChildAt(2);
            if(view instanceof EditText)
                inputET = (EditText) view;
            else
                throw new IllegalStateException("only EditText is allow as child view");
        }
    }

    public void setText(String text){
        inputET.setText(text);
    }

    public String getText() {
        return text;
    }

    public void addValidation(@NonNull Validation validation, @StringRes int errorResourceId){
        addValidation(validation, getContext().getString(errorResourceId));
    }

    public void addValidation(@NonNull Validation validation, @NonNull String error){
        if(!validationMap.containsKey(validation))
            validationMap.put(validation, error);
    }

    public void remoteValidation(@NonNull Validation validation){
        if(validationMap.containsKey(validation))
            validationMap.remove(validation);
    }

    public EditText getInputET() {
        return inputET;
    }

    public TextView getErrorTV() {
        return errorTV;
    }

    @Override
    public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

    }

    @Override
    public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {

    }

    @Override
    public void afterTextChanged(Editable editable) {
        checkValidity();

        if(editable.toString().length() == 0) //if hint
            inputET.setGravity(Gravity.RIGHT);
        else
            inputET.setGravity(defaultGravity);
    }

    public Subject<Boolean> getIsValidObservable() {
        return isValidObservable;
    }

    private void checkValidity(){
        //this function only shows the first matched error.
        errorTV.setVisibility(INVISIBLE);
        for(Validation validation: validationMap.keySet()){
            if(!validation.isValid(inputET.getText().toString())) {
                errorTV.setText(validationMap.get(validation));
                errorTV.setVisibility(VISIBLE);
                isValidObservable.onNext(false);
                return;
            }
        }

        isValidObservable.onNext(true);
    }
}
4

3 回答 3

15

经过数小时的调试,我找到了解决方案。我像这样改变了我的 Binding 类。

@InverseBindingMethods({
        @InverseBindingMethod(type = ErrorInputLayout.class, attribute = "text"),
})
public class ErrorInputBinding {
    @BindingAdapter(value = "textAttrChanged")
    public static void setListener(ErrorInputLayout errorInputLayout, final InverseBindingListener textAttrChanged) {
        if (textAttrChanged != null) {
            errorInputLayout.getInputET().addTextChangedListener(new TextWatcher() {
                @Override
                public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

                }

                @Override
                public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {

                }

                @Override
                public void afterTextChanged(Editable editable) {
                    textAttrChanged.onChange();
                }
            });
        }
    }

    @BindingAdapter("text")
    public static void setText(ErrorInputLayout view, String value) {
        if(value != null && !value.equals(view.getText()))
            view.setText(value);
    }

    @InverseBindingAdapter(attribute = "text")
    public static String getText(ErrorInputLayout errorInputLayout) {
        return errorInputLayout.getText();
    }

首先,我AttrChanged在这样的文本之后@BindingAdapter(value = "textAttrChanged")添加了监听器的默认名称,然后我也在此处添加了 getter 和 setter 方法。

于 2018-01-11T18:29:45.460 回答
2

event = "android:textAttrChanged"为我工作:

object DataBindingUtil {
    @BindingAdapter("emptyIfZeroText")        //replace "android:text" on EditText
    @JvmStatic
    fun setText(editText: EditText, text: String?) {
        if (text == "0" || text == "0.0") editText.setText("") else editText.setText(text)
    }

    @InverseBindingAdapter(attribute = "emptyIfZeroText", event = "android:textAttrChanged")
    @JvmStatic
    fun getText(editText: EditText): String {
        return editText.text.toString()
    }
}
于 2020-10-30T22:10:17.477 回答
1

您需要再添加一项功能

@BindingAdapter("app:textAttrChanged")
fun ErrorInputLayout.bindTextAttrChanged(listener: InverseBindingListener) {

}
于 2021-02-08T17:44:07.517 回答