21

I am now trying to use android data-binding in my project, and encounter this kind of issue, for example: I have 3 checkbox as a checkbox group, if first checkbox is checked, then a variable type is 1. the second makes type to 2, the 3rd makes type to 3. so I implement the code in this way.

   // layout.xml


   <android.support.v7.widget.AppCompatCheckBox
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:checked="@{userInfoViewModel.type == 1}"
        android:onCheckedChanged="@{(compoundButton, checked) -> userInfoViewModel.onTypeChecked(checked, 1)}"
        />

   <android.support.v7.widget.AppCompatCheckBox
        android:layout_width="50dp"
        android:layout_height="55dp"
        android:checked="@{userInfoViewModel.type == 2}"
        android:onCheckedChanged="@{(compoundButton, checked) -> userInfoViewModel.onTypeChecked(checked, 2)}"
        />

    <android.support.v7.widget.AppCompatCheckBox
        android:layout_width="50dp"
        android:layout_height="55dp"
        android:checked="@{userInfoViewModel.type == 3}"
        android:onCheckedChanged="@{(compoundButton, checked) -> userInfoViewModel.onTypeChecked(checked, 3)}"
        />

// viewModel

public void onTypeChecked(boolean checked, int i) {
    if (checked) {
        // if it is a check. set the type
        type.set(i);
    } else {
        // if it is a uncheck. set type to unknown
        type.set(0);
    }
}

Now the problem is that, if I have checked 1st checkbox, then I check the 2nd. type should be set to 2, and the UI should update correctly. But the reality is that uncheck event also occur on the 1st checkbox, after type is set to 2, then type.set(0) is triggered, so no checkbox is checked.

In fact, this issue is same to onCheckedChanged called automatically. What I need is a solution for data-binding.

In non-data-binding project, I think the best solution is using setCheckedSilent(answer by @Emanuel Andrada).

  public void setCheckedSilent(boolean checked) {
    super.setOnCheckedChangeListener(null);
    super.setChecked(checked);
    super.setOnCheckedChangeListener(listener);
}

But in data-binding, I can not do this. So is there any expert can help me out?

According to @Arpan Sharma's answer, listen to onClick instead of onCheckedChanged. This solution works currently, But I am worried about the value of checked, is it always right?

public void onTypeChecked(View view, int i) {
    Boolean checked = ((CheckBox) view).isChecked();

    if (checked) {
        type.set(i);
    } else {
        type.set(0);
    }
}
Community
  • 1
  • 1
Leon
  • 1,935
  • 3
  • 23
  • 36

6 Answers6

15

Expose an ObservableBoolean from the ViewModel, then use two-way databinding over that boolean.

Then you can use the ObservableBoolean's values to decide what you want to do, rather than encode it in the XML.

android:checked="@={vm.checkedValue}"
EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428
8

I faced the same problem and i used onCLick listener instead onCHeck listener .That way the listener wont change the check state when it is set programatically. In your problem you should try setting different check change listeners to your check boxes.

Arpan Sharma
  • 2,142
  • 11
  • 22
  • 1
    So how can you distinct it is check or uncheck? get the current state when handle it? It should be able to work. BTW, Google is so stupid? No elegance solution? – Leon Mar 08 '17 at 09:02
  • I have tried your solution(check the update of the question), it works in my environment. But I am considering wether the state of the checkbox can always right. When the click callback is triggered, the checkbox's state is always updated? – Leon Mar 08 '17 at 09:19
  • yes the state of the check box will be changed automatically,Inside the listener you will have to check its state and then act accordingly. – Arpan Sharma Mar 08 '17 at 10:10
  • It will be check/uncheck on every click.Not to worry – Arpan Sharma Mar 08 '17 at 10:12
  • Yeah, the state should change every time, but what I am worried about is that: Does the state change always happen before onclick callback is invoked. – Leon Mar 08 '17 at 10:48
  • OK. Thank you, just wait for several days. If no better answer, I will choose you as answer. – Leon Mar 08 '17 at 10:58
8

This is very simple with data binding

In xml checkbox component

 <CheckBox
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onCheckedChanged="@{(compoundButton, checked) -> 
changeKeyboardVM.onCheckedChange(compoundButton, checked)}" />

In ViewModel or Activity

  public void onCheckedChange(CompoundButton button, boolean check) {
    Log.d("Z1D1", "onCheckedChange: " + check);
}

now Boolean check true on checked and false on unchecked

Atul
  • 1,477
  • 12
  • 9
7

I came across this question for first time and I think it's better to be implemented using binding adapter.

Here is the code for binding adapter

interface OnUserCheckedChangeListener {
    fun onUserCheckChange(view:CompoundButton, isChecked:Boolean)
}
@BindingAdapter("onUserCheckedChange")
fun setUserCheckedChangeListener(view:CompoundButton, listener: OnUserCheckedChangeListener?){
    if(listener == null){
        view.setOnClickListener(null)
    }else{
        view.setOnClickListener {
            listener.onUserCheckChange(view, view.isChecked)
        }
    }
}

And we can use it on any compound button

<CheckBox
            android:id="@+id/finish_check"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintStart_toStartOf="parent"   
            android:checked="@{your_condition}"
            onUserCheckedChange="@{(view, checked) -> vm.onItemChecked(todo, checked)}"
            />
Debanjan
  • 2,817
  • 2
  • 24
  • 43
  • How does it help? Is `onUserCheckedChange` called after `android:checked` set? Does it raise `onCheckedChanged` event when swipe? – CoolMind Jun 17 '21 at 10:50
1

Using onClick instead of onCheckedChanged to prevent 2-ways binding.

From item_order_view.xml:

<data>

    <variable
        name="viewModel"
        type="com.package.name.OrderItemViewModel" />
</data>

           <CheckBox
                android:id="@+id/cb_selected"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_marginLeft="8dp"
                android:buttonTint="@color/white"
                android:checked="@{viewModel.isSelect}"
                android:onClick="@{() -> viewModel.onClickedCheckBox()}"
                android:textColor="@color/white" />

From OrderItemViewModel.java

public class OrderItemViewModel {

    public final ObservableField<Boolean> isSelect;
    public final OrderItemViewModelListener mListener;
    private final Order mOrder;

    public OrderItemViewModel(Order order, OrderItemViewModelListener listener) {
        this.mListener = listener;
        this.mOrder = order;

        isSelect = new ObservableField<>(mOrder != null ? mOrder.isSelect() : false);
    }

    /**
     * Using onClick instead of onCheckedChanged
     * to prevent 2-ways binding issue.
     */
    public void onClickedCheckBox() {
        mListener.onCheckChanged();
    }

    public interface OrderItemViewModelListener {
        void onCheckChanged();
    } 
}
Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
Tri Ngo Minh
  • 145
  • 1
  • 9
0

See https://stackoverflow.com/a/52606437/2914140:

Write inside OnCheckedChangeListener:

if (button.isPressed()) {
    // A user pressed Switch.
}

Maybe some answers from How to use data binding for Switch onCheckedChageListener event? may help, for instance, defining android:onCheckedChanged listener, but I didnt test.

CoolMind
  • 26,736
  • 15
  • 188
  • 224