显示 DialogFragment 时保持沉浸式模式

Maintain Immersive mode when DialogFragment is Shown

我有一个 Android 使用 Fragments 制作的应用程序

我使用以下代码隐藏屏幕顶部和底部的栏。

 @Override
protected void onResume() {
    super.onResume();
    isInBackground = false;

    if(null == getFragmentManager().findFragmentById(R.id.content_container))
    {
        getFragmentManager().beginTransaction().add(R.id.content_container,new PresenterFragment(), PresenterFragment.FRAG_TAG).commit();
    }

    if(Build.VERSION.SDK_INT >=19)
    {
        View decorView = getWindow().getDecorView();
        int uiOptions = 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;
        decorView.setSystemUiVisibility(uiOptions);
        decorView.setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() {
            @Override
            public void onSystemUiVisibilityChange(int visibility) {
                View decorView = getWindow().getDecorView();
                int uiOptions = 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;
                decorView.setSystemUiVisibility(uiOptions);
            }
        });
    }
}

当显示条形图的软键盘显示时,我可以接受它,因为当键盘处于 dismissed.However 时它们会隐藏,如果在显示软键盘时显示对话框片段,那么当键盘和对话框片段被关闭,它们的栏保留在应用程序的顶部。

我的 3 个问题是

是否可以停止软键盘以更改 UI 模式?

是否可以阻止 DialogsFraments 的显示改变 UI 模式?

编辑:我曾经在下面的代码中查看是否显示了键盘

public static boolean isKeyBoardShown(){
    InputMethodManager imm = (InputMethodManager)currentActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
    if (imm.isAcceptingText()) {
        return true;
    } else {
        return false;
    }
}

-> 我知道 activity 中的对话框有一个解决方法,但我看不到或修改代码以在 DialogFragment

中工作

如果两者都不可能,为什么当同时显示键盘和 DialogFrament 时应用会卡在错误的 UI 模式?

Is it possible to stop the softkeyboard for changing the UI mode?

可能

Is it possible to stop the showing of DialogsFraments from changing the UI mode?

是的,只要你试着理解你在做什么

If neither is possible why does the app get stuck in the wrong UI mode when there is both a shown keyboard and DialogFrament?

因为问题 2 的答案

现在这是您的整体解决方案

current/Focused 视图是 os 从中获取 UI 可见性设置的对象,当视图被遮挡或相对于 z 顺序不在顶部时 setSystemUiVisibility() 设置为默认值。 So 而不是尝试绕过

View decorView = getWindow().getCurrentFocus() != null ? 
                getWindow().getCurrentFocus() :getWindow().getDecorView();

并且每当您的 DialogFragment 被关闭时,请检查以上行并重新调用您的可访问性 ui 代码;因为....猜到这一点很清楚

1.解决方案说明。

我从 android api 文档中引用了以下内容。

Using Immersive Full-Screen Mode

When immersive full-screen mode is enabled, your activity continues to receive all touch events. The user can reveal the system bars with an inward swipe along the region where the system bars normally appear. This clears the SYSTEM_UI_FLAG_HIDE_NAVIGATION flag (and the SYSTEM_UI_FLAG_FULLSCREEN flag, if applied) so the system bars become visible. This also triggers your View.OnSystemUiVisibilityChangeListener, if set.

首先,使用粘性沉浸时不需要OnSystemUiVisibilityChangeListener

However, if you'd like the system bars to automatically hide again after a few moments, you can instead use the SYSTEM_UI_FLAG_IMMERSIVE_STICKY flag. Note that the "sticky" version of the flag doesn't trigger any listeners, as system bars temporarily shown in this mode are in a transient state.

recommendations for using sticky/non sticky immersion:

If you're building a truly immersive app, where you expect users to interact near the edges of the screen and you don't expect them to need frequent access to the system UI, use the IMMERSIVE_STICKY flag in conjunction with SYSTEM_UI_FLAG_FULLSCREEN and SYSTEM_UI_FLAG_HIDE_NAVIGATION. For example, this approach might be suitable for a game or a drawing app.

但是,你提到用户需要键盘,所以我建议:
Use Non-Sticky Immersion

If you're building a book reader, news reader, or a magazine, use the IMMERSIVE flag in conjunction with SYSTEM_UI_FLAG_FULLSCREEN and SYSTEM_UI_FLAG_HIDE_NAVIGATION. Because users may want to access the action bar and other UI controls somewhat frequently, but not be bothered with any UI elements while flipping through content, IMMERSIVE is a good option for this use case.


2。解决方案

我的解决方案是在片段的 onActivityCreated 中设置视图 ui。

我的示例取自 ImmersiveModeFragment.java 示例。

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    final View decorView = getActivity().getWindow().getDecorView();
    decorView.setOnSystemUiVisibilityChangeListener(
            new View.OnSystemUiVisibilityChangeListener() {
                @Override
                public void onSystemUiVisibilityChange(int i) {
                    hideSystemUI();
                }
            });
}

创建一个单独的方法来管理您从 OnSystemUiVisibilityChangeListener()

调用的 ui

取自此处non sticky immersion

private 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.
    mDecorView.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);
}

然后您可以再次调用此方法onResume

onResume(){
    hideSystemUI();
}

3。替代解决方案。

如果粘性沉浸是您真正想要的,您需要尝试不同的方法。

For sticky immersion

@Override
public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    if (hasFocus) {
        decorView.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
            | View.SYSTEM_UI_FLAG_FULLSCREEN
            | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);}
}

希望这能解决您的问题。

此修复以及与软键盘相关的另一个修复包含在该库的 DialogFragment class 中。只需扩展它而不是标准库:

https://github.com/marksalpeter/contract-fragment

该库的主要目的是向利用 parent/child 片段关系的 Fragments 和 DialogFragments 添加委托功能,但您可以只使用错误修复或 copy/paste 文件

我知道这已经过时了,但我为此苦苦挣扎了一段时间,因为我有 dialogFragments 和我的 navigationView 中的下拉菜单,它一直在不必要地显示系统 UI。以下是我所做的最终起作用的事情(前两件我发现了很多地方):

在我AndroidManifest.xml上申请

android:theme="@style/AppTheme.NoActionBar"

在我的activity

View decorView = getWindow().getDecorView();
int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
decorView.setSystemUiVisibility(uiOptions);

最终成为我的关键的是我的 styles.xml

<resources>
    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowFullscreen">true</item>
    </style>
</resources>

在 Dialog 或 BottomSheetDialogFragment 中,您必须实施适合我的解决方案。

第 1 步:

在你的对话框或 BottomSheetDialog 中,在 onActivityCreated 方法中写入这段代码,

override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
var viewParent = view
while (viewParent is View) {
    viewParent.fitsSystemWindows = false
    viewParent.setOnApplyWindowInsetsListener { _, insets -> insets }
    viewParent = viewParent.parent as View?
 }
}

第 2 步:此外,覆盖以下方法:

override fun setupDialog(dialog: Dialog, style: Int) {
super.setupDialog(dialog, style)
dialog?.window?.setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)

}

现在看看魔术:)