Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android 2-Way DataBinding With Custom View and Custom Attr

I've been using 2-way databinding for a basic application, it was going pretty well, until i start with custom views and attrs.

I want to create a custom view, with has a TextView and a EditText, and use it inside another layout:

 <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" />

And i have the custom attr for it

<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>

In the fragment i do the following:

 <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:" />

It works nice has 1-way databind, but whatever i change the text, he don't send me the callback in class

@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);
    }
}

Does anyone know how to make it work properly?

Fully code can be found at here

like image 245
Klaus Klein Avatar asked Aug 02 '16 00:08

Klaus Klein


1 Answers

This works for me:

@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);
    }
}

Of course You have to add @={} in your XML layouts like below:

<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}"/>
like image 84
Małgorzata Urban Avatar answered Sep 27 '22 16:09

Małgorzata Urban