How to let the Spinner items appear below itself when being clicked and with full width, like on G+ app

后端 未结 1 734
误落风尘
误落风尘 2021-01-12 06:31

background

Google plus has a spinner-like view that shows a lot of items, but all of them appear below itself:

I need to mimic this in my own spin

相关标签:
1条回答
  • 2021-01-12 07:02

    OK, the solution for the Spinner of how to put the items below itself, is to just add this:

    <Spinner
    ...
    android:overlapAnchor="false" />
    

    Seems to work even on Kitkat, and not just on Lollipop, so my guess is that it should work on previous versions too.

    However, I still would like to know how it works on G+, and if there are tutorials/samples for whatever it is that I see there.

    I don't know, for example, how to make the window not appear like a window.

    I've tried this:

    android:dropDownWidth="match_parent"
    android:popupBackground="#FFffffff" 
    android:popupElevation="0px"
    

    but it doesn't help, as the right area is not covered by the spinner items.

    I also tried using a TextView that looks like a spinner, and create a PopupMenu for it, but I still get the same issues:

    PopupMenu popupMenu = new PopupMenu(getActivity(), v, Gravity.NO_GRAVITY, R.attr.popupMenuStyle, R.style.PopupMenuFullWidthStyle);
    
    <style name="PopupMenuFullWidthStyle" parent="@style/Widget.AppCompat.PopupMenu">
        <!--<item name="android:dropDownWidth">match_parent</item>-->
        <item name="android:popupBackground">#FFFFFFFF</item>
    </style>
    

    It doesn't do anything using the styles.


    Full solution

    Since it's quite hard to customize the PopupMenu the way that I was instructed, I've made a full solution for it. Here are the relevant parts of it:

    This will trigger showing the spinner's popup (and in fact act as a spinner) :

    <com.example.user.myapplication.FullSizeFakeSpinner
        android:id="@+id/spinner"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/spinner_selector"
        android:gravity="start|center_vertical"
        android:minHeight="44dp"
        android:paddingLeft="8dp"
        android:paddingRight="8dp"
        android:text="Fake Spinner"
        tools:ignore="UnusedAttribute"/>
    

    MainActivity.java     private static final String[] ITEMS = {"Item 0", "Item 1"};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        FullSizeFakeSpinner spinner = (FullSizeFakeSpinner) findViewById(R.id.spinner);
        spinner.setItems(ITEMS);
        }
    

    FullSizeFakeSpinner

    public class FullSizeFakeSpinner extends TextView {
        private String[] mItems;
        private int mSelectedItemPosition = -1;
        private PopupWindow mPopupWindow;
        private boolean mInitialized = false;
        private OnItemClickListener mOnItemSelectedListener;
    
        public interface OnItemClickListener {
            void onItemClick(FullSizeFakeSpinner parent, View clickedView, int position, String item);
    
            void onNothingSelected(FullSizeFakeSpinner parent);
        }
    
        public FullSizeFakeSpinner(final Context context) {
            super(context);
            init(context);
        }
    
        public FullSizeFakeSpinner(final Context context, final AttributeSet attrs) {
            super(context, attrs);
            init(context);
        }
    
        public FullSizeFakeSpinner(final Context context, final AttributeSet attrs, final int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init(context);
        }
    
        @Override
        public Parcelable onSaveInstanceState() {
            Parcelable superState = super.onSaveInstanceState();
            SavedState ss = new SavedState(superState);
            ss.mSelectedItemPosition = this.mSelectedItemPosition;
            ss.mItems = mItems;
            return ss;
        }
    
        @Override
        public void onRestoreInstanceState(Parcelable state) {
            if (!(state instanceof SavedState)) {
                super.onRestoreInstanceState(state);
                return;
            }
            SavedState ss = (SavedState) state;
            super.onRestoreInstanceState(ss.getSuperState());
            setItems(ss.mItems);
            setSelectedItemPosition(ss.mSelectedItemPosition);
        }
    
        public String[] getItems() {
            return mItems;
        }
    
        public void setItems(final String[] items) {
            mItems = items;
            if (mItems != null && mSelectedItemPosition >= 0 && mSelectedItemPosition < mItems.length)
                setText(mItems[mSelectedItemPosition]);
        }
    
        public int getSelectedItemPosition() {
            return mSelectedItemPosition;
        }
    
        public void setSelectedItemPosition(final int selectedItemPosition) {
            mSelectedItemPosition = selectedItemPosition;
            if (mItems != null && mSelectedItemPosition >= 0 && mSelectedItemPosition < mItems.length)
                setText(mItems[mSelectedItemPosition]);
        }
    
        public void setOnItemSelectedListener(OnItemClickListener onItemSelectedListener) {
            mOnItemSelectedListener = onItemSelectedListener;
        }
    
        @Override
        protected void onDetachedFromWindow() {
            super.onDetachedFromWindow();
            if (mPopupWindow != null)
                mPopupWindow.dismiss();
        }
    
    
        protected void init(final Context context) {
            if (mInitialized)
                return;
            mInitialized = true;
            setSaveEnabled(true);
            setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(final View v) {
                    if (mItems == null)
                        return;
                    LayoutInflater layoutInflater = LayoutInflater.from(context);
                    final View popupView = layoutInflater.inflate(R.layout.spinner_drop_down_popup, null, false);
                    final LinearLayout linearLayout = (LinearLayout) popupView.findViewById(android.R.id.list);
                    linearLayout.setOrientation(LinearLayout.VERTICAL);
                    mPopupWindow = new PopupWindow(popupView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
                    mPopupWindow.setOutsideTouchable(true);
                    mPopupWindow.setTouchable(true);
                    mPopupWindow.setBackgroundDrawable(new ColorDrawable(0));
                    mPopupWindow.setFocusable(true);
                    final AtomicBoolean isItemSelected = new AtomicBoolean(false);
                    for (int i = 0; i < mItems.length; ++i) {
                        final String item = mItems[i];
                        final int position = i;
                        View itemView = layoutInflater.inflate(android.R.layout.simple_list_item_1, linearLayout, false);
                        itemView.setBackgroundResource(R.drawable.listview_white_selector);
                        itemView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
                        ((TextView) itemView.findViewById(android.R.id.text1)).setText(item);
                        linearLayout.addView(itemView, linearLayout.getChildCount() - 1);
                        itemView.setOnClickListener(new OnClickListener() {
                            @Override
                            public void onClick(final View v) {
                                isItemSelected.set(true);
                                mPopupWindow.dismiss();
                                mSelectedItemPosition = position;
                                setText(item);
                                if (mOnItemSelectedListener != null)
                                    mOnItemSelectedListener.onItemClick(FullSizeFakeSpinner.this, v, position, item);
                            }
                        });
                    }
                    popupView.findViewById(android.R.id.empty).setOnClickListener(new OnClickListener() {
                        @Override
                        public void onClick(final View v) {
                            mPopupWindow.dismiss();
                        }
                    });
                    mPopupWindow.setOnDismissListener(new OnDismissListener() {
                        @Override
                        public void onDismiss() {
                            setViewBackgroundWithoutResettingPadding(FullSizeFakeSpinner.this, R.drawable.spinner_selector);
                            if (!isItemSelected.get() && mOnItemSelectedListener != null)
                                mOnItemSelectedListener.onNothingSelected(FullSizeFakeSpinner.this);
                        }
                    });
                    // optional: set animation style. look here for more info: http://stackoverflow.com/q/9648797/878126
                    mPopupWindow.showAsDropDown(v, 0, 0);
                    setViewBackgroundWithoutResettingPadding(FullSizeFakeSpinner.this, R.drawable.spinner_opened_selector);
                }
            });
    
        }
    
        public static void setViewBackgroundWithoutResettingPadding(final View v, final int backgroundResId) {
            final int paddingBottom = v.getPaddingBottom(), paddingLeft = v.getPaddingLeft();
            final int paddingRight = v.getPaddingRight(), paddingTop = v.getPaddingTop();
            v.setBackgroundResource(backgroundResId);
            v.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
        }
    
        //////////////////////////////////////
        //SavedState//
        //////////////
        static class SavedState extends BaseSavedState {
            private String[] mItems;
            private int mSelectedItemPosition = -1;
    
            SavedState(Parcelable superState) {
                super(superState);
            }
    
            private SavedState(Parcel in) {
                super(in);
                this.mItems = in.createStringArray();
                mSelectedItemPosition = in.readInt();
            }
    
            @Override
            public void writeToParcel(Parcel out, int flags) {
                super.writeToParcel(out, flags);
                out.writeStringArray(mItems);
                out.writeInt(mSelectedItemPosition);
            }
    
            //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];
                        }
                    };
        }
    
    }
    

    That's it about the code, but there are also some drawable resources too:

    There should be a "spinner" image file that's like "abc_spinner_mtrl_am_alpha" of the support library, and has the color that you wish to use. Not sure how to use tint for it on pre-Lollipop, so it's better to just create the file with the color that you use.

    colors:

    <color name="listview_pressed">#FFE2E2E2</color>
    <color name="listview_focused">#FF7dbcd3</color>
    <color name="listview_checked">#FFededed</color>
    

    listview_white_selector.xml :

    <?xml version="1.0" encoding="utf-8"?>
    <selector xmlns:android="http://schemas.android.com/apk/res/android">
    
        <item android:state_pressed="true"><shape>
            <solid android:color="@color/listview_pressed" />
        </shape></item>
        <item android:state_focused="true"><shape>
            <solid android:color="@color/listview_focused" />
        </shape></item>
        <item android:state_checked="true"><shape>
            <solid android:color="@color/listview_checked" />
        </shape></item>
        <item android:state_selected="true"><shape>
            <solid android:color="@color/listview_checked" />
        </shape></item>
        <item android:drawable="@android:color/white"/>
    
    </selector>
    

    listview_white_selector.xml v21 :

    <?xml version="1.0" encoding="utf-8"?>
    <ripple xmlns:android="http://schemas.android.com/apk/res/android"
            android:color="@color/listview_pressed">
    
        <item android:id="@android:id/mask">
            <color
                android:id="@android:id/mask"
                android:color="@color/listview_pressed"/>
        </item>
        <item android:drawable="@drawable/listview_ripple_white_background_selector"/>
    </ripple>
    

    spinner_selector.xml

    <?xml version="1.0" encoding="utf-8"?>
    <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
        <item>
            <selector xmlns:android="http://schemas.android.com/apk/res/android">
                <item android:state_pressed="true">
                    <shape>
                        <solid android:color="@color/listview_pressed"/>
                    </shape>
                </item>
                <item android:state_focused="true">
                    <shape>
                        <solid android:color="@color/listview_focused"/>
                    </shape>
                </item>
                <item android:state_checked="true">
                    <shape>
                        <solid android:color="@color/listview_checked"/>
                    </shape>
                </item>
                <item android:state_selected="true">
                    <shape>
                        <solid android:color="@color/listview_checked"/>
                    </shape>
                </item>
                <item android:drawable="@android:color/transparent"/>
            </selector>
        </item>
    
        <item android:drawable="@drawable/spinner"/>
    
    </layer-list>
    

    spinner_selector.xml (v21)

    <?xml version="1.0" encoding="utf-8"?>
    <ripple xmlns:android="http://schemas.android.com/apk/res/android"
            android:color="@color/listview_pressed">
        <item android:drawable="@drawable/spinner"/>
        <item android:id="@android:id/mask">
            <color
                android:id="@android:id/mask"
                android:color="@color/listview_pressed"/>
        </item>
        <item android:drawable="@drawable/listview_ripple_background_selector"/>
    </ripple>
    

    listview_ripple_white_background_selector:

    <?xml version="1.0" encoding="utf-8"?>
    <selector xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:state_checked="true"><shape>
            <solid android:color="@color/listview_checked" />
        </shape></item>
        <item android:state_selected="true"><shape>
            <solid android:color="@color/listview_checked" />
        </shape></item>
        <item android:drawable="@android:color/white"/>
    
    </selector>
    

    listview_ripple_background_selector:

    <?xml version="1.0" encoding="utf-8"?>
    <selector xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:state_checked="true"><shape>
                <solid android:color="@color/listview_checked" />
            </shape></item>
        <item android:state_selected="true"><shape>
                <solid android:color="@color/listview_checked" />
            </shape></item>
        <item android:drawable="@android:color/transparent"/>
    
    </selector>
    

    spinner_drop_down_popup

    <ScrollView
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fillViewport="true">
    
        <LinearLayout
            android:id="@android:id/list"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:orientation="vertical">
    
            <View
                android:id="@android:id/empty"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="#33000000"/>
        </LinearLayout>
    </ScrollView>
    
    0 讨论(0)
提交回复
热议问题