以编程方式安装Android M Light and Dark状态栏 - 如何让它再次变黑?

ude*_*fox 28 user-interface android android-statusbar android-6.0-marshmallow

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

<item name="android:windowLightStatusBar">true</item>
Run Code Online (Sandbox Code Playgroud)

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

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);
}
Run Code Online (Sandbox Code Playgroud)

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

我已经尝试过那些变种:

// 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);
Run Code Online (Sandbox Code Playgroud)

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

Car*_*Gil 40

@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); 
            }
}
Run Code Online (Sandbox Code Playgroud)

要将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)); 
        }
    }
Run Code Online (Sandbox Code Playgroud)

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

  • setSystemUiVisibility 在 Api 30+ 中已弃用 (4认同)
  • 是否可以在视图仍在屏幕上时恢复灯状态栏。例如,将主题从浅色模式更改为深色模式时? (2认同)
  • PS,为什么不立即进行还原操作?为什么它与视图相关联? (2认同)

Ara*_*cem 32

根据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);
    }
}
Run Code Online (Sandbox Code Playgroud)

你可以在这里找到项目

  • @ capt.swag我认为它是`View view = window.getDecorView();`. (9认同)
  • 为什么 setSystemUiVisibility() 是 View 类的方法? (2认同)
  • setSystemUiVisibility 在 Api 30+ 中已弃用 (2认同)

Pha*_*inh 16

我基于@Aracem和@CarlosHernándezGil,但我认为如果我们使用按位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
    }
}
Run Code Online (Sandbox Code Playgroud)

说明

首先,看看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;
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

我认为下面的2行代码很难理解

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
Run Code Online (Sandbox Code Playgroud)

乍一看,我认为我们可以使用简单的

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)
Run Code Online (Sandbox Code Playgroud)

但是我们应该使用|并且^因为
示例,我们要将状态栏和导航栏都设置为亮,然后我们将使用

flags = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR | View.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
activity.getWindow().getDecorView().setSystemUiVisibility(flags);
Run Code Online (Sandbox Code Playgroud)

当我们不希望状态栏再亮时,我们可以使用

flags = View.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
activity.getWindow().getDecorView().setSystemUiVisibility(flags);
Run Code Online (Sandbox Code Playgroud)

要么

flags = activity.getWindow().getDecorView().getSystemUiVisibility();
flags = flags ^ View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; 
activity.getWindow().getDecorView().setSystemUiVisibility(flags);
Run Code Online (Sandbox Code Playgroud)

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

  • 但是,如果当前未设置该标志,则XOR还将启用该标志。如果要确保它是_unset_,则应使用`&〜`。 (3认同)

Tim*_*iev 11

systemUiVisibility- 现已弃用。你可以用WindowInsetsControllerCompat它代替。

private val insetsController: WindowInsetsControllerCompat? by lazy {
    activity?.window?.let { window -> WindowInsetsControllerCompat(window, window.decorView) }
}

private fun setLightStatusBar(light: Boolean) {
    insetsController?.isAppearanceLightStatusBars = light
}
Run Code Online (Sandbox Code Playgroud)

UPD:上面的构造函数WindowInsetsControllerCompat已被弃用,因此请使用以下实例化:

private val insetsController: WindowInsetsControllerCompat? by lazy {
    activity?.window?.decorView?.let(ViewCompat::getWindowInsetsController)
}
Run Code Online (Sandbox Code Playgroud)


Orl*_*ndo 8

我为 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()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

API 30+ 的位不会折旧,但实际上没有多少手机达到 API 30,因此还有较低 API 的位

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


hss*_*son 5

我将这个简单的实用程序对象放在一起,它允许您在任何片段内更改状态栏颜色和点亮/关闭状态栏。但是,这依赖于使用 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
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

要使用它,请从任何片段的 中调用以下内容onViewCreated

StatusBarUtil.changeStatusBarColor(requireActivity(), someDarkColor, false)
Run Code Online (Sandbox Code Playgroud)


小智 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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)