package carbon.widget; import android.annotation.TargetApi; import android.content.Context; import android.content.res.TypedArray; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.support.annotation.NonNull; import android.util.AttributeSet; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import java.util.Arrays; import carbon.Carbon; import carbon.R; import carbon.drawable.VectorDrawable; import carbon.internal.DropDownMenu; import carbon.recycler.ArrayAdapter; public class DropDown extends EditText { public enum Mode { Over, Fit } DropDownMenu dropDownMenu; private int selectedIndex; Adapter defaultAdapter; AdapterView.OnItemSelectedListener onItemSelectedListener; private boolean isShowingPopup = false; public DropDown(Context context) { super(context, null, R.attr.carbon_dropDownStyle); initSpinner(context, null, R.attr.carbon_dropDownStyle); } public DropDown(Context context, AttributeSet attrs) { super(context, attrs, R.attr.carbon_dropDownStyle); initSpinner(context, attrs, R.attr.carbon_dropDownStyle); } public DropDown(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initSpinner(context, attrs, defStyleAttr); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public DropDown(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); initSpinner(context, attrs, defStyleAttr); } private void initSpinner(Context context, AttributeSet attrs, int defStyleAttr) { VectorDrawable drawable = new VectorDrawable(getResources(), R.raw.carbon_dropdown); int size = (int) (Carbon.getDip(getContext()) * 24); drawable.setBounds(0, 0, size, size); setCompoundDrawables(null, null, drawable, null); TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.DropDown, defStyleAttr, R.style.carbon_DropDown); int theme = a.getResourceId(R.styleable.DropDown_carbon_popupTheme, -1); defaultAdapter = new Adapter(); dropDownMenu = new DropDownMenu(new ContextThemeWrapper(context, theme)); dropDownMenu.setAdapter(defaultAdapter); dropDownMenu.setOnItemClickedListener(onItemClickedListener); dropDownMenu.setOnDismissListener(() -> isShowingPopup = false); dropDownMenu.setMode(Mode.values()[a.getInt(R.styleable.DropDown_carbon_mode, Mode.Over.ordinal())]); setOnClickListener(view -> { dropDownMenu.show(DropDown.this); isShowingPopup = true; }); a.recycle(); } public Mode getMode() { return dropDownMenu.getMode(); } public void setMode(Mode mode) { dropDownMenu.setMode(mode); } public void setSelectedIndex(int index) { selectedIndex = index; if (getAdapter() != null) setText(getAdapter().getItem(index).toString()); } public int getSelectedIndex() { return selectedIndex; } public void setSelectedItem(Object item) { setSelectedIndex(Arrays.binarySearch(getAdapter().getItems(), item)); } public Object getSelectedItem() { return getAdapter().getItem(selectedIndex); } public void setAdapter(final RecyclerView.Adapter adapter) { if (adapter == null) { dropDownMenu.setAdapter(defaultAdapter); } else { dropDownMenu.setAdapter(adapter); } setText(getAdapter().getItem(selectedIndex).toString()); } public ArrayAdapter getAdapter() { return dropDownMenu.getAdapter(); } RecyclerView.OnItemClickedListener onItemClickedListener = new RecyclerView.OnItemClickedListener() { @Override public void onItemClicked(View view, Object item, int position) { setText(item.toString()); selectedIndex = position; if (onItemSelectedListener != null) onItemSelectedListener.onItemSelected(null, view, selectedIndex, 0); dropDownMenu.dismiss(); } }; public void setOnItemSelectedListener(AdapterView.OnItemSelectedListener onItemSelectedListener) { this.onItemSelectedListener = onItemSelectedListener; } public void setItems(Object[] items) { dropDownMenu.setAdapter(defaultAdapter); defaultAdapter.setItems(items); defaultAdapter.notifyDataSetChanged(); setSelectedIndex(0); } public static class Adapter extends ArrayAdapter<ViewHolder, Object> { @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { LayoutInflater inflater = LayoutInflater.from(parent.getContext()); View view = inflater.inflate(R.layout.carbon_popupmenu_item, parent, false); return new ViewHolder(view); } @Override public void onBindViewHolder(ViewHolder holder, final int position) { holder.tv.setText(items[position].toString()); holder.itemView.setOnClickListener(view -> fireOnItemClickedEvent(holder.itemView, holder.getAdapterPosition())); } } public static class ViewHolder extends RecyclerView.ViewHolder { TextView tv; public ViewHolder(View itemView) { super(itemView); tv = (TextView) itemView.findViewById(R.id.carbon_itemText); } } @Override protected boolean setFrame(int l, int t, int r, int b) { boolean result = super.setFrame(l, t, r, b); if (dropDownMenu != null) { carbon.widget.FrameLayout container = (FrameLayout) dropDownMenu.getContentView().findViewById(R.id.carbon_popupContainer); if (container.getAnimator() == null) dropDownMenu.update(); } return result; } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); if (isShowingPopup) dropDownMenu.showImmediate(DropDown.this); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); if (isShowingPopup) dropDownMenu.dismissImmediate(); } @Override public Parcelable onSaveInstanceState() { //begin boilerplate code that allows parent classes to save state Parcelable superState = super.onSaveInstanceState(); SavedState ss = new SavedState(superState); //end ss.isShowingPopup = this.isShowingPopup ? 1 : 0; return ss; } @Override public void onRestoreInstanceState(Parcelable state) { //begin boilerplate code so parent classes can restore state if (!(state instanceof SavedState)) { super.onRestoreInstanceState(state); return; } SavedState ss = (SavedState) state; super.onRestoreInstanceState(ss.getSuperState()); //end this.isShowingPopup = ss.isShowingPopup > 0; } static class SavedState implements Parcelable { public static final SavedState EMPTY_STATE = new SavedState() { }; int isShowingPopup; Parcelable superState; SavedState() { superState = null; } SavedState(Parcelable superState) { this.superState = superState != EMPTY_STATE ? superState : null; } private SavedState(Parcel in) { Parcelable superState = in.readParcelable(EditText.class.getClassLoader()); this.superState = superState != null ? superState : EMPTY_STATE; this.isShowingPopup = in.readInt(); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(@NonNull Parcel out, int flags) { out.writeParcelable(superState, flags); out.writeInt(this.isShowingPopup); } public Parcelable getSuperState() { return superState; } //required field that makes Parcelables from a Parcel public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() { public SavedState createFromParcel(Parcel in) { return new SavedState(in); } public SavedState[] newArray(int size) { return new SavedState[size]; } }; } }