when clicked, the Spinner
will show either a Dialog
or a PopupWindow
. neither of these will be attached to the same Window
as your Activity
so you won't be able to intercept touch events from there.
maybe one could hack his way subclassing a Spinner
I found a way to do this, it is very much hacky.
1- we need to override public void onWindowFocusChanged(boolean hasFocus)
this method will be called when the Activity
's Window
loses its focus because the PopupWindow
's View had been attached to a new Window
on top of the Activity
's one
2- get a list of all Window
s root View
s, this answer has a very dirty hacky method to do it
3- one of these root View
s will be a PopupDecorView
, which is a private non-static class of PopupWindow
. we need to get the instance of PopupWindow
via reflection
4- once we have the instance of PopupWindow
, we need to get the OnTouchListener
, wrap it around one of our own and set it back to the PopupWindow
the overridden method looks like this:
@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();
}
}
}
});
}
where getWindowManagerViews() is taken from the aforementioned answer, and it looks like this
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>();
}