대화 상자에서 몰입 형 모드를 유지하려면 어떻게해야합니까?


104

내 활동에 사용자 지정 대화 상자가 표시 될 때 새로운 몰입 형 모드를 유지하려면 어떻게해야합니까?

대화 상자에서 몰입 형 모드를 유지하기 위해 아래 코드를 사용하고 있지만 해당 솔루션을 사용하면 사용자 지정 대화 상자를 시작할 때 NavBar가 1 초 미만 동안 나타난 다음 사라집니다.

다음 비디오는 문제를 더 잘 설명합니다 (NavBar가 나타날 때 화면 하단 참조). http://youtu.be/epnd5ghey8g

이 동작을 어떻게 피합니까?

암호

내 지원서의 모든 활동의 아버지 :

public abstract class ImmersiveActivity extends Activity {

    @SuppressLint("NewApi")
    private void disableImmersiveMode() {
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
            getWindow().getDecorView().setSystemUiVisibility(
                    View.SYSTEM_UI_FLAG_FULLSCREEN
            );
        }
    }

    @SuppressLint("NewApi")
    private void enableImmersiveMode() {
        if (android.os.Build.VERSION.SDK_INT >= android.os.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_FULLSCREEN 
                    | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY 
                    | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
            );
        }
    }


    /**
     * Set the Immersive mode or not according to its state in the settings:
     * enabled or not.
     */
    protected void updateSystemUiVisibility() {
        // Retrieve if the Immersive mode is enabled or not.
        boolean enabled = getSharedPreferences(Util.PREF_NAME, 0).getBoolean(
                "immersive_mode_enabled", true);

        if (enabled) enableImmersiveMode();
        else disableImmersiveMode();
    }

    @Override
    public void onResume() {
        super.onResume();
        updateSystemUiVisibility();
    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        updateSystemUiVisibility();
    }

}


내 모든 사용자 지정 대화 상자는 해당 onCreate(. . .)메서드 에서이 메서드를 호출합니다 .

/**
 * Copy the visibility of the Activity that has started the dialog {@link mActivity}. If the
 * activity is in Immersive mode the dialog will be in Immersive mode too and vice versa.
 */
@SuppressLint("NewApi")
private void copySystemUiVisibility() {
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
        getWindow().getDecorView().setSystemUiVisibility(
                mActivity.getWindow().getDecorView().getSystemUiVisibility()
        );
    }
}


편집-해결책 (Beaver6813 덕분에 자세한 내용은 답변을 참조하십시오) :

내 모든 사용자 지정 대화 상자는 다음과 같은 방식으로 show 메서드를 재정의합니다.

/**
 * An hack used to show the dialogs in Immersive Mode (that is with the NavBar hidden). To
 * obtain this, the method makes the dialog not focusable before showing it, change the UI
 * visibility of the window like the owner activity of the dialog and then (after showing it)
 * makes the dialog focusable again.
 */
@Override
public void show() {
    // Set the dialog to not focusable.
    getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);

    copySystemUiVisibility();

    // Show the dialog with NavBar hidden.
    super.show();

    // Set the dialog to focusable again.
    getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
}

대화를 어떻게 표시합니까? DialogFragments를 사용합니까?
Tadeas Kriz 2014

DialogFragments를 사용하지 않고 사용자 지정 Dialog 하위 클래스를 사용합니다. developer.android.com/reference/android/app/Dialog.html show () 메서드를 호출하여 대화 상자를 표시합니다.
VanDir 2014

대화 상자가 나타나면 onWindowFocusChanged가 호출됩니다. 대화 상자가 나타날 때 활성화 된 값은 무엇입니까? 그것이 사실입니까 아니면 무언가 잘못 되었습니까?
Kevin van Mierlo 2014

Dialog 클래스의 onWindowFocusChanged (boolean hasFocus) 메서드를 의미합니까 (Activity 클래스가 아님)? 이 경우 "hasFocus"플래그는 참입니다.
VanDir 2014

5
누구든지 dialogfragments와 함께 몰입 형 모드를 사용 했습니까?
gbero

답변:


167

이 문제에 대한 많은 연구 끝에 Dialog 클래스를 분리하여 찾아내는 문제가 해결되었습니다 . 관리자에 추가하기 전에 UI 가시성을 설정 한 경우에도 대화 창이 창 관리자에 추가되면 탐색 모음이 표시됩니다. Android Immersive 예제 에서는 다음과 같이 주석 처리되었습니다.

// * Uses semi-transparent bars for the nav and status bars
// * This UI flag will *not* be cleared when the user interacts with the UI.
// When the user swipes, the bars will temporarily appear for a few seconds and then
// disappear again.

나는 이것이 우리가 여기서보고있는 것이라고 믿는다 (새롭고 포커스 가능한 창보기가 관리자에 추가 될 때 사용자 상호 작용이 트리거되고 있음).

이 문제를 어떻게 해결할 수 있습니까? 대화 상자를 만들 때 초점을 맞출 수 없도록 설정하고 (사용자 상호 작용을 트리거하지 않음) 표시된 후 초점을 맞출 수 있도록합니다.

//Here's the magic..
//Set the dialog to not focusable (makes navigation ignore us adding the window)
dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);

//Show the dialog!
dialog.show();

//Set the dialog to immersive
dialog.getWindow().getDecorView().setSystemUiVisibility(
context.getWindow().getDecorView().getSystemUiVisibility());

//Clear the not focusable flag from the window
dialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);

분명히 이것은 이상적이지는 않지만 Android 버그 인 것 같습니다. Window에 몰입 형 세트가 있는지 확인해야합니다.

내 작업 테스트 코드를 Github로 업데이트했습니다 (해키 지저분 함을 용서하십시오) . Nexus 5 에뮬레이터에서 테스트 해 보았지만 KitKat보다 적은 것으로 폭발 할 수 있지만 개념 증명 전용입니다.


3
샘플 프로젝트에 감사하지만 작동하지 않았습니다. 내 Nexus 5에서 무슨 일이 일어나는지보세요 : youtu.be/-abj3V_0zcU
VanDir 2014

문제를 조사한 후 내 답변을 업데이트했지만 어려운 문제였습니다! Nexus 5 에뮬레이터에서 테스트되었으며이 기능이 작동하기를 바랍니다.
Beaver6813 2014-04-22

2
'콘텐츠를 추가하기 전에 반드시 requestFeature ()를 호출해야합니다'라는 메시지가 표시됩니다 (활동의 활성 기능에 따라 달라진다고 생각합니다). 해결 방법 : dialog.show ()를 한 줄 위로 이동하여 SystemUiVisibility를 복사하기 전에 (하지만 초점을 맞출 수 없도록 설정 한 후) show ()가 호출되도록합니다. 그런 다음 탐색 모음을 건너 뛰지 않고도 작동합니다.
arberg

5
@ Beaver6813 EditText를 사용하고 DialogFragment에 소프트 키보드가 표시되면 작동하지 않습니다. 그것을 처리 할 제안이 없습니까?
androidcodehunter

1
안녕하세요 Beaver6813. 대화 상자에 편집 텍스트가있을 때 작동하지 않습니다. 해결책이 있습니까 ??
Navin Gupta

33

귀하의 정보를 위해 @ Beaver6813의 답변 덕분에 DialogFragment를 사용하여이 작업을 수행 할 수있었습니다.

내 DialogFragment의 onCreateView 메서드에서 다음을 추가했습니다.

    getDialog().getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
    getDialog().getWindow().getDecorView().setSystemUiVisibility(getActivity().getWindow().getDecorView().getSystemUiVisibility());

    getDialog().setOnShowListener(new DialogInterface.OnShowListener() {
        @Override
        public void onShow(DialogInterface dialog) {
            //Clear the not focusable flag from the window
            getDialog().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);

            //Update the WindowManager with the new attributes (no nicer way I know of to do this)..
            WindowManager wm = (WindowManager) getActivity().getSystemService(Context.WINDOW_SERVICE);
            wm.updateViewLayout(getDialog().getWindow().getDecorView(), getDialog().getWindow().getAttributes());
        }
    });

5
onCreateDialog ()의 Builder 패턴에서 작동합니까? 내 DialogFragments와 작업이 해킹을 얻기 위해 아직했습니다 대화 "는 내용을 추가하기 전에 호출해야합니다) requestFeature ("내 응용 프로그램 충돌
IAmKale

1
이것은 훌륭하고 DialogFragments 사용에 대한 유일한 해결책이었습니다. 정말 고맙습니다.
se22as

큰! 완벽하게 작동합니다. 많은 것을 시도했지만 이제 완벽한 몰입 형 모드를 계속합니다.
Peterdk

와우, 매력처럼 작동합니다. 감사합니다
Abdul

16

onCreateDialog () 를 사용하려면 이 클래스를 사용해보십시오. 그것은 나를 위해 꽤 잘 작동합니다 ...

public class ImmersiveDialogFragment extends DialogFragment {

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {

        AlertDialog alertDialog = new AlertDialog.Builder(getActivity())
                .setTitle("Example Dialog")
                .setMessage("Some text.")
                .create();

        // Temporarily set the dialogs window to not focusable to prevent the short
        // popup of the navigation bar.
        alertDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);

        return alertDialog;

    }

    public void showImmersive(Activity activity) {

        // Show the dialog.
        show(activity.getFragmentManager(), null);

        // It is necessary to call executePendingTransactions() on the FragmentManager
        // before hiding the navigation bar, because otherwise getWindow() would raise a
        // NullPointerException since the window was not yet created.
        getFragmentManager().executePendingTransactions();

        // Hide the navigation bar. It is important to do this after show() was called.
        // If we would do this in onCreateDialog(), we would get a requestFeature()
        // error.
        getDialog().getWindow().getDecorView().setSystemUiVisibility(
            getActivity().getWindow().getDecorView().getSystemUiVisibility()
        );

        // Make the dialogs window focusable again.
        getDialog().getWindow().clearFlags(
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
        );

    }

}

대화 상자를 표시하려면 활동에서 다음을 수행하십시오.

new ImmersiveDialogFragment().showImmersive(this);

ActionBar 메뉴가 표시 될 때 NavBar가 표시되는 것을 중지하는 방법에 대한 아이디어가 있습니까?
William

getDialog ()가 null을 반환하기 때문에 여전히 NPE를 얻습니다. 어떻게 관리 했습니까?
Anton Shkurenko 2016

8

여기에 답변을 결합하여 모든 경우에 작동하는 추상 클래스를 만들었습니다.

public abstract class ImmersiveDialogFragment extends DialogFragment {

    @Override
    public void setupDialog(Dialog dialog, int style) {
        super.setupDialog(dialog, style);

        // Make the dialog non-focusable before showing it
        dialog.getWindow().setFlags(
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
    }

    @Override
    public void show(FragmentManager manager, String tag) {
        super.show(manager, tag);
        showImmersive(manager);
    }

    @Override
    public int show(FragmentTransaction transaction, String tag) {
        int result = super.show(transaction, tag);
        showImmersive(getFragmentManager());
        return result;
    }

    private void showImmersive(FragmentManager manager) {
        // It is necessary to call executePendingTransactions() on the FragmentManager
        // before hiding the navigation bar, because otherwise getWindow() would raise a
        // NullPointerException since the window was not yet created.
        manager.executePendingTransactions();

        // Copy flags from the activity, assuming it's fullscreen.
        // It is important to do this after show() was called. If we would do this in onCreateDialog(),
        // we would get a requestFeature() error.
        getDialog().getWindow().getDecorView().setSystemUiVisibility(
                getActivity().getWindow().getDecorView().getSystemUiVisibility()
        );

        // Make the dialogs window focusable again
        getDialog().getWindow().clearFlags(
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
        );
    }
}

androidx, setupDialog 메소드가 제한된 API가 되었기 때문에 무시하지 않고 어떻게 처리 할 수 ​​있습니까?
Mingkang Pan

5

이것은 또한 대화 조각의 onDismiss 메서드를 타고 작동합니다. 그리고 해당 메서드 내에서 연결된 활동의 메서드를 호출하여 다시 전체 화면 플래그를 설정합니다.

@Override
    public void onDismiss(DialogInterface dialog) {
        super.onDismiss(dialog);
        Logger.e(TAG, "onDismiss");
        Log.e("CallBack", "CallBack");
        if (getActivity() != null &&
                getActivity() instanceof LiveStreamingActivity) {
            ((YourActivity) getActivity()).hideSystemUI();
        }
    }

그리고 활동에 다음 방법을 추가하십시오.

public void hideSystemUI() {
        // Set the IMMERSIVE flag.
        // Set the content to appear under the system bars so that the content
        // doesn't resize when the system bars hide and show.
        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_HIDE_NAVIGATION // hide nav bar
                        | View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar
                        | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
    }

1
대부분의 유용한 의견, 그것은으로 위대한 작품을 같이 AlertDialog.Builder하고.setOnDismissListener()
tomash

4

자신의 DialogFragment를 만드는 동안이 메서드를 재정의하면됩니다.

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    Dialog dialog = super.onCreateDialog(savedInstanceState);

    dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);

    return dialog;
}

대화 상자 안에서 아무 것도 누를 수 없기 때문에 이상적이지 않습니다 :(
slott

이것을 사용하면 내 대화 상자가 응답하지 않습니다. 그러나 그것이 효과가 있다고 주장한다면 나는 그것을 다시 시도 할 것입니다.
slott

@slott 당신은 지워야 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE후 대화 상자 표시
4emodan

2

나는 이것이 오래된 게시물이라는 것을 알고 있지만 내 대답은 다른 사람들에게 도움이 될 수 있습니다.

다음은 대화 상자의 몰입 형 효과에 대한 해키 수정입니다.

public static void showImmersiveDialog(final Dialog mDialog, final Activity mActivity) {
        //Set the dialog to not focusable
        mDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
        mDialog.getWindow().getDecorView().setSystemUiVisibility(setSystemUiVisibility());

        mDialog.setOnShowListener(new DialogInterface.OnShowListener() {
            @Override
            public void onShow(DialogInterface dialog) {
                //Clear the not focusable flag from the window
                mDialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);

                //Update the WindowManager with the new attributes
                WindowManager wm = (WindowManager) mActivity.getSystemService(Context.WINDOW_SERVICE);
                wm.updateViewLayout(mDialog.getWindow().getDecorView(), mDialog.getWindow().getAttributes());
            }
        });

        mDialog.getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() {
            @Override
            public void onSystemUiVisibilityChange(int visibility) {
                if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
                    mDialog.getWindow().getDecorView().setSystemUiVisibility(setSystemUiVisibility());
                }

            }
        });
    }

    public static int setSystemUiVisibility() {
        return View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_FULLSCREEN
                | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
    }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.