android - 我如何在 Android 中打开微调器之外监听触摸?

标签 android spinner ontouchlistener touch-event

我正在做一个项目,要求我在屏幕上发生不同类型的触摸时写入日志。当我触摸打开的微调器下拉菜单的外部时,它会关闭。我不知道如何检测这种触摸。

这段代码没有捕捉到它,但它似乎捕捉到了小部件之外的所有其他触摸:

mFullView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    touchCounter++;
                    Log.d(TAG, "Touch #" + touchCounter + ", no button touch registered.");
                }
                return false;
            }
        });

其中 mFullView 是我拥有的父 RelativeLayout 并且设置如下:

mFullView = findViewById(R.id.full_view);

我也试过像这样使用 onTouchEvent:

@Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getActionMasked();
        if (action == MotionEvent.ACTION_DOWN) {
            Log.d(TAG, "screen was touched outside of open spinner dropdown");
        }
        return super.onTouchEvent(event);
    }

我在 onCreate 之外有这段代码,对放置或实现不太自信。

我找不到关于如何实现这个的任何信息,感谢您的帮助!

最佳答案

单击时,Spinner 将显示一个Dialog 或一个PopupWindow。这些都不会附加到与您的 Activity 相同的 Window,因此您将无法从那里拦截触摸事件。

也许有人可以破解他的子类化 Spinner

我找到了一种方法来做到这一点,它非常 hacky。

1- 我们需要覆盖 public void onWindowFocusChanged(boolean hasFocus) 这个方法将在 ActivityWindow 丢失时被调用焦点是因为 PopupWindow 的 View 已附加到 Activity 的 View 之上的新 Window

2- 获取所有 Window 的根 View 的列表,this answer有一个非常肮脏的 hacky 方法来做到这一点

3- 这些根 View 之一将是 PopupDecorView,它是 PopupWindow 的私有(private)非静态类。我们需要通过反射获取PopupWindow的实例

4- 一旦我们有了 PopupWindow 的实例,我们需要获取 OnTouchListener,将它包裹在我们自己的一个实例上并将其设置回 弹出窗口

重写的方法如下所示:

@Override
public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    new Handler().post(new Runnable() {
        @Override
        public void run() {
            for(View view : getWindowManagerViews()){
                try {
                    Class clazz = view.getClass();
                    Field outerField = clazz.getDeclaredField("this$0");
                    outerField.setAccessible(true);
                    Object popupWindow = outerField.get(view);

                    Field field = popupWindow.getClass().getDeclaredField("mTouchInterceptor");
                    field.setAccessible(true);
                    final View.OnTouchListener innerOnTouchListener = (View.OnTouchListener) field.get(popupWindow);
                    View.OnTouchListener outerOnTOuchListener = new View.OnTouchListener() {
                        @Override
                        public boolean onTouch(View v, MotionEvent event) {
                            Log.d(MainActivity.class.getSimpleName(), String.format("popupwindow event %s at %s-%s", event.getAction(), event.getX(), event.getY()));
                            return innerOnTouchListener.onTouch(v, event);
                        }
                    };
                    field.set(popupWindow, outerOnTOuchListener);
                }catch (Exception e){
                    //e.printStackTrace();
                }
            }
        }
    });
}

其中 getWindowManagerViews() 取自上述答案,看起来像这样

public static List<View> getWindowManagerViews() {
    try {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH &&
                Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {

            // get the list from WindowManagerImpl.mViews
            Class wmiClass = Class.forName("android.view.WindowManagerImpl");
            Object wmiInstance = wmiClass.getMethod("getDefault").invoke(null);

            return viewsFromWM(wmiClass, wmiInstance);

        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {

            // get the list from WindowManagerGlobal.mViews
            Class wmgClass = Class.forName("android.view.WindowManagerGlobal");
            Object wmgInstance = wmgClass.getMethod("getInstance").invoke(null);

            return viewsFromWM(wmgClass, wmgInstance);
        }

    } catch (Exception e) {
        e.printStackTrace();
    }

    return new ArrayList<View>();
}

private static List<View> viewsFromWM(Class wmClass, Object wmInstance) throws Exception {

    Field viewsField = wmClass.getDeclaredField("mViews");
    viewsField.setAccessible(true);
    Object views = viewsField.get(wmInstance);

    if (views instanceof List) {
        return (List<View>) viewsField.get(wmInstance);
    } else if (views instanceof View[]) {
        return Arrays.asList((View[])viewsField.get(wmInstance));
    }

    return new ArrayList<View>();
}

关于android - 我如何在 Android 中打开微调器之外监听触摸?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44785267/

相关文章:

Android 进程经常产生 logcat "has died"消息。

java - 调用请求限制

javascript - 在新图像完全加载时添加微调器

android - OnTouchListener 阻止 map 移动

android - onTouchListener 不工作

android - 如何检测android中禁用复选框的触摸事件

android - 带有 SD 卡的平板电脑上的 Linux?

android - RecognizerIntent.ACTION_RECOGNIZE_SPEECH - 在模拟器上找不到处理它的 Activity

Android:如何将微调器绑定(bind)到自定义对象列表?

java - Spinner 不使用 API 23 中的 onItemSelected 方法