diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPicker.java b/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPicker.java index 09498f3175d40f..a030a454908618 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPicker.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPicker.java @@ -13,6 +13,7 @@ import android.view.View; import android.widget.AdapterView; import android.widget.Spinner; +import android.widget.SpinnerAdapter; import com.facebook.react.common.annotations.VisibleForTesting; @@ -23,6 +24,7 @@ public class ReactPicker extends AppCompatSpinner { private int mMode = Spinner.MODE_DIALOG; private @Nullable Integer mPrimaryColor; private @Nullable OnSelectListener mOnSelectListener; + private @Nullable SpinnerAdapter mStagedAdapter; private @Nullable Integer mStagedSelection; private final OnItemSelectedListener mItemSelectedListener = new OnItemSelectedListener() { @@ -111,33 +113,42 @@ public void setOnSelectListener(@Nullable OnSelectListener onSelectListener) { return mOnSelectListener; } + /* package */ void setStagedAdapter(final SpinnerAdapter adapter) { + mStagedAdapter = adapter; + } + /** - * Will cache "selection" value locally and set it only once {@link #updateStagedSelection} is + * Will cache "selection" value locally and set it only once {@link #commitStagedData} is * called */ - public void setStagedSelection(int selection) { + /* package */ void setStagedSelection(int selection) { mStagedSelection = selection; } - public void updateStagedSelection() { - if (mStagedSelection != null) { - setSelectionWithSuppressEvent(mStagedSelection); - mStagedSelection = null; - } - } - /** - * Set the selection while suppressing the follow-up {@link OnSelectListener#onItemSelected(int)} - * event. This is used so we don't get an event when changing the selection ourselves. - * - * @param position the position of the selected item + * Used to commit staged data into ReactPicker view. + * During this period, we will disable {@link OnSelectListener#onItemSelected(int)} temporarily, + * so we don't get an event when changing the items/selection ourselves. */ - private void setSelectionWithSuppressEvent(int position) { - if (position != getSelectedItemPosition()) { - setOnItemSelectedListener(null); - setSelection(position, false); - setOnItemSelectedListener(mItemSelectedListener); + /* package */ void commitStagedData() { + setOnItemSelectedListener(null); + + final int origSelection = getSelectedItemPosition(); + if (mStagedAdapter != null && mStagedAdapter != getAdapter()) { + setAdapter(mStagedAdapter); + // After setAdapter(), Spinner will reset selection and cause unnecessary onValueChange event. + // Explicitly setup selection again to prevent this. + // Ref: https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/widget/AbsSpinner.java#123 + setSelection(origSelection, false); + mStagedAdapter = null; } + + if (mStagedSelection != null && mStagedSelection != origSelection) { + setSelection(mStagedSelection, false); + mStagedSelection = null; + } + + setOnItemSelectedListener(mItemSelectedListener); } public @Nullable Integer getPrimaryColor() { diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPickerManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPickerManager.java index db26da8e0a28e6..9e70d661f33c92 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPickerManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPickerManager.java @@ -44,9 +44,9 @@ public void setItems(ReactPicker view, @Nullable ReadableArray items) { } ReactPickerAdapter adapter = new ReactPickerAdapter(view.getContext(), data); adapter.setPrimaryTextColor(view.getPrimaryColor()); - view.setAdapter(adapter); + view.setStagedAdapter(adapter); } else { - view.setAdapter(null); + view.setStagedAdapter(null); } } @@ -77,7 +77,7 @@ public void setSelected(ReactPicker view, int selected) { @Override protected void onAfterUpdateTransaction(ReactPicker view) { super.onAfterUpdateTransaction(view); - view.updateStagedSelection(); + view.commitStagedData(); } @Override