Как поддерживать режим погружения в диалогах?
как я могу поддерживать новый режим погружения, когда мои действия отображают пользовательский Диалог?
Я использую этот код для поддержания режима погружения в диалогах, но с этим решением навигационная панель появляется менее чем на секунду, когда я запускаю свой пользовательский диалог, а затем исчезает.
вот видео, которое лучше объясняет проблему (посмотрите в нижней части экрана, когда появится навигационная панель):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);
}
7 ответов:
после многих исследований в этой проблеме есть хакерское исправление для этого, которое включало разрывание класс найти. Панель навигации отображается при добавлении диалогового окна в Диспетчер окон, даже если вы задали видимость пользовательского интерфейса перед его добавлением в диспетчер. В 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, они должны проверить, если окно имеет захватывающий набор.
я обновил свой рабочий тестовый код (простите за халтуру) до Github. Я проверил на эмулятор Nexus 5, он, вероятно, взорвется с чем-то меньшим, чем KitKat, но только для доказательства концепции.
для вашей информации, благодаря ответу @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()); } });
Если вы хотите использовать 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);
объединение ответов здесь я сделал абстрактный класс, который работает во всех случаях:
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 ); } }
Whean вы создаете свой собственный 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; }
Это также работает над ездить 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); }
Я знаю, это старый пост, но мой ответ может помочь другим.
Ниже приведено хакерское исправление для эффекта погружения в диалогах:
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; }