我观察了源代码,这是在InputEventReceiver.java的finishInputEvent(InputEvent event, boolean handled)方法中打印的。但它是一个final方法,因此没有覆盖它的意义。请问有人能建议一种处理在这些情况下关闭下拉菜单的方法吗?
我使用了弹出菜单
而不是Spinner。因为据我所知,Spinner无法捕获dismiss事件,但是通过将onDismissListener()
设置为弹出菜单,我成功地做到了。
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
Toast.makeText(MyActivity.this,"Clicked on: " + item.getTitle(),Toast.LENGTH_LONG).show();
return true;
}
});
popup.setOnDismissListener (new PopupMenu.OnDismissListener(){
public void onDismiss()
{
//catch dismiss event here.
}
});
我曾经遇到过同样的问题,我想要检测弹出窗口何时关闭,无论是点击外部还是选择元素。我不知道为什么Google不想添加简单的监听器,让我们可以用来检测这样关键的事情。现在已经是2021年了,仍然没有好的方法来检测它,真的吗,Google?
显然,解决方案是使用反射并获取对私有变量的访问权限。正如@Kanth建议的那样,我们需要访问OnDismissListener()。但他的答案有点过时,特别是如果你打算使用AppCompatSpinner。
进一步检查后,我们可以看到AppCompatSpinner包含了私有对象“mPopup”,它是从接口SpinnerPopup类型中获得的。
private SpinnerPopup mPopup;
这个接口随后被类DropdownPopup使用,并实现了它的方法,我们需要更仔细地查看实现的方法show()。如果我们往下滑动,就可以看到它设置了OnDismissListener()。因此,该侦听器随后用于使用方法removeGlobalOnLayoutListener()移除全局布局侦听器。因此,我们不能直接更改setOnDismissListener,因为之前添加的全局布局侦听器需要被移除。
现在我们需要找到侦听器存储的确切位置,然后获取该值并保留它。然后设置新的OnDismissListener,在其中可以检测弹出窗口的关闭。最后非常重要的是调用原始的OnDismissListener,以便它可以删除全局布局侦听器。因此,调用方法 setOnDismissListener() 进入ListPopupWindow类,从其 'mPopup' 对象调用相同的方法。
最后,我们来到了结束方法和存储监听器引用对象的类。该对象称为mOnDismissListener,当我们使用方法setOnDismissListener()设置新的监听器时,我们需要保持对它的引用。因此,我们需要覆盖Spinner类,并以某种方式覆盖OnDismissListener,为此我们需要深入3个父类。
CustomSpinner
├── AppCompatSpinner (mPopup)
│ ├── ListPopupWindow (mPopup)
| | ├── PopupWindow (mOnDismissListener) finally!!!
| | |
|────────
现在,我们需要找到一个方法,在DropdownPopup类中的show()方法之后调用,该类声明在AppCompatSpinner类中。但是,在原始OnDismissListener被调用之前,应该触发此方法。这个特殊的方法是performClick(),当用户点击下拉框时,将调用此方法,然后触发show()方法,然后附加原始的OnDismissListener。
因此,以下是我们需要在performClick()方法中执行的步骤:
所以这就是我们自定义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
}
onDetachFromWindow
的其他事件吗?下拉菜单没有我们经常使用的任何常规生命周期事件--拥有一个onStop
或onDestroy
将是很好的。当然,您需要扩展下拉菜单类并创建接口来定义自己的监听器:public class ChattySpinner extends Spinner {
private ChattySpinnerListener chattyListener;
public ChattySpinner(Context context) {
super(context);
}
public ChattySpinner(Context context, int mode) {
super(context, mode);
}
public ChattySpinner(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ChattySpinner(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public ChattySpinner(Context context, AttributeSet attrs, int defStyle, int mode) {
super(context, attrs, defStyle, mode);
}
public void setChattyListener(ChattySpinnerListener listener) {
this.chattyListener = listener;
}
@Override
protected void onDetachedFromWindow() {
if(chattyListener != null) {
chattyListener.onDetach();
}
super.onDetachedFromWindow();
}
public interface ChattySpinnerListener {
public void onDetach();
}
}
final ArrayAdapter adapterPlayerSelect = new ArrayAdapter(getActivity(), R.layout.item_simple_list, playModules);
adapterPlayerSelect.setDropDownViewResource(R.layout.item_simple_list_bg);
spPlayerSelect.setAdapter(adapterPlayerSelect);
spPlayerSelect.setOnTouchListener((v, event) -> {
if (event.getAction() == MotionEvent.ACTION_UP) {
final ArrayAdapter adapterPlayerPopup = new ArrayAdapter(getActivity(), R.layout.item_simple_list_bg, playModules);
final ListView lvMenu = new ListView(getActivity());
lvMenu.setAdapter(adapterPlayerPopup);
final androidx.appcompat.app.AlertDialog.Builder builder = new androidx.appcompat.app.AlertDialog.Builder(getActivity(), R.style.Theme_AppCompat_Dialog);
builder.setView(lvMenu);
final androidx.appcompat.app.AlertDialog dialog = builder.create();
lvMenu.setOnItemClickListener((parent, view, position, id) -> {
spPlayerSelect.setSelection(position);
dialog.dismiss();
});
dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
//Action on dismiss
}
});
dialog.show();
return true;
}
return false;
});
final Dialog dialog = new Dialog(context);
dialog.setContentView(R.layout.custom_list_popup);
//dialog.setCancelable(false);
dialog.setTitle("Title");
dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
//
//Do your onCancel things here
//
}
});
final ListView listView = (ListView) dialog.findViewById(R.id.lv_sales_tax);
listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//
//Do your stuff here
//
dialog.dismiss();
}
});
dialogButton.setVisibility(View.GONE);
dialog.show();
}
});
custom_list_popup.xml的内容
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="20dp"
android:orientation="vertical">
<ListView
android:id="@+id/lv_sales_tax"
android:divider="@drawable/list_divider"
android:dividerHeight="20dp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
setOnItemSelectedListener()
进行检查? - rekire