在纯Android原生系统中隐藏导航栏

5
我看到过一些关于如何通过Java隐藏Android应用程序中的导航栏的文章。然而,我想知道的是,如何通过纯Android C++本地活动应用程序来移除导航栏,以实现全屏应用程序(游戏)(完全没有Java!)。从Android清单的全屏功能可以隐藏顶部栏,但导航栏仍然可见。
这是我希望移除的导航栏
我已经查阅了书籍,但没有找到任何有用的信息。原生活动的实际文档并不存在,谷歌搜索也无结果。只有一些头文件中的注释,它们是微小的注释,甚至没有帮助。似乎关于纯C++ Android应用程序的主题是一个黑洞,然而许多市场上推出的游戏都是用C++编写的。
我还尝试设置:
AConfiguration_setNavHidden(m_app->config, ACONFIGURATION_NAVHIDDEN_YES);

但它似乎没有任何作用,事实上,所有的AConfiguration_setXXX似乎都没有作用。也许我在错误的位置调用了它?我一直在APP_CMD_INIT_WINDOW期间创建窗口后调用它。我应该在哪里调用这个函数呢?


据我所知,您无法在不调用Java API的情况下完成此操作。您可能需要通过JNI从纯C++代码中使用GetMethodID相关函数调用Java方法。根据快速浏览文档,AConfiguration可能不是您所需的——它用于编辑Java“Configuration”对象。您需要获取您的“Activity”的jobject,获取JNIEnv并使用它来调用您任务所需的Java方法。 - user1643723
我今天刚参加了一次面试,我提到了这个问题,显然他们也遇到了同样的问题,但是他们用纯C++解决了它。在我离开之前,我忘记问他们是如何做到的,但听起来似乎是可能的。如果找不到答案,我的最后选择是部分使用Java,像一个正常人一样。感谢您的建议! - MightyMoo
使用C++通过JNI调用Java方法是“纯C++”,因为您不编写任何Java代码,只是使用JVM C++ API来调用Java方法。 - user1643723
是的,我明白“纯C++”仍在使用Java调用。我只需要隐藏导航的秘密配方。我知道有一个,一定有!我所说的部分使用Java作为最后的手段,是指在Java上隐藏导航。 - MightyMoo
2个回答

6

我发现了这段代码,它可以工作(需要API级别> 19)。只需在main()函数的开头调用该函数即可。

void AutoHideNavBar(struct android_app* state)
{
    JNIEnv* env{};
    state->activity->vm->AttachCurrentThread(&env, NULL);

    jclass activityClass = env->FindClass("android/app/NativeActivity");
    jmethodID getWindow = env->GetMethodID(activityClass, "getWindow", "()Landroid/view/Window;");

    jclass windowClass = env->FindClass("android/view/Window");
    jmethodID getDecorView = env->GetMethodID(windowClass, "getDecorView", "()Landroid/view/View;");

    jclass viewClass = env->FindClass("android/view/View");
    jmethodID setSystemUiVisibility = env->GetMethodID(viewClass, "setSystemUiVisibility", "(I)V");

    jobject window = env->CallObjectMethod(state->activity->clazz, getWindow);

    jobject decorView = env->CallObjectMethod(window, getDecorView);

    jfieldID flagFullscreenID = env->GetStaticFieldID(viewClass, "SYSTEM_UI_FLAG_FULLSCREEN", "I");
    jfieldID flagHideNavigationID = env->GetStaticFieldID(viewClass, "SYSTEM_UI_FLAG_HIDE_NAVIGATION", "I");
    jfieldID flagImmersiveStickyID = env->GetStaticFieldID(viewClass, "SYSTEM_UI_FLAG_IMMERSIVE_STICKY", "I");

    const int flagFullscreen = env->GetStaticIntField(viewClass, flagFullscreenID);
    const int flagHideNavigation = env->GetStaticIntField(viewClass, flagHideNavigationID);
    const int flagImmersiveSticky = env->GetStaticIntField(viewClass, flagImmersiveStickyID);
    const int flag = flagFullscreen | flagHideNavigation | flagImmersiveSticky;

    env->CallVoidMethod(decorView, setSystemUiVisibility, flag);

    state->activity->vm->DetachCurrentThread();
}

1
这对我非常有效,而且不需要添加JVM代码。这应该是被接受的答案! - Jon Watte

5

如果有人感兴趣,我在研究微软的一个茶壶示例时找到了答案。以下是我的结论:

public class NameOfActivity extends NativeActivity {

void setImmersiveSticky() {
    View decorView = getWindow().getDecorView();
    decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN
            | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
            | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
            | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
            | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
            | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    setContentView(R.layout.activity_main);

    int SDK_INT = android.os.Build.VERSION.SDK_INT;
    if (SDK_INT >= 19) {
        setImmersiveSticky();

        View decorView = getWindow().getDecorView();
        decorView.setOnSystemUiVisibilityChangeListener
                (new View.OnSystemUiVisibilityChangeListener() {
                    @Override
                    public void onSystemUiVisibilityChange(int visibility) {
                        setImmersiveSticky();
                    }
                });
    }
    super.onCreate(savedInstanceState);
}

@Override
protected void onResume() {
    //Hide toolbar
    int SDK_INT = android.os.Build.VERSION.SDK_INT;
    if (SDK_INT >= 11 && SDK_INT < 14) {
        getWindow().getDecorView().setSystemUiVisibility(View.STATUS_BAR_HIDDEN);
    } else if (SDK_INT >= 14 && SDK_INT < 19) {
        getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LOW_PROFILE);
    } else if (SDK_INT >= 19) {
        setImmersiveSticky();
    }
    super.onResume();
}

纯C++ Android开发仍然使用Java,它从Android SDK目录内部获取Java。您可以通过一些额外的调整扩展NativeActivity。在清单文件中,您只需要执行以下操作:

    android:hasCode="true"

然后最重要的部分是更改活动名称从

        android:name="android.app.NativeActivity"

to

        android:name="com.example.package.NameOfActivity"

它仍将调用Android主要功能并设置它,就像常规本地活动一样,但现在它为您提供了完全的全屏。希望这能帮助到某个人。我花了几天时间寻找答案,这就是我能想到的!祝你好运!

我在sfml中找到了这个,它是完整的c++代码,并考虑了旧版本的api:https://github.com/SFML/SFML/blob/fae3b65f0567f87fa9925cd42d28df15eb69e79c/src/SFML/Main/MainAndroid.cpp#L139 - user2591935
非常感谢,我真的希望这对我也有效 :) - Viktor Sehr
是的,扩展NativeActivity是解决这个问题的方法。这也是Google在这里所做的:https://github.com/googlesamples/android-ndk/tree/master/teapots/classic-teapot - Slion
这怎么成为被接受的答案了?它使用了Java,而请求是不要使用Java! - Jon Watte

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接