【问题标题】:Android M Light and Dark status bar programmatically - how to make it dark again?Android M Light and Dark状态栏以编程方式 - 如何使其再次变暗?
【发布时间】:2016-10-06 23:17:36
【问题描述】:

在 Android M 中,我们可以使状态栏图标变暗。为此,我们可以在主题的 xml 中指定属性:

<item name="android:windowLightStatusBar">true</item>

或者我们在运行时使用以下代码设置它:

View someView = findViewById(R.id.some_view);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    someView.setSystemUiVisibility(someView.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}

而且它实际上工作正常。但问题是如何在运行时正确地将状态栏模式设置为暗?

我已经尝试过这些变体:

// Makes status bar mode dark, but also hides it along with all navigation views. 
someView.setSystemUiVisibility(someView.getSystemUiVisibility() | ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);

// Does nothing 
someView.setSystemUiVisibility(someView.getSystemUiVisibility() & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);

// Also does nothing 
someView.setSystemUiVisibility(someView.getSystemUiVisibility() ^ View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);

那么如何才能以正确的方式完成呢?

【问题讨论】:

    标签: android user-interface android-6.0-marshmallow android-statusbar


    【解决方案1】:

    @Aracem 发布的解决方案是有效的,但是如果您尝试更改状态栏的背景颜色,则不起作用。就我而言,我按以下方式进行操作。

    启用 windowLightStatusBar(以编程方式,例如在 Utils 类中):

     public static void setLightStatusBar(View view,Activity activity){
    
    
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    
                    int flags = view.getSystemUiVisibility();
                    flags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
                    view.setSystemUiVisibility(flags);
                    activity.getWindow().setStatusBarColor(Color.WHITE); 
                }
    }
    

    将StatusBar恢复到之前的状态:

      public static void clearLightStatusBar(Activity activity) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                Window window = activity.getWindow();
                window.setStatusBarColor(ContextCompat
                     .getColor(activity,R.color.colorPrimaryDark)); 
            }
        }
    

    恢复状态栏的颜色就足够了,它还恢复了图标的颜色。 非常重要:在 setLightStatusBar(View view..) 中使用的视图从屏幕上消失(即 view.getVisibility()==GONE|INVISIBLE)之前,不会发生恢复操作。

    【讨论】:

    • 是否可以在视图仍在屏幕上时恢复灯光状态栏。例如,将主题从浅色模式更改为深色模式时?
    • P.S.,为什么没有立即进行还原操作?为什么它与视图相关联?
    • 如何管理低于Build.VERSION_CODES.M的设备版本
    • setSystemUiVisibility 在 Api 30+ 中已弃用
    【解决方案2】:

    根据 Nick Butcher 的项目“Plaid”

    public static void clearLightStatusBar(@NonNull View view) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            int flags = view.getSystemUiVisibility();
            flags &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
            view.setSystemUiVisibility(flags);
        }
    }
    

    你可以找到这个文件here

    【讨论】:

    • 为什么 setSystemUiVisibility() 是 View 类的方法?
    • @capt.swag 我想是View view = window.getDecorView();
    • @imknown 应该是window.getDecorView()。如果您尝试在片段或 Activity 中的随机视图上设置它,它将不起作用。
    • setSystemUiVisibility 在 Api 30+ 中已弃用
    【解决方案3】:

    我基于@Aracem 和@Carlos Hernández Gil 但我认为如果我们使用bitwise XOR(Java 中的^ 运算符)会很容易理解

    private void setLightStatusBar(Activity activity) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            int flags = activity.getWindow().getDecorView().getSystemUiVisibility(); // get current flag
            flags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;   // add LIGHT_STATUS_BAR to flag
            activity.getWindow().getDecorView().setSystemUiVisibility(flags); 
            activity.getWindow().setStatusBarColor(Color.GRAY); // optional
        }
    }
    
    private void clearLightStatusBar(Activity activity) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            int flags = activity.getWindow().getDecorView().getSystemUiVisibility(); // get current flag
            flags = flags ^ View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; // use XOR here for remove LIGHT_STATUS_BAR from flags
            activity.getWindow().getDecorView().setSystemUiVisibility(flags);
            activity.getWindow().setStatusBarColor(Color.GREEN); // optional
        }
    }
    

    解释

    首先看SYSTEM_UI_FLAG_LIGHT_STATUS_BARsetSystemUiVisibility

    /**
     * Flag for {@link #setSystemUiVisibility(int)}: Requests the status bar to draw in a mode that
     * is compatible with light status bar backgrounds.
     */
    public static final int SYSTEM_UI_FLAG_LIGHT_STATUS_BAR = 0x00002000;
    
    public void setSystemUiVisibility(int visibility) {
        if (visibility != mSystemUiVisibility) {
            mSystemUiVisibility = visibility;
            ...
        }
    }
    

    我觉得下面两行代码很难理解

    flags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; // for set light status bar
    flags = flags ^ View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; // for clear light status bar
    

    乍一看,我只是觉得我们可以使用简单的like

    flags = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; // for set light status bar
    flags = 0; // for clear light status bar (0 <=> LIGHT_STATUS_BAR <=> default systemUiVisibility)
    

    但我们应该使用|^ 因为
    例如,我们要将状态栏和导航栏都设置为亮,那么我们将使用

    flags = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR | View.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
    activity.getWindow().getDecorView().setSystemUiVisibility(flags);
    

    当我们不想让状态栏变亮时,我们可以使用

    flags = View.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
    activity.getWindow().getDecorView().setSystemUiVisibility(flags);
    

    flags = activity.getWindow().getDecorView().getSystemUiVisibility();
    flags = flags ^ View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; 
    activity.getWindow().getDecorView().setSystemUiVisibility(flags);
    

    要了解更多为什么我们使用|^,我认为下面的教程可能会有所帮助 https://medium.com/@JakobUlbrich/flag-attributes-in-android-how-to-use-them-ac4ec8aee7d1 这是我的理解。希望对您有所帮助

    【讨论】:

    • 如果当前未设置 XOR,XOR 也会启用该标志。如果你想确保它是unset,你应该使用&amp; ~
    • @IanMacDonald 为什么 XOR 可以启用我们当前未设置的标志?能举个例子吗?
    • 如果我想为低于M 版本怎么办
    • @Sagar 不行!使用Activity#getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) 添加从 4.4 到 5.1 的原生黑色渐变,以提高状态栏图标的可见性。要取消设置,请使用具有相同参数的 clearFlags。在 4.4 之前,状态栏通常是黑色的。
    • 使用异或,状态栏切换主题。使用 & ~ 效果很好。
    【解决方案4】:

    我将这个简单的实用程序对象放在一起,它允许您在任何片段中更改状态栏颜色和点亮状态栏的开/关。但是,这依赖于使用 Android Jetpack Navigation 组件进行导航 (Kotlin):

    object StatusBarUtil {
        fun changeStatusBarColor(activity: Activity, @ColorInt color: Int, lightStatusBar: Boolean) {
            activity.window?.let { win ->
                val nav = Navigation.findNavController(activity, R.id.your_nav_host_fragmen /* TODO: Use the ID of your nav host fragment */)
                val currentDest = nav.currentDestination?.id
                val oldColor = win.statusBarColor
                val oldFlags = win.decorView.systemUiVisibility
                win.statusBarColor = color
    
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    var flags = oldFlags
                    flags = if (lightStatusBar) {
                        flags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
                    } else {
                        flags and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv()
                    }
                    win.decorView.systemUiVisibility = flags
                }
    
                nav.addOnNavigatedListener { _, dest ->
                    if (dest.id != currentDest) {
                        win.statusBarColor = oldColor
                        win.decorView.systemUiVisibility = oldFlags
                    }
                }
            }
        }
    }
    

    要使用它,请从任何片段的onViewCreated 中调用以下命令:

    StatusBarUtil.changeStatusBarColor(requireActivity(), someDarkColor, false)
    

    【讨论】:

    • 哇,正是我想要的。如果他们要推送单一活动的应用程序,Android 真的需要为状态栏控制添加一个全新的 API :)
    • 这段代码有问题。如果我回到这个片段,那么颜色就没有设置,因为 currentDest 仍然是 onViewCreated 中的前一个屏幕。我在这里改进了它:gist.github.com/Chozzle/adf31f3bd709caec99c96cd996cd67ce
    • addOnNavigatedListener 不存在!
    • Navigation.findNavController(activity, R.id.your_nav_host_fragment) - 在哪里可以找到这个?
    【解决方案5】:

    SDK 的 API 30 略有变化,现在灯光状态栏外观由WindowInsetsController 控制,可以从Window 获得。 下面是 Kotlin 中的示例方法(在 Activity 内),将新 API 与之前用于旧 Android SDK 版本的 View.setSystemUiVisibility 结合起来。请记住,这只会改变状态栏的系统图标外观,状态栏的实际颜色仍然可以由Window.setStatusBarColor设置。

    @Suppress("DEPRECATION")
    private fun setSystemUiLightStatusBar(isLightStatusBar: Boolean) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                val systemUiAppearance = if (isLightStatusBar) {
                    WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
                } else {
                    0
                }
                window.insetsController?.setSystemBarsAppearance(systemUiAppearance,
                                                                 WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS)
            } else {
                val systemUiVisibilityFlags = if (isLightStatusBar) {
                    window.decorView.systemUiVisibility or SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
                } else {
                    window.decorView.systemUiVisibility and SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv()
                }
                window.decorView.systemUiVisibility = systemUiVisibilityFlags
            }
        }
    }
    

    【讨论】:

    • 你可以写else if (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.M) {而不是两个嵌套的if
    【解决方案6】:

    我为 API 23-30 切换明暗的方式与这些略有不同。这是一个 kotlin 版本

    由于我使用 Compose 和 Crossfade 动画来更改主题,在某些情况下会调用此函数两次,从而使 xor 自行撤消。另一种方法是逆 or 操作。我的灯光主题切换器最终看起来像这样

    @Suppress("DEPRECATION")
    fun invertInsets(darkTheme: Boolean, window: Window) {
        if (Build.VERSION.SDK_INT >= 30) {
            //Correct way of doing things
            val statusBar = APPEARANCE_LIGHT_STATUS_BARS
            val navBar = APPEARANCE_LIGHT_NAVIGATION_BARS
            if (!darkTheme) {
                window.insetsController?.setSystemBarsAppearance(statusBar, statusBar)
                window.insetsController?.setSystemBarsAppearance(navBar, navBar)
            } else {
                window.insetsController?.setSystemBarsAppearance(0, statusBar)
                window.insetsController?.setSystemBarsAppearance(0, navBar)
            }
        } else {
            // Does bitwise operations (or to add, inverse or to remove)
            // This is depreciated but the new version is API 30+ so I should have this here
            val flags = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR or
                if (Build.VERSION.SDK_INT >= 26) View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR else 0
    
            if (!darkTheme) {
                window.decorView.systemUiVisibility = 
                    window.decorView.systemUiVisibility or flags
            } else {
                window.decorView.systemUiVisibility = 
                    (window.decorView.systemUiVisibility.inv() or flags).inv()
            }
        }
    }
    

    API 30+ 的位没有贬值,但实际上 API 30 的手机并不多,因此也有较低 API 的位

    为了简洁起见,它只是预先计算标志(因为设置LIGHT_NAVIGATION_BARS 是 API 26+),然后明确设置或重置那些确切的标志。没有andxor 有趣的生意。 or 将始终将标志设置为1,反之或将始终将标志设置为0。这只是可能的,因为SYSTEM_UI_FLAG_LIGHT_STATUS_BARSYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR 都是一位。否则可能需要使用xor

    【讨论】:

      【解决方案7】:

      根据@phan-van-linh 的回答,我为 Xamarin Android 编写了这个类

      public static class ActivityExtensions
      {
          public static void SetLightStatusBar(this Activity activity)
          {
              int flags = (int)activity.Window.DecorView.SystemUiVisibility; // get current flag
              flags |= (int)SystemUiFlags.LightStatusBar;   // add LIGHT_STATUS_BAR to flag
              activity.Window.DecorView.SystemUiVisibility = (StatusBarVisibility)flags;
              //activity.Window.SetStatusBarColor(Color.GRAY); // optional
          }
      
          public static void ClearLightStatusBar(this Activity activity)
          {
              int flags = (int)activity.Window.DecorView.SystemUiVisibility; // get current flag
              flags = flags ^ (int)SystemUiFlags.LightStatusBar; // use XOR here for remove LIGHT_STATUS_BAR from flags
              activity.Window.DecorView.SystemUiVisibility = (StatusBarVisibility)flags;
              //activity.Window.setStatusBarColor(Color.GREEN); // optional
          }
      }
      

      【讨论】:

        【解决方案8】:

        我将对以上答案进行一些更改。

        开课

         public class DarkStatusBar {
            public static void setLightStatusBar(View view, Activity activity){
        
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        
                    int flags = view.getSystemUiVisibility();
                    flags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
                    view.setSystemUiVisibility(flags);
                    activity.getWindow().setStatusBarColor(Color.WHITE);
                }
            }
        }
        

        然后像这样在任何你想要的地方调用它

                Window window = getWindow();
                View view = window.getDecorView();
                DarkStatusBar.setLightStatusBar(view,this);
        

        【讨论】:

        • 感谢分享。上述选项均无效。只有你的。
        【解决方案9】:

        要更改为浅色状态栏,请使用:-

         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) 
             activity?.window?.decorView?.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
        
        

        要变回深色状态栏:-

         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) 
             activity?.window?.decorView?.systemUiVisibility = 0
        

        【讨论】:

          【解决方案10】:

          用浅色文字设置蓝色背景状态栏kotlin版本

          fun setBlueStatusBarColor(window: Window, context: Context) {
              if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                      window.statusBarColor = context.getColor(R.color.colorBlue)
                  }else {
                      window.statusBarColor = context.resources.getColor(R.color.colorBlue)
                  }
          
                  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                      var flags: Int = window.decorView.systemUiVisibility
                      flags = flags and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
                      window.decorView.systemUiVisibility = flags
                  }
              }
          }
          

          【讨论】:

            【解决方案11】:
            /**
             * Changes color of the status bar icons
             * @param isLight if true - shows dark icons, light else
             */
            fun setStatusBarUiTheme(activity: Activity?, isLight: Boolean) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    activity?.window?.decorView?.let {
                        it.systemUiVisibility = if (isLight)
                            it.systemUiVisibility or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR // dark icons
                        else
                            it.systemUiVisibility and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv() // light icons
                    }
                }
            }
            

            【讨论】:

              【解决方案12】:

              在 res/styles.xml 中

              <style name="AppTheme" parent="Theme.AppCompat.NoActionBar">
                  <item name="android:windowLightStatusBar">true</item>
                  .......
              </style>
              
              <style name="AppTheme.DarkStatus" parent="AppTheme" tools:targetApi="23" >
                  <item name="android:windowLightStatusBar">false</item>
                  <item name="android:statusBarColor" >@color/status_bar_color</item>
              </style>
              

              在代码中

              @Override
              protected void onCreate(Bundle savedInstanceState) {
                  super.onCreate(savedInstanceState);
                  setTheme(R.style.AppTheme_DarkStatus);  //To set DarkStatusBar theme
                  setContentView(R.layout.activity_drawer);
                  ....
              }
              

              【讨论】:

                【解决方案13】:

                对我有用

                fun Activity.clearLightStatusBar() {
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                        val window = window
                        window.statusBarColor = ContextCompat
                            .getColor(this, R.color.ultramarine_blue)
                    }
                }
                

                【讨论】:

                  【解决方案14】:

                  systemUiVisibility - 现在已弃用。你可以改用WindowInsetsControllerCompat

                  private val insetsController: WindowInsetsControllerCompat? by lazy {
                      activity?.window?.let { window -> WindowInsetsControllerCompat(window, window.decorView) }
                  }
                  
                  private fun setLightStatusBar(light: Boolean) {
                      insetsController?.isAppearanceLightStatusBars = light
                  }
                  

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 2023-04-09
                    • 1970-01-01
                    • 2011-09-28
                    • 1970-01-01
                    • 2012-04-05
                    • 2015-02-28
                    • 2021-08-28
                    相关资源
                    最近更新 更多