如何在 Android 中的打开微调器之外监听触摸?
How can I listen for a touch outside of an open spinner in Android?
我正在做一个项目,要求我在屏幕上发生不同类型的触摸时写入日志。当我触摸打开的微调器下拉菜单的外部时,它会关闭。我不知道如何检测这种触摸。
这段代码没有捕捉到它,而它似乎捕捉到了小部件之外的所有其他触摸:
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
,因此您将无法从那里拦截触摸事件。
也许有人可以破解他的方式 classing a Spinner
我找到了一个方法来做到这一点,它非常 hacky。
1- 我们需要覆盖 public void onWindowFocusChanged(boolean hasFocus)
当 Activity
的 Window
失去焦点时将调用此方法,因为 PopupWindow
的视图已被在 Activity
的
之上附加一个新的 Window
2- 获取所有 Window
s root View
s 的列表,this answer 有一个非常肮脏的 hacky 方法来做到这一点
3- 这些根 View
之一将是 PopupDecorView
,它是 PopupWindow
的私有非静态 class。我们需要通过反射
获取PopupWindow
的实例
4- 一旦我们有了 PopupWindow
的实例,我们需要获取 OnTouchListener
,将它包裹在我们自己的实例中并将其设置回 PopupWindow
覆盖的方法如下所示:
@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[=10=]");
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>();
}
我正在做一个项目,要求我在屏幕上发生不同类型的触摸时写入日志。当我触摸打开的微调器下拉菜单的外部时,它会关闭。我不知道如何检测这种触摸。
这段代码没有捕捉到它,而它似乎捕捉到了小部件之外的所有其他触摸:
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
,因此您将无法从那里拦截触摸事件。
也许有人可以破解他的方式 classing a Spinner
我找到了一个方法来做到这一点,它非常 hacky。
1- 我们需要覆盖 public void onWindowFocusChanged(boolean hasFocus)
当 Activity
的 Window
失去焦点时将调用此方法,因为 PopupWindow
的视图已被在 Activity
的
Window
2- 获取所有 Window
s root View
s 的列表,this answer 有一个非常肮脏的 hacky 方法来做到这一点
3- 这些根 View
之一将是 PopupDecorView
,它是 PopupWindow
的私有非静态 class。我们需要通过反射
PopupWindow
的实例
4- 一旦我们有了 PopupWindow
的实例,我们需要获取 OnTouchListener
,将它包裹在我们自己的实例中并将其设置回 PopupWindow
覆盖的方法如下所示:
@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[=10=]");
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>();
}