Skip to content

Instantly share code, notes, and snippets.

@kakajika
Last active May 3, 2023 16:01
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save kakajika/a236ba721a5c0ad3c1446e16a7423a63 to your computer and use it in GitHub Desktop.
Save kakajika/a236ba721a5c0ad3c1446e16a7423a63 to your computer and use it in GitHub Desktop.
Avoid taking window focus by Android Spinner's Dropdown to keep setSystemUiVisibility flags (such as Immersive Mode).
import android.widget.ListPopupWindow;
import android.widget.PopupWindow;
import android.widget.Spinner;
public static void avoidSpinnerDropdownFocus(Spinner spinner) {
try {
Field listPopupField = Spinner.class.getDeclaredField("mPopup");
listPopupField.setAccessible(true);
Object listPopup = listPopupField.get(spinner);
if (listPopup instanceof ListPopupWindow) {
Field popupField = ListPopupWindow.class.getDeclaredField("mPopup");
popupField.setAccessible(true);
Object popup = popupField.get((ListPopupWindow) listPopup);
if (popup instanceof PopupWindow) {
((PopupWindow) popup).setFocusable(false);
}
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
import android.widget.ListPopupWindow
import android.widget.PopupWindow
import android.widget.Spinner
fun Spinner.avoidDropdownFocus() {
try {
val listPopup = Spinner::class.java
.getDeclaredField("mPopup")
.apply { isAccessible = true }
.get(this)
if (listPopup is ListPopupWindow) {
val popup = ListPopupWindow::class.java
.getDeclaredField("mPopup")
.apply { isAccessible = true }
.get(listPopup)
if (popup is PopupWindow) {
popup.isFocusable = false
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
@CheckHelzio
Copy link

When you call this method to prevent exit immersive mode?

@kakajika
Copy link
Author

@CheckHelzio It should be called before showing dropdown.

@h6ah4i
Copy link

h6ah4i commented May 5, 2019

Thank you for the very useful code snippet, but I noticed it does not work with AppCompatSpinner (appcompat v1.1.0-alpha04).

Here is a bit modified version which compatible with AppCompatSpinner 👏

import android.widget.PopupWindow
import android.widget.Spinner

fun Spinner.avoidDropdownFocus() {
    try {
        val isAppCompat = this is androidx.appcompat.widget.AppCompatSpinner
        val spinnerClass = if (isAppCompat) androidx.appcompat.widget.AppCompatSpinner::class.java else Spinner::class.java
        val popupWindowClass = if (isAppCompat) androidx.appcompat.widget.ListPopupWindow::class.java else android.widget.ListPopupWindow::class.java

        val listPopup = spinnerClass
                .getDeclaredField("mPopup")
                .apply { isAccessible = true }
                .get(this)
        if (popupWindowClass.isInstance(listPopup)) {
            val popup = popupWindowClass
                    .getDeclaredField("mPopup")
                    .apply { isAccessible = true }
                    .get(listPopup)
            if (popup is PopupWindow) {
                popup.isFocusable = false
            }
        }
    } catch (e: Exception) {
        e.printStackTrace()
    }
}

@namgk
Copy link

namgk commented Jan 2, 2020

shame on Android team!

@namgk
Copy link

namgk commented Jan 3, 2020

apparently this breaks my espresso test, not sure why. It still works with manual pressing the item though.

@dukess
Copy link

dukess commented Aug 2, 2021

Thanks for nice solution!

@peterdk
Copy link

peterdk commented Aug 29, 2021

It's working nice, but with setting it to non focusable, it loses the click outside to close functionality. Unfortunately I can't find any workaround, and I tried many.

@edenman
Copy link

edenman commented Aug 30, 2021

isOutsideTouchable = true works for me fwiw

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment