Skip to content

Commit 413a047

Browse files
committed
[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
1 parent 934985e commit 413a047

File tree

53 files changed

+101
-59
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+101
-59
lines changed

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

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import android.view.ViewParent;
5656
import android.view.accessibility.AccessibilityEvent;
5757
import android.view.accessibility.AccessibilityNodeInfo;
58+
import android.widget.CompoundButton;
5859
import androidx.annotation.AnimatorRes;
5960
import androidx.annotation.BoolRes;
6061
import androidx.annotation.CallSuper;
@@ -152,6 +153,7 @@ public class Chip extends AppCompatCheckBox
152153
@Nullable private RippleDrawable ripple;
153154

154155
@Nullable private OnClickListener onCloseIconClickListener;
156+
@Nullable private CompoundButton.OnCheckedChangeListener onCheckedChangeListener;
155157
@Nullable private MaterialCheckable.OnCheckedChangeListener<Chip> onCheckedChangeListenerInternal;
156158
private boolean deferredCheckedValue;
157159
private boolean closeIconPressed;
@@ -250,6 +252,16 @@ public Chip(Context context, AttributeSet attrs, int defStyleAttr) {
250252
setMinHeight(minTouchTargetSize);
251253
}
252254
lastLayoutDirection = ViewCompat.getLayoutDirection(this);
255+
256+
super.setOnCheckedChangeListener(
257+
(buttonView, isChecked) -> {
258+
if (onCheckedChangeListenerInternal != null) {
259+
onCheckedChangeListenerInternal.onCheckedChanged(Chip.this, isChecked);
260+
}
261+
if (onCheckedChangeListener != null) {
262+
onCheckedChangeListener.onCheckedChanged(buttonView, isChecked);
263+
}
264+
});
253265
}
254266

255267
@Override
@@ -706,17 +718,17 @@ public void setChecked(boolean checked) {
706718
// Defer the setChecked() call until after initialization.
707719
deferredCheckedValue = checked;
708720
} else if (chipDrawable.isCheckable()) {
709-
boolean wasChecked = isChecked();
710721
super.setChecked(checked);
711-
712-
if (wasChecked != checked) {
713-
if (onCheckedChangeListenerInternal != null) {
714-
onCheckedChangeListenerInternal.onCheckedChanged(this, checked);
715-
}
716-
}
717722
}
718723
}
719724

725+
@Override
726+
public void setOnCheckedChangeListener(
727+
@Nullable CompoundButton.OnCheckedChangeListener listener) {
728+
// Do not call super here - the wrapped listener set in the constructor will call the listener.
729+
onCheckedChangeListener = listener;
730+
}
731+
720732
/** Register a callback to be invoked when the close icon is clicked. */
721733
public void setOnCloseIconClickListener(OnClickListener listener) {
722734
this.onCloseIconClickListener = listener;

lib/javatests/com/google/android/material/appbar/MaterialToolbarTest.java

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

18-
import com.google.android.material.test.R;
18+
import com.google.android.material.R;
1919

2020
import static com.google.common.truth.Truth.assertThat;
2121

lib/javatests/com/google/android/material/badge/BadgeDrawableTest.java

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

18-
import com.google.android.material.test.R;
18+
import com.google.android.material.R;
1919

2020
import static com.google.common.truth.Truth.assertThat;
2121

lib/javatests/com/google/android/material/badge/BadgeUtilsTest.java

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

18-
import com.google.android.material.test.R;
18+
import com.google.android.material.R;
1919

2020
import static com.google.common.truth.Truth.assertThat;
2121

lib/javatests/com/google/android/material/bottomnavigation/BottomNavigationViewTest.java

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

18-
import com.google.android.material.test.R;
18+
import com.google.android.material.R;
1919

2020
import static com.google.common.truth.Truth.assertThat;
2121

lib/javatests/com/google/android/material/bottomsheet/BottomSheetBehaviorTest.java

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

18-
import com.google.android.material.test.R;
18+
import com.google.android.material.R;
1919

2020
import static com.google.common.truth.Truth.assertThat;
2121

lib/javatests/com/google/android/material/button/MaterialButtonTest.java

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

18-
import com.google.android.material.test.R;
18+
import com.google.android.material.R;
1919

2020
import static com.google.common.truth.Truth.assertThat;
2121

lib/javatests/com/google/android/material/button/MaterialButtonToggleGroupTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
package com.google.android.material.button;
1818

19-
import com.google.android.material.test.R;
19+
import com.google.android.material.R;
2020

2121
import static android.view.View.GONE;
2222
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;

lib/javatests/com/google/android/material/checkbox/MaterialCheckBoxTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
package com.google.android.material.checkbox;
1818

19-
import com.google.android.material.test.R;
19+
import com.google.android.material.R;
2020

2121
import static com.google.common.truth.Truth.assertThat;
2222

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

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

18-
import com.google.android.material.test.R;
18+
import com.google.android.material.R;
1919

20+
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
2021
import static com.google.common.truth.Truth.assertThat;
2122
import static org.junit.Assert.assertEquals;
2223
import static org.junit.Assert.assertTrue;
2324

2425
import android.content.Context;
2526
import androidx.appcompat.app.AppCompatActivity;
2627
import android.view.View;
28+
import android.widget.CompoundButton;
2729
import androidx.core.view.ViewCompat;
2830
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
2931
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.CollectionInfoCompat;
@@ -196,6 +198,29 @@ public void onCheckedChanged(ChipGroup group, List<Integer> checkedIds) {
196198
assertThat(checkedIds).contains(second.getId());
197199
}
198200

201+
@Test
202+
public void multipleSelection_chipListener() {
203+
chipgroup.setSingleSelection(false);
204+
205+
Chip first = (Chip) chipgroup.getChildAt(0);
206+
first.setOnCheckedChangeListener(this::onChipCheckedStateChanged);
207+
208+
Chip second = (Chip) chipgroup.getChildAt(1);
209+
second.setOnCheckedChangeListener(this::onChipCheckedStateChanged);
210+
211+
first.performClick();
212+
getInstrumentation().waitForIdleSync();
213+
214+
assertThat(checkedChangeCallCount).isEqualTo(1);
215+
assertThat(checkedIds).containsExactly(first.getId());
216+
217+
second.performClick();
218+
getInstrumentation().waitForIdleSync();
219+
220+
assertThat(checkedChangeCallCount).isEqualTo(2);
221+
assertThat(checkedIds).containsExactly(first.getId(), second.getId());
222+
}
223+
199224
@Test
200225
public void multiSelection_withSelectionRequired_unSelectsIfTwo() {
201226
chipgroup.setSingleSelection(false);
@@ -273,4 +298,9 @@ public void getChipAccessibilityClassName_singleChecked_radioButtonName() {
273298
Chip chip = (Chip) chipgroup.getChildAt(0);
274299
assertEquals("android.widget.RadioButton", chip.getAccessibilityClassName().toString());
275300
}
301+
302+
private void onChipCheckedStateChanged(CompoundButton chip, boolean checked) {
303+
checkedChangeCallCount++;
304+
checkedIds = chipgroup.getCheckedChipIds();
305+
}
276306
}

0 commit comments

Comments
 (0)