17

我想在微调器下拉被解除时捕捉一个事件。当用户单击 onItemSelected() 中的任何项目时,我们可以捕获它。但是即使用户在下拉区域或后退按钮之外触摸,我也想抓住它,因为它们也会使其消失。在这两个原因中,当我观察日志时,它显示“尝试完成输入事件,但输入事件接收器已被释放”

我观察了源代码,这是从InputEventReceiver.java中的finishInputEvent(InputEvent event, boolean handler)方法打印出来的。但这是一种最终方法,因此没有必要覆盖它。有人可以建议在这些情况下关闭下拉菜单时的处理方式吗?

4

4 回答 4

1

我用过Popup MenuSpinner 代替。因为据我所知,微调器无法捕获解雇事件,但是使用弹出菜单我通过设置弹出菜单onDismissListerner()来做到这一点

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.
}
});
于 2014-02-20T09:17:24.713 回答
0

寻找另一个事件怎么样onDetachFromWindow?spinner 没有任何我们经常使用的常规生命周期事件——如果有一个onStoporonDestroy使用它会很好。当然,您必须扩展 spinner 类并创建一个接口来定义您自己的侦听器:

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

在您的布局 XML 中,您要确保指定此控件而不是正常的微调器,并在您的代码中设置侦听器,实现微调器分离时您想要执行的任何操作。您可以在客户端确定如何跟踪是否已选择某些内容,可能通过在onItemSelected您提供选择侦听器的方法中设置一个变量来决定。

于 2013-11-16T20:57:22.470 回答
0

我遇到了同样的问题,我想检测弹出窗口何时关闭,无论是在外部单击还是选择了元素。我不知道为什么谷歌不想添加简单的侦听器,我们可以用它来检测如此重要的事情。都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!!!     
|   |   |    
|────────      
  1. 我们创建实现 AppCompatSpinner 类的自定义类CustomSpinner
  2. 我们需要从 AppCompatSpinner访问私有对象mPopup
  3. 然后我们需要从 ListPopupWindow 类中获取私有对象mPopup
  4. 然后我们需要从 PopupWindow 类中获取私有对象mOnDismissListener

现在我们需要在 AppCompatSpinner 类中声明的 DropdownPopup 类中找到一个在方法 show() 之后调用的方法。但是这个方法应该在调用原来的 OnDismissListener 之前触发。这个特殊的方法是performClick(),当用户点击微调器时调用它,然后触发 show() 方法,然后附加原始的 OnDismissListener。

下面是我们需要在 performClick() 方法中执行的步骤:

  1. 存储对原始 OnDismissListener 的引用
  2. 设置弹窗关闭时调用的新 OnDismissListener
  3. 调用我们可以使用的自定义监听器 onPopUpClosedListener
  4. 最后,最重要的是使用我们之前存储的引用调用原始 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
}
于 2021-09-13T02:34:39.333 回答
-1

如果您真的不需要使用微调器,请尝试使用此代码。
Dialog内的ListView。您可以侦听对话框的取消/关闭事件(同样的事情)。您可以在 API 11 中使用它。

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>
于 2016-05-02T06:57:30.597 回答