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
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.
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>