Фон
Мне пришлось создать похожий на spinner-образ, который имеет такое поведение:
- textView, в котором есть выделенный текст элемента, со стрелкой справа, указывающей, находится ли он в состоянии "открыто".
- после щелчка по просмотру под ним появляется всплывающее меню (не сверху), показывающее список элементов на выбор, а также отмеченный выделенный элемент.
- вокруг popupWindow, есть полупрозрачный черный цвет.
- имеет настраиваемую анимацию для ее открытия и закрытия.
Проблема
Мне удалось сделать это представление (код ниже), но по какой-то причине, на Android 7.1, я получил всплывающее меню, которое появляется поверх представления (даже перекрывая его и просматривая над ним) вместо него, как это должно. Вот как это выглядит, и как это должно выглядеть:
Поскольку это довольно много кода и ресурсов, я поместил все это в репозиторий Github ( здесь), но здесь основная часть кода (мои попытки исправить это в комментариях):
FullSizePopupSpinner.java
public class FullSizePopupSpinner extends android.support.v7.widget.AppCompatTextView {
private static final long ANIMATION_DURATION = 150;
private int[] mItemsTextsResIds, mItemsIconsResIds;
private int mSelectedItemPosition = -1;
private SpinnerPopupWindow mPopupWindow;
private boolean mInitialized = false;
private OnItemSelectedListener mOnItemSelectedListener;
private Drawable mClosedDrawable;
private Drawable mOpenedDrawable;
public interface OnItemSelectedListener {
void onItemSelected(FullSizePopupSpinner parent, int position, String item, int previousSelectedPosition);
void onNothingSelected(FullSizePopupSpinner parent);
}
public FullSizePopupSpinner(final Context context) {
super(context);
init(context);
}
public FullSizePopupSpinner(final Context context, final AttributeSet attrs) {
super(context, attrs);
init(context);
}
public FullSizePopupSpinner(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.mItemsTextsResIds = mItemsTextsResIds;
ss.mItemsIconsResIds = mItemsIconsResIds;
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.mItemsTextsResIds, ss.mItemsIconsResIds);
setSelectedItemPosition(ss.mSelectedItemPosition);
}
public void setItems(final int[] itemsTextsResIds, final int[] itemsIconsResIds) {
mItemsTextsResIds = itemsTextsResIds;
mItemsIconsResIds = itemsIconsResIds;
if (mItemsTextsResIds != null && mSelectedItemPosition >= 0 && mSelectedItemPosition < mItemsTextsResIds.length)
setText(mItemsTextsResIds[mSelectedItemPosition]);
TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(this, null, null, isPopupShown() ? mOpenedDrawable : mClosedDrawable, null);
}
public boolean isPopupShown() {
return mPopupWindow != null && mPopupWindow.isShowing();
}
public int getSelectedItemPosition() {
return mSelectedItemPosition;
}
public void setSelectedItemPosition(final int selectedItemPosition) {
int lastSelectedItemPosition = mSelectedItemPosition;
mSelectedItemPosition = selectedItemPosition;
final String itemText = mItemsTextsResIds != null && mSelectedItemPosition >= 0 && mSelectedItemPosition < mItemsTextsResIds.length ?
getResources().getString(mItemsTextsResIds[mSelectedItemPosition]) : null;
setText(itemText);
TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(FullSizePopupSpinner.this, null, null, mClosedDrawable, null);
if (mOnItemSelectedListener != null)
mOnItemSelectedListener.onItemSelected(FullSizePopupSpinner.this, selectedItemPosition, itemText, lastSelectedItemPosition);
}
public void setOnItemSelectedListener(OnItemSelectedListener onItemSelectedListener) {
mOnItemSelectedListener = onItemSelectedListener;
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (mPopupWindow != null)
mPopupWindow.dismissRightAway();
}
protected void init(final Context context) {
if (mInitialized)
return;
mInitialized = true;
setSaveEnabled(true);
mClosedDrawable = ResourcesCompat.getDrawable(getResources(), R.drawable.drop_down_menu_ic_arrow_down, null);
mOpenedDrawable = ViewUtil.getRotateDrawable(mClosedDrawable, 180);
TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(FullSizePopupSpinner.this, null, null, mClosedDrawable, null);
setOnClickListener(new OnClickListener() {
@Override
public void onClick(final View v) {
if (mItemsTextsResIds == null)
return;
if (mPopupWindow != null)
mPopupWindow.dismissRightAway();
TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(FullSizePopupSpinner.this, null, null, mOpenedDrawable, null);
LayoutInflater layoutInflater = LayoutInflater.from(context);
final View popupView = layoutInflater.inflate(R.layout.spinner_drop_down_popup, null, false);
final LinearLayout linearLayout = (LinearLayout) popupView.findViewById(R.id.spinner_drop_down_popup__itemsContainer);
final View overlayView = popupView.findViewById(R.id.spinner_drop_down_popup__overlay);
linearLayout.setPivotY(0);
linearLayout.setScaleY(0);
linearLayout.animate().scaleY(1).setDuration(ANIMATION_DURATION).start();
mPopupWindow = new SpinnerPopupWindow(popupView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, true, overlayView, linearLayout);
mPopupWindow.setOutsideTouchable(true);
mPopupWindow.setTouchable(true);
mPopupWindow.setBackgroundDrawable(new ColorDrawable(0));
//PopupWindowCompat.setOverlapAnchor(mPopupWindow, false);
//if (VERSION.SDK_INT >= VERSION_CODES.M)
// mPopupWindow.setOverlapAnchor(false);
final AtomicBoolean isItemSelected = new AtomicBoolean(false);
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
popupView.findViewById(R.id.spinner_drop_down_popup__preLollipopShadow).setVisibility(View.GONE);
linearLayout.setBackgroundColor(0xFFffffff);
}
for (int i = 0; i < mItemsTextsResIds.length; ++i) {
final String itemText = getResources().getString(mItemsTextsResIds[i]);
final int position = i;
View itemView = layoutInflater.inflate(R.layout.spinner_drop_down_popup_item, linearLayout, false);
final TextView textView = (TextView) itemView.findViewById(android.R.id.text1);
textView.setText(itemText);
if (mItemsIconsResIds != null)
TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(textView, mItemsIconsResIds[position], 0,
position == mSelectedItemPosition ? R.drawable.drop_down_menu_ic_v : 0, 0);
else
TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(textView, 0, 0, position == mSelectedItemPosition ? R.drawable.drop_down_menu_ic_v : 0, 0);
linearLayout.addView(itemView, linearLayout.getChildCount() - 2);
itemView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(final View v) {
isItemSelected.set(true);
mPopupWindow.dismiss();
setSelectedItemPosition(position);
}
});
}
overlayView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(final View v) {
mPopupWindow.dismiss();
}
});
overlayView.setAlpha(0);
overlayView.animate().alpha(1).setDuration(ANIMATION_DURATION).start();
mPopupWindow.setOnDismissListener(new OnDismissListener() {
@Override
public void onDismiss() {
TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(FullSizePopupSpinner.this, null, null, mClosedDrawable, null);
if (!isItemSelected.get() && mOnItemSelectedListener != null)
mOnItemSelectedListener.onNothingSelected(FullSizePopupSpinner.this);
}
});
// optional: set animation style. look here for more info: http://stackoverflow.com/q/9648797/878126
mPopupWindow.setAnimationStyle(0);
//PopupWindowCompat.showAsDropDown(mPopupWindow, v, 0, 0, Gravity.TOP);
//mPopupWindow.showAsDropDown(v, 0, 0, Gravity.TOP);
mPopupWindow.showAsDropDown(v, 0, 0);
}
});
}
static class SpinnerPopupWindow extends PopupWindow {
private final View mOverlayView;
private final View mLayout;
public SpinnerPopupWindow(final View contentView, final int width, final int height, final boolean focusable, View overlayView, View layout) {
super(contentView, width, height, focusable);
mOverlayView = overlayView;
mLayout = layout;
}
public void dismissRightAway() {
super.dismiss();
}
@Override
public void dismiss() {
final ViewPropertyAnimator animator = mOverlayView.animate().alpha(0);
mLayout.setPivotY(0);
mLayout.animate().scaleY(0).setDuration(ANIMATION_DURATION);
ViewUtil.runOnAnimationEnd(animator, new Runnable() {
@Override
public void run() {
dismissRightAway();
}
});
animator.start();
}
}
//////////////////////////////////////
//SavedState//
//////////////
static class SavedState extends BaseSavedState {
private int[] mItemsTextsResIds;
private int mSelectedItemPosition = -1;
public int[] mItemsIconsResIds;
SavedState(Parcelable superState) {
super(superState);
}
private SavedState(@NonNull Parcel in) {
super(in);
this.mItemsTextsResIds = in.createIntArray();
mSelectedItemPosition = in.readInt();
mItemsIconsResIds = in.createIntArray();
}
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeIntArray(mItemsTextsResIds);
out.writeInt(mSelectedItemPosition);
out.writeIntArray(mItemsIconsResIds);
}
//required field that makes Parcelables from a Parcel
public static final Creator<SavedState> CREATOR =
new Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
}
Что я пробовал
Я попытался вызвать следующие функции (и комбинации), но никто не помог:
-
mPopupWindow.setOverlapAnchor(ложь);
-
PopupWindowCompat.setOverlapAnchor(mPopupWindow, ложь);
-
mPopupWindow.showAsDropDown(v, 0, 0, Gravity.BOTTOM);
-
PopupWindowCompat.showAsDropDown(mPopupWindow, v, 0, 0, Gravity.BOTTOM);
-
mPopupWindow.showAsDropDown(v, 0, 0, Gravity.TOP);
-
PopupWindowCompat.showAsDropDown(mPopupWindow, v, 0, 0, Gravity.TOP);
Вопрос
Почему всплывающее окно появляется поверх представления? Как я могу избежать этого и все еще иметь окно под представлением, как это было сделано раньше?
Возможно, есть ошибка на Android 7.1, которая вызывает такое поведение? Как я могу это преодолеть?