在过去 >48 小时内,我一直在努力解决这个问题(我有一些额外的时间不要怪我),我可以肯定地告诉你这些:
- 使用 AppCompat 主题和普通的
Spinner 为您提供免费的涟漪(耶!)。如果你有一个自定义的Spinner,请确保扩展AppCompatSpinner,你会得到一些很好的主题。引用AppCompatSpinner javadoc:
支持旧版本平台兼容功能的 Spinner,包括:
- 允许通过 ViewCompat 中的背景着色方法对其背景进行动态着色。
- 允许使用 backgroundTint 和 backgroundTintMode 设置背景色调。
- 允许使用 popupTheme 设置弹出窗口主题。
这将在您在布局中使用 Spinner 时自动使用。您应该只需要在编写自定义视图时手动使用此类。
没有没有方法来检索通过扩展 ANY 类显示的PopupWindow。有很多嵌套类有助于该功能(剧透:它们大多是私有的)。
-
您可以在您的主题中自定义PopupWindow...静态的进入和退出过渡(哇哦!)。我目前正在使用 AppCompat v23 并进行了一些调查,发现:
-
自定义ListPopupWindowLOLLIPOP及以上的样式
<style name="Widget.Material.ListPopupWindow">
<item name="dropDownSelector">?attr/listChoiceBackgroundIndicator</item>
<item name="popupBackground">@drawable/popup_background_material</item>
<item name="popupElevation">@dimen/floating_window_z</item>
<item name="popupAnimationStyle">@empty</item>
<item name="popupEnterTransition">@transition/popup_window_enter</item>
<item name="popupExitTransition">@transition/popup_window_exit</item>
<item name="dropDownVerticalOffset">0dip</item>
<item name="dropDownHorizontalOffset">0dip</item>
<item name="dropDownWidth">wrap_content</item>
</style>
-
在 LOLLIPOP 之前对其进行自定义的样式:
<style name="Base.Widget.AppCompat.ListPopupWindow" parent="">
<item name="android:dropDownSelector">?attr/listChoiceBackgroundIndicator</item>
<item name="android:popupBackground">@drawable/abc_popup_background_mtrl_mult</item>
<item name="android:dropDownVerticalOffset">0dip</item>
<item name="android:dropDownHorizontalOffset">0dip</item>
<item name="android:dropDownWidth">wrap_content</item>
</style>
-
这就是您在主题中设置的内容:
// From the constructor listPopupWindowStyle is the
// theme attribute you're looking for.
public ListPopupWindow(Context context, AttributeSet attrs) {
this(context, attrs, R.attr.listPopupWindowStyle);
}
也就是说,我仍在获取您的详细信息,但我正在通过 克隆 2 个类来解决它:AppCompatSpinner 和 ListPopupWindow 并添加一个公共 getPopup() getter到克隆的ListPopupWindow 代码,因此它可以在我的克隆AppCompatSpinner 中访问。这带来了几个机会。一个例子是我的centerPopup() 方法,它应该根据 spinnerItem(在微调器视图本身中显示的选定项目)和 spinnerDropdownItem(在落下)。然后在调用super.show() 之后立即在DropdownPopup.show() 中调用centerPopup() 方法
private void centerPopup(boolean updateListSelection) {
boolean aboveAnchor = getPopup().isAboveAnchor();
int[] spinnerLocation = new int[2];
int[] popupLocation = new int[2];
ListView listView = getListView();
/*
Popup is anchored at spinner TOP LEFT when it is set to overlap else at BOTTOM LEFT
Also seems the windowlayoutparams generated for popup is wrapcontent for width and height
As a result popup locationOnScreen returns [0, 0] which is relative to the window.
We can take it as relative to spinner location and add spinnerLocation X value to give
left edge of popup
*/
MaterialSpinner.this.getLocationInWindow(spinnerLocation);
int spMidX = spinnerLocation[0] + (MaterialSpinner.this.getWidth() / 2);
int spMidY = spinnerLocation[1] + (MaterialSpinner.this.getHeight() / 2);
Rect spinnerBgdPadding = new Rect();
MaterialSpinner.this.getBackground().getPadding(spinnerBgdPadding);
// ----- BUG - returns erroneous height
// Ideally should measure one of the drop down list children and give a height
// exactly as it wouldwhen laid out eventually.
// Works only for lists with homogenously tall content
View child = listView.getAdapter().getView(0, null, listView);
child.setLayoutParams(new LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
child.measure(
MeasureSpec.makeMeasureSpec(listView.getWidth() - listView.getPaddingLeft() - listView.getPaddingRight(), MeasureSpec.AT_MOST),
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
);
int listItemHeight = child.getMeasuredHeight();
// ----- /BUG
getPopup().getContentView().getLocationInWindow(popupLocation);
int popMidX = spinnerLocation[0] + (getPopup().getWidth()) / 2;
int popMidY = spinnerLocation[1] + (listItemHeight / 2);
int hoff = getHorizontalOffset();
int voff = getVerticalOffset();
int xoff = spMidX - popMidX - hoff - spinnerBgdPadding.left;
int yoff = spMidY - popMidY - voff;
getPopup().update(spinnerLocation[0] + xoff, spinnerLocation[1] + yoff, -1, -1, true);
if (updateListSelection)
listView.setSelectionFromTop(MaterialSpinner.this.getSelectedItemPosition(), 0)
;
// getPopup().update(MaterialSpinner.this, xoff, yoff, -1, -1);
// int numVisItems = listView.getLastVisiblePosition() - getFirstVisiblePosition();
}
如果返回了正确的 listItemHeight,弹出窗口不仅应该出现在微调器上,而且文本中心也应该对齐。 (由于背景和填充相关问题,可能会偏离一些像素,但可以解决)。
一旦居中,下一步就是将 dropDownList selectedPosition View 滚动到 popupWindow 的中心,并相应地更新 yoff Y 偏移量。
popupEnterTransition 属性仅限于LOLLIPOP,但如果这不是问题,您可以在此处插入放大转换,这应该可以获得您想要的效果。不过我还没有尝试过,但我也认为更好的动画应该是微调器中心的显示效果(看起来像你的规格视频中的那个)。
但所有这些都很好...理论上 x_x 哈哈