Вопрос

I've been battling with setSystemUiVisibility() to try to hide the soft navigation buttons for a while (for a video player). It does not seem to work as advertised. Here is my code, inside a visible FrameLayout.

void setNavVisibility(boolean visible)
{
    int newVis = SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
    if (!visible)
    {
        newVis |= SYSTEM_UI_FLAG_LOW_PROFILE | SYSTEM_UI_FLAG_HIDE_NAVIGATION;
    }
    setSystemUiVisibility(newVis);
}

Slightly modified from the SDK example:

    void setNavVisibility(boolean visible) {
        int newVis = SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                | SYSTEM_UI_FLAG_LAYOUT_STABLE;
        if (!visible) {
            newVis |= SYSTEM_UI_FLAG_LOW_PROFILE | SYSTEM_UI_FLAG_FULLSCREEN
                    | SYSTEM_UI_FLAG_HIDE_NAVIGATION;
        }

Neither do anything when called. I had some success when I called them from a different View, but the documentation doesn't mention that the View you are calling from has any effect? I assume that the documentation is (surprise surprise) somewhat lacking here. Does anyone know what's really going on?

Это было полезно?

Решение

Hmm apparently I duplicated my own question! But anyway I found the answer: After reading the source code - which is often the only way to find things out in Android-land - I discovered the following undocumented fact:

setSystemUiVisibility() only has effect when the view you call it from is visible!

Even more: The view in which you call setSystemUiVisibility() must remain visible for the nav bar to remain hidden. Thanks for documenting that guys, really great.

Here is the relevant code, in View.java.

void performCollectViewAttributes(AttachInfo attachInfo, int visibility) {
    if ((visibility & VISIBILITY_MASK) == VISIBLE) {
        if ((mViewFlags & KEEP_SCREEN_ON) == KEEP_SCREEN_ON) {
            attachInfo.mKeepScreenOn = true;
        }
        attachInfo.mSystemUiVisibility |= mSystemUiVisibility;
        ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnSystemUiVisibilityChangeListener != null) {
            attachInfo.mHasSystemUiListeners = true;
        }
    }
}

Другие советы

I use setOnSystemUiVisibilityChangeListener() to make this full screen mode work for me including hiding the navigation bar.

@Override
protected void onResume() {
    if (Build.VERSION.SDK_INT >= 16) {
        getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(
                new OnSystemUiVisibilityChangeListener() {
                    @Override
                    public void onSystemUiVisibilityChange(int visibility) {
                        if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
                            getActionBar().show();
                        } else {
                            int mUIFlag = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                                    | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                                    | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                                    | View.SYSTEM_UI_FLAG_LOW_PROFILE
                                    | View.SYSTEM_UI_FLAG_FULLSCREEN
                                    | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
                            getWindow().getDecorView()
                                    .setSystemUiVisibility(mUIFlag);
                            getActionBar().hide();
                        }
                    }
                });
    }

    super.onResume();
}

it seems that android only trigger the setSystemUiVisibility() when inside the listener.

I implemented this as below however I get a blank gap at the bottom (see screenshot)

@Override
public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    if (hasFocus) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            getWindow().getDecorView().setSystemUiVisibility(
                    View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                            | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                            | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                            | View.SYSTEM_UI_FLAG_LOW_PROFILE
                            | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar
                            | View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar
                            | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
            );
        }
    }
}

enter image description here

Are you sure no user interaction is going on? i.e. emulating a touch which causes the controls to instantly reappear?

From the docs:

SYSTEM_UI_FLAG_HIDE_NAVIGATION: There is a limitation: because navigation controls are so important, the least user interaction will cause them to reappear immediately. When this happens, both this flag and SYSTEM_UI_FLAG_FULLSCREEN will be cleared automatically, so that both elements reappear at the same time.

On a side note, I've noticed issues with the view not resizing under some circumstances even with the stock video player in JB. I think it's (or was) a bug.

I found that if the user interacted with the device then the navigation bar would return. Immediately setting the flags in the OnSystemUiVisibilityChangeListener handler method would not work. It would work if you changed them after a one second delay.

protected static final int requiredDecorViewFlags = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
        | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
        | View.SYSTEM_UI_FLAG_LOW_PROFILE
        | View.SYSTEM_UI_FLAG_FULLSCREEN
        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;


protected void listenToUiVisibilityChange() {
    Window window = getWindow();
    if(window == null) {
        return;
    }
    final View view = window.getDecorView();
    if(view == null) {
        return;
    }
    view.setOnSystemUiVisibilityChangeListener(
        new View.OnSystemUiVisibilityChangeListener() {
            @Override
            public void onSystemUiVisibilityChange(int visibility) {
                Handler handler = new Handler(
                    getMainLooper()
                );
                handler.postDelayed(
                    new Runnable() {
                        @Override
                        public void run() {
                            Window activityWindow = getWindow();
                            if(activityWindow == null) {
                                return;
                            }
                            View decorView = activityWindow.getDecorView();
                            if(decorView == null) {
                                return;
                            }
                            decorView.setSystemUiVisibility(requiredDecorViewFlags);
                       }
                   },
                   1000
               );
           }
       }
    );
}

@Override
protected void onResume() {
    super.onResume();
    listenToUiVisibilityChange();
}
//do it by the following method hide it the status bar in sdk < 19 and for 
// greater u can do by providing the by the following i am doing here for both



      public void FullScreencall() {
    if (Build.VERSION.SDK_INT < 19) {
        getWindow().getDecorView().setSystemUiVisibility(8);
     }
    else {
        getWindow().getDecorView().setSystemUiVisibility
     (InputDeviceCompat.SOURCE_TOUCHSCREEN);
     }
   }
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top