【问题标题】:Android 2-Way DataBinding With Custom View and Custom Attr具有自定义视图和自定义属性的 Android 2-Way DataBinding
【发布时间】:2016-08-02 00:00:39
【问题描述】:

我一直在为一个基本应用程序使用 2-way 数据绑定,它运行得很好,直到我开始使用自定义视图和属性。

我想创建一个自定义视图,有一个 TextView 和一个 EditText,并在另一个布局中使用它:

 <TextView
    android:text="Holder"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/tvTitle"
    android:layout_weight="1" />

<EditText
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:inputType="none"
    android:text="Name"
    android:ems="10"
    android:id="@+id/etAnwser"
    android:layout_weight="1" />

我有它的自定义属性

<resources>
<declare-styleable name="form_item">
    <attr name="tvTitle" format="string" />
    <attr name="anwserHint" format="string" />
    <attr name="anwserText" format="string" />
    <attr name="android:enabled" />
</declare-styleable>

在片段中我执行以下操作:

 <rhcloud.com.financialcontrol.tabutil.FormItem
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:enabled="@{state.get()}"
            form_item:anwserText='@={expense.description}'
            form_item:tvTitle="Description:" />

单向数据绑定很好用,但无论我更改文本,他都不会在课堂上向我发送回调

@InverseBindingMethods(value = {
        @InverseBindingMethod(type = FormItem.class, attribute = "anwserText"),
})
public class FormItem extends LinearLayout {

    private TextView tvTitle;
    private EditText etAnwser;

    public FormItem(@NonNull Context context) {
        super(context);
        LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        inflater.inflate(R.layout.form_item, this);

        tvTitle = (TextView) findViewById(R.id.tvTitle);
        etAnwser = (EditText) findViewById(R.id.etAnwser);
    }

    public FormItem(@NonNull Context context, @NonNull String title) {
        this(context);
        setTvTitle(title);
    }

    public FormItem(@NonNull Context context, @NonNull String title, @NonNull String hint) {
        this(context, title);
        setAnwserHint(hint);
    }

    public FormItem(@NonNull Context context, @NonNull String title, @NonNull String hint, @NonNull String anwserText) {
        this(context, title, hint);
        setAnwserHint(anwserText);
    }


    public FormItem(@NonNull Context context, @NonNull AttributeSet attrs) {
        super(context, attrs);

        LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        inflater.inflate(R.layout.form_item, this);

        tvTitle = (TextView) findViewById(R.id.tvTitle);
        etAnwser = (EditText) findViewById(R.id.etAnwser);

        TypedArray a = context.getTheme().obtainStyledAttributes(
                attrs,
                R.styleable.form_item,
                0, 0);

        try {
            setTvTitle(a.getString(R.styleable.form_item_tvTitle));
            setAnwserHint(a.getString(R.styleable.form_item_anwserHint));
            setAnwserText(a.getString(R.styleable.form_item_anwserText));
            String isEnabled = a.getString(R.styleable.form_item_android_enabled);
            if (isEnabled != null) {
                setEnable(Boolean.parseBoolean(isEnabled));
            }
        } finally {
            a.recycle();
        }
    }

    public void setTvTitle(String title) {
        tvTitle.setText(title);
    }

    public String getTvTitle() {
        return tvTitle.getText().toString();
    }

    public void setAnwserHint(String hint) {
        etAnwser.setHint(hint);
    }

    public String getAnwserHint() {
        return etAnwser.getHint().toString();
    }

    public void setEnable(boolean isEnable) {
        tvTitle.setEnabled(isEnable);
        etAnwser.setEnabled(isEnable);
    }

    public void setAnwserText(String anwserText) {
        etAnwser.setText(anwserText);
    }

    public String getAnwserText() {
        return etAnwser.getText().toString();
    }

    @InverseBindingAdapter(attribute = "form_item:anwserText")
    public static String setOnAnwserTextAttrChanged(final String value){

        Log.d("Test","Calling InverseBindingAdapter: " + value);
        return value;
    }


    @BindingAdapter(value = {"anwserTextAttrChanged"},
            requireAll = false)
    public static void setOnAnwserTextAttrChanged(final FormItem view,final InverseBindingListener anwserTextAttrChanged){

        Log.d("Test","Calling BindingAdapter: " + view.getAnwserText());


    if(anwserTextAttrChanged == null){

        }else{
        Log.d("Test","Calling here");
            anwserTextAttrChanged.onChange();

        }
    }

    @BindingAdapter(value = {"android:enabled"})
    public static void customEnable(FormItem formItem, boolean isEnable) {
        formItem.setEnable(isEnable);
    }
}

有人知道如何让它正常工作吗?

完整代码可以在here找到

【问题讨论】:

    标签: android android-layout android-databinding


    【解决方案1】:

    这对我有用:

    @InverseBindingMethods(value = {
            @InverseBindingMethod(type = FilterPositionView.class, attribute = "bind:filterStringValue", method = "getFilterValue", event = "android:filterStringValuetAttrChanged")
    })
    public class FilterPositionView extends LinearLayout {
        private FilterPositionBinding mBinding;
    
        public FilterPositionView(Context context) {
            super(context);
            init(context);
        }
    
        public FilterPositionView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init(context);
        }
    
        public FilterPositionView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init(context);
        }
    
        public FilterPositionView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
            super(context, attrs, defStyleAttr, defStyleRes);
            init(context);
        }
    
        private void init(Context context) {
            mBinding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.filter_position, this, true);
            setOrientation(HORIZONTAL);
    
            mBinding.filterPositionCheck.setOnCheckedChangeListener((buttonView, isChecked) -> {
                mBinding.filterPositionValue.setEnabled(isChecked);
                if (!isChecked) mBinding.filterPositionValue.setText("");
            });
        }
    
        /**
         * Zwraca wpisywany text
         *
         * @return wpisane litery tekstu
         */
        public String getFilterValue() {
            return mBinding.filterPositionValue.getText().toString();
        }
    
        @BindingAdapter(value = {"bind:filterTitle", "bind:filterStringValue", "bind:filterDateValue"}, requireAll = false)
        public static void setFilterBinding(FilterPositionView positionView, String filterTitle,
                                            String filterStringValue, Long filterDateValue) {
            positionView.mBinding.filterPositionTitle.setText(filterTitle);
            if (filterStringValue != null)
                positionView.mBinding.filterPositionValue.setText(filterStringValue);
            if (filterDateValue != null)
                positionView.mBinding.filterPositionValue.setText(DateTimeFormatUtil.format(filterDateValue));
        }
    
        @BindingAdapter(value = {"android:afterTextChanged", "android:filterStringValuetAttrChanged"}, requireAll = false)
        public static void setTextWatcher(FilterPositionView filterPositionView, final TextViewBindingAdapter.AfterTextChanged after,
                                          final InverseBindingListener textAttrChanged) {
            TextWatcher newValue = new TextWatcher() {
    
                @Override
                public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                }
    
                @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {
                }
    
                @Override
                public void afterTextChanged(Editable s) {
                    if (after != null) {
                        after.afterTextChanged(s);
                    }
                    if (textAttrChanged != null) {
                        textAttrChanged.onChange();
                    }
                }
            };
            TextWatcher oldValue = ListenerUtil.trackListener(filterPositionView.mBinding.filterPositionValue, newValue, R.id.textWatcher);
            if (oldValue != null) {
                filterPositionView.mBinding.filterPositionValue.removeTextChangedListener(oldValue);
            }
            filterPositionView.mBinding.filterPositionValue.addTextChangedListener(newValue);
        }
    }
    

    当然,您必须在 XML 布局中添加 @={},如下所示:

    <com.example.customviews.FilterPositionView
                    style="@style/verticalLabeledValueStyle"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    bind:filterTitle="@{@string/filter_product}"
                    bind:filterStringValue="@={sfmodel.product}"/>
    

    【讨论】:

    • 我有同样的问题,但不想让我的自定义视图对数据绑定有任何依赖。就像 textView 没有数据绑定,但你可以使用它的属性“text”作为双向数据绑定。所以我想准备一些东西,如果我的图书馆用户想将它与数据绑定一起使用,他们可以这样做。
    猜你喜欢
    • 2013-09-01
    • 1970-01-01
    • 2011-11-28
    • 1970-01-01
    • 1970-01-01
    • 2015-01-27
    • 2011-11-13
    • 1970-01-01
    相关资源
    最近更新 更多