gpt4 book ai didi

android - 如何在 TextView 下方正确显示弹出菜单,类似于 Spinner?

转载 作者:IT老高 更新时间:2023-10-28 23:35:55 25 4
gpt4 key购买 nike

背景

我必须创建一个类似微调器的 View ,它具有以下行为:

  • 其中包含所选项目文本的textView,右侧有一个箭头图标,指示它是否处于“打开”状态。
  • 点击 View 后,会在其下方(不是顶部)出现一个弹出菜单,显示可供选择的项目列表,并标记所选项目。
  • 在popupWindow周围,有一个半透明的黑色。
  • 具有用于打开和关闭它的自定义动画。

问题

我已经成功地制作了这个 View (代码如下),但由于某种原因,在 Android 7.1 上,我让弹出菜单出现在 View 的顶部(甚至重叠它和它上面的 View ),而不是它下面,正如它应该。这是它的外观和它的外观:

enter image description here

由于代码和资源相当多,我已将其全部放在 Github 存储库 (here) 中,但这里是代码的主要部分(我的修复尝试在注释中):

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(false);

  • PopupWindowCompat.setOverlapAnchor(mPopupWindow,false);

  • 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);

问题

为什么弹出窗口会出现在 View 的顶部?我怎样才能避免这种情况,并且仍然像以前一样在 View 下方保留窗口?

Android 7.1 上是否存在导致此行为的错误?我该如何克服这个问题?

最佳答案

好的,我已经通过更改布局及其代码来修复它,但我仍然不明白为什么代码在 Android 7.1.1 上运行不佳,但在旧版本上运行良好。

这是当前代码(也更新了 github 存储库,可以找到有问题的原始代码 here):

ViewUtil.java

class ViewUtil {
static Drawable getRotateDrawable(final Drawable d, final int angle) {
return new LayerDrawable(new Drawable[]{d}) {
@Override
public void draw(final Canvas canvas) {
canvas.save();
canvas.rotate(angle, d.getBounds().width() / 2, d.getBounds().height() / 2);
super.draw(canvas);
canvas.restore();
}
};
}

@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
static ViewPropertyAnimator runOnAnimationEnd(final ViewPropertyAnimator animator, final Runnable runnable) {
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN)
animator.withEndAction(runnable);
else
animator.setListener(new android.animation.Animator.AnimatorListener() {

@Override
public void onAnimationStart(final android.animation.Animator animation) {
}

@Override
public void onAnimationRepeat(final android.animation.Animator animation) {
}

@Override
public void onAnimationEnd(final android.animation.Animator animation) {
animator.setListener(null);
runnable.run();
}

@Override
public void onAnimationCancel(final android.animation.Animator animation) {
}
});
return animator;
}
}

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);
final LayoutInflater layoutInflater = LayoutInflater.from(context);
final View popupView = layoutInflater.inflate(R.layout.spinner_drop_down_popup, null, false);
final RecyclerView recyclerView = (RecyclerView) popupView.findViewById(R.id.spinner_drop_down_popup__recyclerView);
final View overlayView = popupView.findViewById(R.id.spinner_drop_down_popup__overlay);
final View itemsContainer = popupView.findViewById(R.id.spinner_drop_down_popup__itemsContainer);
itemsContainer.setPivotY(0);
itemsContainer.setScaleY(0);
itemsContainer.animate().scaleY(1).setDuration(ANIMATION_DURATION).start();
mPopupWindow = new SpinnerPopupWindow(popupView, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, true, overlayView,
itemsContainer);
mPopupWindow.setOutsideTouchable(true);
mPopupWindow.setTouchable(true);
mPopupWindow.setBackgroundDrawable(new ColorDrawable(0));
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);
recyclerView.setBackgroundColor(0xFFffffff);
}
recyclerView.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false));
recyclerView.setAdapter(new Adapter() {
@Override
public ViewHolder onCreateViewHolder(final ViewGroup parent, final int viewType) {
final View itemView = layoutInflater.inflate(R.layout.spinner_drop_down_popup_item, recyclerView, false);
final ViewHolder holder = new ViewHolder(itemView) {
};
itemView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(final View v) {
isItemSelected.set(true);
mPopupWindow.dismiss();
setSelectedItemPosition(holder.getAdapterPosition());
}
});
return holder;
}

@Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
final String itemText = getResources().getString(mItemsTextsResIds[position]);
final TextView textView = (TextView) holder.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);
}

@Override
public int getItemCount() {
return mItemsTextsResIds.length;
}
});
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);
}
});

}

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];
}
};
}
}

spinner_drop_down_popup.xml

<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false">

<View
android:id="@+id/spinner_drop_down_popup__overlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#33000000"/>

<LinearLayout
android:id="@+id/spinner_drop_down_popup__itemsContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">

<android.support.v7.widget.RecyclerView
android:id="@+id/spinner_drop_down_popup__recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="8dp"
android:gravity="center"/>

<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@drawable/list_view_horizontal_divider"/>

<FrameLayout
android:id="@+id/spinner_drop_down_popup__preLollipopShadow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:foreground="?android:windowContentOverlay"/>
</LinearLayout>
</FrameLayout>

关于android - 如何在 TextView 下方正确显示弹出菜单,类似于 Spinner?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41919897/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com