androidkotlinandroid-spinner

Spinner in Full Screen android app is showing status bar


I have an android app. I have a spinner called dd1 defined in my mainactivity.xml.

I have the onItemSelected method defined in a fragment.

The app runs full screen, i.e. hides the navigation and status bar. Everything is fine. But the moment I touch the spinner, the status bar pops up, and never goes back.

I need to ensure that the status bar remains hidden.

Attempt to solve

I read this SO Question. It already refers to this GIT gist.

In my Fragment, just before the override fun onCreateView I define:

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

Then, in the onItemSelected, I have:

 dd1.onItemSelectedListener =  object : AdapterView.OnItemSelectedListener {
            override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {

                // getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
                

                dd1.avoidDropdownFocus()
    ... // do my thing
 }
} 

This does not work. I am using Android 10, but on a DJI Remote Control.

Question

How do I force the status bar to remain hidden during the runtime of the app, despite activating the spinner(s)?

Thank you


Solution

  • Please avoid this reflection way.
    May be it's an issue before Android 11 that status bar pops up when spinner opens, 'cause I can reproduce it on Android 9, but can't on Android 11.

    So, I think you can ignore the pop-up issue.

    And to avoid status bar remaining visible when spinner closed, hide status bar manually in your activity:

        override fun onWindowFocusChanged(hasFocus: Boolean) {
            super.onWindowFocusChanged(hasFocus)
            if (hasFocus) {
                WindowCompat.getInsetsController(window, window.decorView).apply {
                    hide(WindowInsetsCompat.Type.statusBars())
                    // Or instead hide statusBar and navigationBar together if required.
                    //hide(WindowInsetsCompat.Type.systemBars())
                }
            }
        }
    

    Updated 1: ---------

    I found the statusbar pop-up issue is because of "Temporary full screen".
    To implement full screen, we usually do these ways:

    1. Stable full screen

    Use Theme or window flag:

    // Set theme to full screen or use a full screen theme from SDK.
    <item name="android:windowFullscreen">true</item>
    
    // Or set window flag in activity:
    window.setFlags(
        WindowManager.LayoutParams.FLAG_FULLSCREEN,
        WindowManager.LayoutParams.FLAG_FULLSCREEN
    )
    

    In that way statusbar will not pop up when Spinner/PopupWindow shows.

    1. "Temporary full screen"

    Hide status bar via code:

    // By insets controller 
    WindowCompat.getInsetsController(window, window.decorView).apply {
        hide(WindowInsetsCompat.Type.statusBars())
    }
    // Or
    window.decorView.systemUiVisibility =
        window.decorView.systemUiVisibility or View.SYSTEM_UI_FLAG_FULLSCREEN
    
    // Usually, we will also set systembar's behavior so the hidden 
    // statusbar will hide after a while when user swipe to show it.
    WindowCompat.getInsetsController(window, window.decorView).apply {
                systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
            }
    

    This way, status bar will pop up when Spinner/PopupWindow shows bellow Android 11 version.
    To avoid it, use this code:

    
    import androidx.appcompat.widget.AppCompatSpinner
    import androidx.appcompat.widget.ListPopupWindow
    
    // Note we should import androidx.appcompat.widget.ListPopupWindow
    fun AppCompatSpinner.avoidDropdownFocus() {
        try {
            val popupDelegate =
                AppCompatSpinner::class.java
                    .getDeclaredField("mPopup")
                    .apply { isAccessible = true }
                    .get(this)
            if (popupDelegate is ListPopupWindow) {
                val popup = ListPopupWindow::class.java
                    .getDeclaredField("mPopup")
                    .apply { isAccessible = true }
                    .get(popupDelegate)
                (popup as? PopupWindow)?.apply {
                    isFocusable = false
                    // We need to call "update" to let 'focusable' to take effect.
                    update()
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }
    

    Note this is only working for spinner dropdown mode, not for dialog mode.