Как сохранить режим Immersive в диалогах?

Как сохранить новый режим Immersive, когда мои действия отображают пользовательский диалог?

Я использую этот код для поддержки Immersive Mode в Dialogs, но с этим решением NavBar появляется менее чем за секунду, когда я запускаю свой пользовательский диалог, и он исчезает.

Вот видео, которое лучше объясняет проблему (посмотрите в нижней части экрана при появлении 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: 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()); } } 


EDIT – РЕШЕНИЕ (спасибо 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); } 

Solutions Collecting From Web of "Как сохранить режим Immersive в диалогах?"

После большого количества исследований в этой проблеме есть хакерское исправление, которое связано с разрывом класса Dialog для поиска. Панель навигации отображается, когда диалоговое окно добавляется в диспетчер окон, даже если вы устанавливаете видимость пользовательского интерфейса перед добавлением его в диспетчер. В примере 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. 

Я считаю, что это то, что мы видим здесь (что взаимодействие с пользователем запускается, когда к диспетчеру добавляется новое, настраиваемое окно).

Как мы можем обойти это? Сделайте Dialog неосведомленным, когда мы его создадим (поэтому мы не запускаем взаимодействие с пользователем), а затем сделаем его фокусируемым после его отображения.

 //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, они должны проверить, имеет ли Окно immersive set.

Я обновил свой рабочий тестовый код (простите хакерскую беспорядок) до Github . Я тестировал эмулятор Nexus 5, он, вероятно, взорвется чем-то меньшим, чем KitKat, но только для доказательства концепции.

Если вы хотите использовать 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); 

Для вашей информации, благодаря ответу @ Beaver6813, я смог получить эту работу с помощью DialogFragment.

В методе onCreateView моего DialogFragment, я только что добавил следующее:

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

Вы создаете свой собственный 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; } 

Объединив ответы здесь, я сделал абстрактный класс, который работает во всех случаях:

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

Я знаю, что это старый пост, но мой ответ может помочь другим.

Ниже приводится хакерское исправление для эффекта Immersive в диалогах:

 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; } 

Это также работает над методом 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); }