Skip to content

Commit 768d0cf

Browse files
drchenpekingme
authored andcommitted
[ChipGroup] Fix ChipGroup.getCheckedChipIds() returns wrong state
In the Chip implementation, onCheckedChangeListener was called before onCheckedChangeListenerInternal. This causes an issue that in onCheckedChangeListener's callback, the checkable group's checked state is not updated yet, therefore ChipGroup.getCheckedChipIds() will return the outdated checked state. Fixes this by overriding Chip.setOnCheckedChangeListener to get full control of the execution order between onCheckedChangeListener and onCheckedChangeListenerInternal. Resolves #2691 PiperOrigin-RevId: 449100861 (cherry picked from commit 413a047)
1 parent bc9595b commit 768d0cf

File tree

2 files changed

+64
-8
lines changed

2 files changed

+64
-8
lines changed

lib/java/com/google/android/material/chip/Chip.java

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
import android.view.ViewParent;
5555
import android.view.accessibility.AccessibilityEvent;
5656
import android.view.accessibility.AccessibilityNodeInfo;
57+
import android.widget.CompoundButton;
5758
import androidx.annotation.AnimatorRes;
5859
import androidx.annotation.BoolRes;
5960
import androidx.annotation.CallSuper;
@@ -151,6 +152,7 @@ public class Chip extends AppCompatCheckBox
151152
@Nullable private RippleDrawable ripple;
152153

153154
@Nullable private OnClickListener onCloseIconClickListener;
155+
@Nullable private CompoundButton.OnCheckedChangeListener onCheckedChangeListener;
154156
@Nullable private MaterialCheckable.OnCheckedChangeListener<Chip> onCheckedChangeListenerInternal;
155157
private boolean deferredCheckedValue;
156158
private boolean closeIconPressed;
@@ -251,6 +253,19 @@ public Chip(Context context, AttributeSet attrs, int defStyleAttr) {
251253
setMinHeight(minTouchTargetSize);
252254
}
253255
lastLayoutDirection = ViewCompat.getLayoutDirection(this);
256+
257+
super.setOnCheckedChangeListener(
258+
new CompoundButton.OnCheckedChangeListener() {
259+
@Override
260+
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
261+
if (onCheckedChangeListenerInternal != null) {
262+
onCheckedChangeListenerInternal.onCheckedChanged(Chip.this, isChecked);
263+
}
264+
if (onCheckedChangeListener != null) {
265+
onCheckedChangeListener.onCheckedChanged(buttonView, isChecked);
266+
}
267+
}
268+
});
254269
}
255270

256271
@Override
@@ -707,17 +722,17 @@ public void setChecked(boolean checked) {
707722
// Defer the setChecked() call until after initialization.
708723
deferredCheckedValue = checked;
709724
} else if (chipDrawable.isCheckable()) {
710-
boolean wasChecked = isChecked();
711725
super.setChecked(checked);
712-
713-
if (wasChecked != checked) {
714-
if (onCheckedChangeListenerInternal != null) {
715-
onCheckedChangeListenerInternal.onCheckedChanged(this, checked);
716-
}
717-
}
718726
}
719727
}
720728

729+
@Override
730+
public void setOnCheckedChangeListener(
731+
@Nullable CompoundButton.OnCheckedChangeListener listener) {
732+
// Do not call super here - the wrapped listener set in the constructor will call the listener.
733+
onCheckedChangeListener = listener;
734+
}
735+
721736
/** Register a callback to be invoked when the close icon is clicked. */
722737
public void setOnCloseIconClickListener(OnClickListener listener) {
723738
this.onCloseIconClickListener = listener;

lib/javatests/com/google/android/material/chip/ChipGroupTest.java

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,18 @@
1515
*/
1616
package com.google.android.material.chip;
1717

18-
import com.google.android.material.R;
18+
import android.widget.CompoundButton.OnCheckedChangeListener;
19+
import com.google.android.material.test.R;
1920

21+
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
2022
import static com.google.common.truth.Truth.assertThat;
2123
import static org.junit.Assert.assertEquals;
2224
import static org.junit.Assert.assertTrue;
2325

2426
import android.content.Context;
2527
import androidx.appcompat.app.AppCompatActivity;
2628
import android.view.View;
29+
import android.widget.CompoundButton;
2730
import androidx.core.view.ViewCompat;
2831
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
2932
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat;
@@ -196,6 +199,39 @@ public void onCheckedChanged(ChipGroup group, List<Integer> checkedIds) {
196199
assertThat(checkedIds).contains(second.getId());
197200
}
198201

202+
@Test
203+
public void multipleSelection_chipListener() {
204+
chipgroup.setSingleSelection(false);
205+
206+
Chip first = (Chip) chipgroup.getChildAt(0);
207+
first.setOnCheckedChangeListener(new OnCheckedChangeListener() {
208+
@Override
209+
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
210+
onChipCheckedStateChanged(buttonView, isChecked);
211+
}
212+
});
213+
214+
Chip second = (Chip) chipgroup.getChildAt(1);
215+
second.setOnCheckedChangeListener(new OnCheckedChangeListener() {
216+
@Override
217+
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
218+
onChipCheckedStateChanged(buttonView, isChecked);
219+
}
220+
});
221+
222+
first.performClick();
223+
getInstrumentation().waitForIdleSync();
224+
225+
assertThat(checkedChangeCallCount).isEqualTo(1);
226+
assertThat(checkedIds).containsExactly(first.getId());
227+
228+
second.performClick();
229+
getInstrumentation().waitForIdleSync();
230+
231+
assertThat(checkedChangeCallCount).isEqualTo(2);
232+
assertThat(checkedIds).containsExactly(first.getId(), second.getId());
233+
}
234+
199235
@Test
200236
public void multiSelection_withSelectionRequired_unSelectsIfTwo() {
201237
chipgroup.setSingleSelection(false);
@@ -260,4 +296,9 @@ public void isNotSingleLine_initializesAccessibilityNodeInfo() {
260296
assertEquals(1, itemInfo.getRowIndex());
261297
assertTrue(itemInfo.isSelected());
262298
}
299+
300+
private void onChipCheckedStateChanged(CompoundButton chip, boolean checked) {
301+
checkedChangeCallCount++;
302+
checkedIds = chipgroup.getCheckedChipIds();
303+
}
263304
}

0 commit comments

Comments
 (0)