Unity3D:Unity5.3以上版本Android状态栏的显示

大腿Plus 2017年5月24日09:58:46Unity3D之项目开发71,284阅读模式

Unity在5.3以上的版本里把状态栏的显隐功能去掉了,但是iOS的没有去掉,果然安卓不是亲儿子啊,前两天做项目,需要这个功能,我开发用的是5.6版本,怎么找都没找到这个功能,还以为放在其他地方了呢,还特意去官网看了Unity的更新内容,结果百度了一下,果然5.3以后,安卓就没有这个功能了。后来在网上看到有人写了两个代码,就把这个问题搞定了,我稍微整理了一下,下面放代码。文章源自大腿Plus-https://www.shijunzh.com/archives/513

using System;
using System.Collections.Generic;
using UnityEngine;

public class AndroidStatusBar
{
    // Enums
    public enum States
    {
        Unknown,
        Visible,
        VisibleOverContent,
        TranslucentOverContent,
        Hidden,
    }

    // Constants
    private const uint DEFAULT_BACKGROUND_COLOR = 0xff000000;



#if UNITY_ANDROID
    // Original Android flags
    private const int VIEW_SYSTEM_UI_FLAG_VISIBLE = 0;                                        // Added in API 14 (Android 4.0.x): Status bar visible (the default)
    private const int VIEW_SYSTEM_UI_FLAG_LOW_PROFILE = 1;                                    // Added in API 14 (Android 4.0.x): Low profile for games, book readers, and video players; the status bar and/or navigation icons are dimmed out (if visible)
    private const int VIEW_SYSTEM_UI_FLAG_HIDE_NAVIGATION = 2;                                // Added in API 14 (Android 4.0.x): Hides all navigation. Cleared when theres any user interaction.
    private const int VIEW_SYSTEM_UI_FLAG_FULLSCREEN = 4;                                     // Added in API 16 (Android 4.1.x): Hides status bar. Does nothing in Unity (already hidden if "status bar hidden" is checked)
    private const int VIEW_SYSTEM_UI_FLAG_LAYOUT_STABLE = 256;                                // Added in API 16 (Android 4.1.x): ?
    private const int VIEW_SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 512;                       // Added in API 16 (Android 4.1.x): like HIDE_NAVIGATION, but for layouts? it causes the layout to be drawn like that, even if the whole view isn't (to avoid artifacts in animation)
    private const int VIEW_SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 1024;                           // Added in API 16 (Android 4.1.x): like FULLSCREEN, but for layouts? it causes the layout to be drawn like that, even if the whole view isn't (to avoid artifacts in animation)
    private const int VIEW_SYSTEM_UI_FLAG_IMMERSIVE = 2048;                                   // Added in API 19 (Android 4.4): like HIDE_NAVIGATION, but interactive (it's a modifier for HIDE_NAVIGATION, needs to be used with it)
    private const int VIEW_SYSTEM_UI_FLAG_IMMERSIVE_STICKY = 4096;                            // Added in API 19 (Android 4.4): tells that HIDE_NAVIGATION and FULSCREEN are interactive (also just a modifier)

    private static int WINDOW_FLAG_FULLSCREEN = 0x00000400;
    private static int WINDOW_FLAG_FORCE_NOT_FULLSCREEN = 0x00000800;
    private static int WINDOW_FLAG_LAYOUT_IN_SCREEN = 0x00000100;
    private static int WINDOW_FLAG_TRANSLUCENT_STATUS = 0x04000000;
    private static int WINDOW_FLAG_TRANSLUCENT_NAVIGATION = 0x08000000;
    private static int WINDOW_FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS = -2147483648; // 0x80000000; // Added in API 21 (Android 5.0): tells the Window is responsible for drawing the background for the system bars. If set, the system bars are drawn with a transparent background and the corresponding areas in this window are filled with the colors specified in getStatusBarColor() and getNavigationBarColor()

    // Current values
    private static int systemUiVisibilityValue;
    private static int flagsValue;
#endif

    //Properties
    private static States _statusBarState;
    private static uint _statusBarColor = DEFAULT_BACKGROUND_COLOR;
    private static bool _isStatusBarTranslucent; 
    private static bool _dimmed;

    static AndroidStatusBar()
    {
        applyUIStates();
        applyUIColors();
    }

    private static void applyUIStates()
    {
        if (Application.platform == RuntimePlatform.Android)
        {
            int newFlagsValue = 0;
            int newSystemUiVisibilityValue = 0;

            // Apply dim values
            if (_dimmed) newSystemUiVisibilityValue |= VIEW_SYSTEM_UI_FLAG_LOW_PROFILE;

            // Apply color values
            if (_statusBarColor != DEFAULT_BACKGROUND_COLOR) newFlagsValue |= WINDOW_FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;

            // Apply status bar values
            switch (_statusBarState)
            {
                case States.Visible:
                    _isStatusBarTranslucent = false;
                    newFlagsValue |= WINDOW_FLAG_FORCE_NOT_FULLSCREEN;
                    break;
                case States.VisibleOverContent:
                    _isStatusBarTranslucent = false;
                    newFlagsValue |= WINDOW_FLAG_FORCE_NOT_FULLSCREEN | WINDOW_FLAG_LAYOUT_IN_SCREEN;
                    newSystemUiVisibilityValue |= VIEW_SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
                    break;
                case States.TranslucentOverContent:
                    _isStatusBarTranslucent = true;
                    newFlagsValue |= WINDOW_FLAG_FORCE_NOT_FULLSCREEN | WINDOW_FLAG_LAYOUT_IN_SCREEN | WINDOW_FLAG_TRANSLUCENT_STATUS;
                    newSystemUiVisibilityValue |= VIEW_SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
                    break;
                case States.Hidden:
                    newFlagsValue |= WINDOW_FLAG_FULLSCREEN | WINDOW_FLAG_LAYOUT_IN_SCREEN;
                    if (_isStatusBarTranslucent) newFlagsValue |= WINDOW_FLAG_TRANSLUCENT_STATUS;
                    break;
            }
            if (Screen.fullScreen) Screen.fullScreen = false;

            // Applies everything natively
            setFlags(newFlagsValue);
            setSystemUiVisibility(newSystemUiVisibilityValue);

        }
    }

    private static void applyUIColors()
    {
        if (Application.platform == RuntimePlatform.Android)
        {
            runOnAndroidUiThread(applyUIColorsAndroidInThread);
        }
    }

#if UNITY_ANDROID
    private static void runOnAndroidUiThread(Action target)
    {
        using (var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
        {
            using (var activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity"))
            {
                activity.Call("runOnUiThread", new AndroidJavaRunnable(target));
            }
        }
    }

    private static void setSystemUiVisibility(int value)
    {
        if (systemUiVisibilityValue != value)
        {
            systemUiVisibilityValue = value;
            runOnAndroidUiThread(setSystemUiVisibilityInThread);
        }
    }

    private static void setSystemUiVisibilityInThread()
    {
        using (var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
        {
            using (var activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity"))
            {
                using (var window = activity.Call<AndroidJavaObject>("getWindow"))
                {
                    using (var view = window.Call<AndroidJavaObject>("getDecorView"))
                    {
                        view.Call("setSystemUiVisibility", systemUiVisibilityValue);
                    }
                }
            }
        }
    }

    private static void setFlags(int value)
    {
        if (flagsValue != value)
        {
            flagsValue = value;
            runOnAndroidUiThread(setFlagsInThread);
        }
    }

    private static void setFlagsInThread()
    {
        using (var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
        {
            using (var activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity"))
            {
                using (var window = activity.Call<AndroidJavaObject>("getWindow"))
                {
                    window.Call("setFlags", flagsValue, -1); // (int)0x7FFFFFFF
                }
            }
        }
    }

    private static void applyUIColorsAndroidInThread()
    {
        using (var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
        {
            using (var activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity"))
            {
                using (var window = activity.Call<AndroidJavaObject>("getWindow"))
                {
                    window.Call("setStatusBarColor", unchecked((int)_statusBarColor));
                }
            }
        }
    }
#endif
    public static States statusBarState
    {
        get { return _statusBarState; }
        set
        {
            if (_statusBarState != value)
            {
                _statusBarState = value;
                applyUIStates();
            }
        }
    }

    public static bool dimmed
    {
        get { return _dimmed; }
        set
        {
            if (_dimmed != value)
            {
                _dimmed = value;
                applyUIStates();
            }
        }
    }

    public static uint statusBarColor
    {
        get { return _statusBarColor; }
        set
        {
            if (_statusBarColor != value)
            {
                _statusBarColor = value;
                applyUIColors();
                applyUIStates();
            }
        }
    }
}

上面的代码是调用了Unity打包成安卓后自动生成的UnityPlayerActivity类里面的状态栏显示隐藏的方法,所以不需要任何其他的安卓的jar包。文章源自大腿Plus-https://www.shijunzh.com/archives/513

下面是使用方法:文章源自大腿Plus-https://www.shijunzh.com/archives/513

using UnityEngine;
using System.Collections;

public class AppStatusBar : MonoBehaviour
{
    [Tooltip("状态栏是否显示状态及通知")]
    public bool statusBar;
    [Tooltip("状态栏样式")]
    public AndroidStatusBar.States states = AndroidStatusBar.States.Visible;
    // Use this for initialization
    void Awake()
    {
        if (Application.platform == RuntimePlatform.Android)
        {
            AndroidStatusBar.dimmed = !statusBar;
            //当AndroidStatusBar.dimmed=false时,状态栏显示所有状态及通知图标
            //当AndroidStatusBar.dimmed=true时,状态栏仅电量和时间,不显示其他状态及通知

            ////显示状态栏,占用屏幕最上方的一部分像素
            //AndroidStatusBar.statusBarState = AndroidStatusBar.States.Visible;

            ////悬浮显示状态栏,不占用屏幕像素
            //AndroidStatusBar.statusBarState = AndroidStatusBar.States.VisibleOverContent;

            ////透明悬浮显示状态栏,不占用屏幕像素
            //AndroidStatusBar.statusBarState = AndroidStatusBar.States.TranslucentOverContent;

            ////隐藏状态栏
            //AndroidStatusBar.statusBarState = AndroidStatusBar.States.Hidden;

            AndroidStatusBar.statusBarState = states;
        }
    }
}

可以直接将上面的代码拖到任意一个GameObject上,但是只有发布出来后才能看到效果。文章源自大腿Plus-https://www.shijunzh.com/archives/513

大腿Plus
  • 本文由 发表于 2017年5月24日09:58:46
  • 转载请务必保留本文链接:https://www.shijunzh.com/archives/513

发表评论

评论:7   其中:访客  4   博主  3
    • Rex
      Rex 1

      你好,我用这个出现一个问题, 5.3的时候mUnityPlayer.setOnSystemUiVisibilityChangeListener是好着的,5.5之后我在安卓部分添加了 mUnityPlayer.setOnSystemUiVisibilityChangeListener 函数后,发现这个函数并不能完全监听到所有的消息,有时候会丢消息,导致我的显示有时候会有问题, 我可以留个QQ交流下 493213418

        • 大腿Plus
          大腿Plus

          @ Rex 这个可能和Unity版本的问题,我记得在哪个版本里面Unity的Activity里面的内容发生了一些变化,还用之前的方法可能会存在问题。

        • Rex
          Rex 1

          哦,那有没有什么参考可以说下,这部分内容用的人比较少~~ 困扰很久了~

            • 大腿Plus
              大腿Plus

              @ Rex 这个我也没怎么用过,可以问一下安卓原生开发的人,好久没有碰安卓原生的东西了,我也不太清楚了。

                • Rex
                  Rex 1

                  @ 大腿Plus 好的,谢谢了

              • ‭
                0

                你好 这种方法很实用 我想问一下 有什么方法可以获取状态栏的高度吗 因为不同机型他的状态栏高度不一样 如果有的话 可以尽量说详细点

                  • 大腿Plus
                    大腿Plus

                    @ 上面有个状态栏样式的那个可以控制占用上方屏幕的像素,不用去获取状态栏宽度,只要设置好UI锚点应该不会有什么问题