我遇到了同样的问题,我想检测弹出窗口何时关闭,无论是在外部单击还是选择了元素。我不知道为什么谷歌不想添加简单的侦听器,我们可以用它来检测如此重要的事情。都2021年了,还没有什么好的检测方法,真的谷歌???
显然,解决方案是使用反射并访问私有变量。正如@Kanth 建议的那样,我们需要访问OnDismissListener()。但是他的回答有点过时,特别是如果您打算使用AppCompatSpinner
进一步检查我们可以看到 AppCompatSpinner 包含私有对象“mPopup”,它来自接口SpinnerPopup类型。
private SpinnerPopup mPopup;
然后这个接口被DropdownPopup类使用并实现了它的方法,我们需要更仔细地查看实现的方法 show()。如果我们继续下去,我们可以看到它设置了 OnDismissListener()。因此,监听器随后用于使用 removeGlobalOnLayoutListener() 方法移除全局布局监听器。所以我们不能直接改变setOnDismissListener,因为之前添加的全局布局监听器需要移除。
现在我们需要找到监听器的确切存储位置,然后我们需要获取该值并保留它。然后设置新的 OnDismissListener,我们可以在其中检测弹出窗口的关闭。最后调用原来的 OnDismissListener 非常重要,这样可以去掉全局布局监听器。所以调用setOnDismissListener()方法是在 ListPopupWindow 类内部,它从它的“mPopup”对象调用相同的方法。
最后我们到了 end 方法和存储监听器引用对象的类。该对象称为mOnDismissListener ,当我们使用setOnDismissListener()方法设置新的侦听器时,我们需要保持对它的引用。
因此,我们需要覆盖微调器类并以某种方式覆盖 OnDismissListener,为此我们需要将 3 个父类放到兔子洞中。
CustomSpinner
├── AppCompatSpinner (mPopup)
│ ├── ListPopupWindow (mPopup)
| | ├── PopupWindow (mOnDismissListener) finally!!!
| | |
|────────
- 我们创建实现 AppCompatSpinner 类的自定义类CustomSpinner
- 我们需要从 AppCompatSpinner访问私有对象mPopup
- 然后我们需要从 ListPopupWindow 类中获取私有对象mPopup
- 然后我们需要从 PopupWindow 类中获取私有对象mOnDismissListener
现在我们需要在 AppCompatSpinner 类中声明的 DropdownPopup 类中找到一个在方法 show() 之后调用的方法。但是这个方法应该在调用原来的 OnDismissListener 之前触发。这个特殊的方法是performClick(),当用户点击微调器时调用它,然后触发 show() 方法,然后附加原始的 OnDismissListener。
下面是我们需要在 performClick() 方法中执行的步骤:
- 存储对原始 OnDismissListener 的引用
- 设置弹窗关闭时调用的新 OnDismissListener
- 调用我们可以使用的自定义监听器 onPopUpClosedListener
- 最后,最重要的是使用我们之前存储的引用调用原始 OnDismissListener 以删除全局布局侦听器
所以这是我们自定义 Spinner 类的最终源代码
open class CustomSpinner: androidx.appcompat.widget.AppCompatSpinner {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
lateinit var listPopupWindow: ListPopupWindow
lateinit var onPopUpClosedListener: (dropDownMenu: DropDownMenu) -> Unit
init {
try {
// get private property and make it accessible
val listPopupWindowField = androidx.appcompat.widget.AppCompatSpinner::class.java.getDeclaredField("mPopup")
listPopupWindowField.isAccessible = true
// get the list popup window
listPopupWindow = listPopupWindowField.get(this) as ListPopupWindow
listPopupWindow.isModal = false
} catch (e: Exception) {
e.printStackTrace()
}
}
override fun performClick(): Boolean {
val returnValue = super.performClick()
try {
// get the popupWindow
val popupWindowField = ListPopupWindow::class.java.getDeclaredField("mPopup")
popupWindowField.isAccessible = true
val popupWindow = popupWindowField.get(listPopupWindow) as PopupWindow
// get the original onDismissListener
val onDismissListenerField = PopupWindow::class.java.getDeclaredField("mOnDismissListener")
onDismissListenerField.isAccessible = true
val onDismissListener = onDismissListenerField.get(popupWindow) as PopupWindow.OnDismissListener
// now override the original OnDismissListener
listPopupWindow.setOnDismissListener {
// here we detect when the drop down is dismissed and call the listener
if (::onPopUpClosedListener.isInitialized) {
onPopUpClosedListener.invoke(this)
}
// now we need to call the original listener that will remove the global OnLayoutListener
onDismissListener.onDismiss()
}
} catch (e: Exception) {
e.printStackTrace()
}
return returnValue
}
}
然后我们可以简单地使用 onPopUpClosedListener 监听器并检测弹出窗口何时关闭。
val customSpinner: CustomSpinner = findViewById(R.id.mySpinner)
customSpinner.onPopUpClosedListener = {
// here we detect when the pop-up from our custom spinner is closed
}