/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.react.views.picker;
import javax.annotation.Nullable;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Spinner;
import com.facebook.react.common.annotations.VisibleForTesting;
public class ReactPicker extends Spinner {
private int mMode = MODE_DIALOG;
private @Nullable Integer mPrimaryColor;
private boolean mSuppressNextEvent;
private @Nullable OnSelectListener mOnSelectListener;
private @Nullable Integer mStagedSelection;
/**
* Listener interface for ReactPicker events.
*/
public interface OnSelectListener {
void onItemSelected(int position);
}
public ReactPicker(Context context) {
super(context);
}
public ReactPicker(Context context, int mode) {
super(context, mode);
mMode = mode;
}
public ReactPicker(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ReactPicker(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public ReactPicker(Context context, AttributeSet attrs, int defStyle, int mode) {
super(context, attrs, defStyle, mode);
mMode = mode;
}
private final Runnable measureAndLayout = new Runnable() {
@Override
public void run() {
measure(
MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY));
layout(getLeft(), getTop(), getRight(), getBottom());
}
};
@Override
public void requestLayout() {
super.requestLayout();
// The spinner relies on a measure + layout pass happening after it calls requestLayout().
// Without this, the widget never actually changes the selection and doesn't call the
// appropriate listeners. Since we override onLayout in our ViewGroups, a layout pass never
// happens after a call to requestLayout, so we simulate one here.
post(measureAndLayout);
}
public void setOnSelectListener(@Nullable OnSelectListener onSelectListener) {
if (getOnItemSelectedListener() == null) {
// onItemSelected gets fired immediately after layout because checkSelectionChanged() in
// AdapterView updates the selection position from the default INVALID_POSITION. To match iOS
// behavior, we don't want the event emitter for onItemSelected to fire right after layout.
mSuppressNextEvent = true;
setOnItemSelectedListener(
new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (!mSuppressNextEvent && mOnSelectListener != null) {
mOnSelectListener.onItemSelected(position);
}
mSuppressNextEvent = false;
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
if (!mSuppressNextEvent && mOnSelectListener != null) {
mOnSelectListener.onItemSelected(-1);
}
mSuppressNextEvent = false;
}
});
}
mOnSelectListener = onSelectListener;
}
@Nullable public OnSelectListener getOnSelectListener() {
return mOnSelectListener;
}
/**
* Will cache "selection" value locally and set it only once {@link #updateStagedSelection} is
* called
*/
public 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
*/
private void setSelectionWithSuppressEvent(int position) {
if (position != getSelectedItemPosition()) {
mSuppressNextEvent = true;
setSelection(position);
}
}
public @Nullable Integer getPrimaryColor() {
return mPrimaryColor;
}
public void setPrimaryColor(@Nullable Integer primaryColor) {
mPrimaryColor = primaryColor;
}
@VisibleForTesting
public int getMode() {
return mMode;
}
}