Android 如何在对话框中保持沉浸模式?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/22794049/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I maintain the Immersive Mode in Dialogs?
提问by VanDir
How do I maintain the new Immersive Mode when my activities display a custom Dialog?
当我的活动显示自定义对话框时,如何维护新的沉浸式模式?
I am using the code below to maintain the Immersive Mode in Dialogs, but with that solution, the NavBar appears for less than a second when I start my custom Dialog, then it disappears.
我正在使用下面的代码来维护对话框中的沉浸式模式,但是使用该解决方案,当我启动自定义对话框时,导航栏出现不到一秒钟,然后消失。
The following video explains the issue better (look at the bottom of the screen when the NavBar appears): http://youtu.be/epnd5ghey8g
以下视频更好地解释了该问题(当 NavBar 出现时,请看屏幕底部):http: //youtu.be/epnd5ghey8g
How do I avoid this behavior?
我如何避免这种行为?
CODE
代码
The father of all activities in my application:
我的应用程序中所有活动的父亲:
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();
}
}
All my custom Dialogs call this method in their onCreate(. . .)
method:
我所有的自定义对话框都在他们的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 - THE SOLUTION (thanks to Beaver6813, look his answer for more details) :
编辑 - 解决方案(感谢 Beaver6813,查看他的答案以获取更多详细信息):
All my custom dialogs override the show method in this way:
我所有的自定义对话框都以这种方式覆盖了 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);
}
回答by Beaver6813
After a lot of research into the issue there is a hacky fix for this, which involved tearing apart the Dialogclass to find. The navigation bar is shown when the dialog window is added to the Window Manager even if you set the UI visibility before adding it to the manager. In the Android Immersive exampleit's commented that:
在对该问题进行了大量研究之后,有一个针对此问题的hacky 解决方案,其中涉及拆分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.
I believe that's what we're seeing here (that a user-interaction is being triggered when a new, focusable, window view is added to the manager).
我相信这就是我们在这里看到的(当一个新的、可聚焦的、窗口视图被添加到管理器时,用户交互被触发)。
How can we work around this? Make the Dialog non-focusable when we create it (so we don't trigger a user-interaction) and then make it focusable after it's displayed.
我们如何解决这个问题?在创建对话框时使其不可聚焦(因此我们不会触发用户交互),然后在显示后使其可聚焦。
//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);
Clearly this is not ideal but it seems to be an Android bug, they should check if the Window has immersive set.
显然这并不理想,但它似乎是一个 Android 错误,他们应该检查 Window 是否具有沉浸式设置。
I've updated my working test code (forgive the hacky messiness) to Github. I've tested on the Nexus 5 emulator, it will probably blow up with anything less than KitKat but its for proof-of-concept only.
我已经将我的工作测试代码(请原谅我的乱七八糟)更新到Github。我已经在 Nexus 5 模拟器上进行了测试,它可能会因为 KitKat 以外的任何东西而崩溃,但它仅用于概念验证。
回答by gbero
For your information, thanks to @Beaver6813's answer, I've been able to get this working using DialogFragment.
供您参考,感谢@Beaver6813 的回答,我已经能够使用 DialogFragment 完成这项工作。
in the onCreateView method of my DialogFragment, I've just added the following :
在我的 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());
}
});
回答by 2r4
If you want to use onCreateDialog(), try this class. It works pretty well for me...
如果你想使用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
);
}
}
To show the dialog, do the following in your activity...
要显示对话框,请在您的活动中执行以下操作...
new ImmersiveDialogFragment().showImmersive(this);
回答by 4emodan
Combining the answers here I made an abstract class that works in all cases:
结合这里的答案,我创建了一个适用于所有情况的抽象类:
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
);
}
}
回答by Salil Kaul
This also works over ride the onDismiss method of your dialog fragment . And within that method call the method of the activity to which it is attached to again set the full screen flags .
这也适用于您的对话框片段的 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();
}
}
And in your activity add this method :
并在您的活动中添加此方法:
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);
}
回答by Dawid Drozd
Whean you are creating your own DialogFragment you need only override this method.
当您创建自己的 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;
}
回答by Rahul Parihar
I know this is old post, but my answer may help others.
我知道这是旧帖子,但我的回答可能对其他人有所帮助。
Below is the hacky fix for Immersive effect in Dialogs:
以下是对话中沉浸式效果的黑客修复:
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;
}