The Android immersive status bar theme automatically changes the theme following the status bar background color, and the status bar theme follows the status bar background color adaptively

1. Introduction to status bar display mode

The Android system provides two display modes: bright mode and dark mode

Light Model: The overall color is bright, that is, the background is bright and text and other content are dark.

Dark Model: The overall color is darker, that is, the background is dark and text and other content are bright.

2. Android status bar defects

The Android status bar content color (non-background color) is only black and white. When it is in bright mode, it is a black theme, and when it is in dark mode, it is a white theme. Of course, having only two colors is not a flaw.

When the status bar background color changes, we need to manually set the display mode to change the color of its content. This is extremely inconvenient, especially when the background color of the status bar is gradient.

For example:

When WeChat is pulled down, the background color of the status bar keeps changing, but the content color does not change in time, as shown in the red box in the figure.

3. Solution Ideation 

In fact, the color of the status bar content is completely adaptive. The logic is very simple. When the background is dark, the status bar content will be bright; when the background is bright, the status bar content will be dark. So there is a general idea. Every time the interface is drawn:

  1. Get status bar pixels

  2. Calculate its average color value

  3. Determine whether it is a bright color

  4. If it is a bright color, set it to bright mode, otherwise set it to dark mode.

4. Specific code logic implementation

/**
     * 获取状态栏像素
     */
    private fun getStatusBarPixels(activity:Activity) = activity.window.decorView.let {
        it.isDrawingCacheEnabled = true
        it.buildDrawingCache()
        // 截屏
        val screenBitmap = it.getDrawingCache()
        val width = screenBitmap.width
        val height = BarUtils.getStatusBarHeight()
        val pixels = IntArray(width * height)
        // 获取状态栏区域像素
        screenBitmap.getPixels(pixels, 0, width, 0, 0, width, height)
        it.destroyDrawingCache()
        pixels
    }

    /**
     * 获取平均色值
     */
    fun getAvgColor(pixels: IntArray): Int {
        var r = 0L
        var g = 0L
        var b = 0L
        pixels.forEach {
            r += Color.red(it)
            g += Color.green(it)
            b += Color.blue(it)
        }
        r /= pixels.size
        g /= pixels.size
        b /= pixels.size
        return Color.rgb(r.toInt(), g.toInt(), b.toInt())
    }

The above code automatically obtains the color value of the mobile phone status bar and calculates the average value.

Then the following method can directly pass in the color value to dynamically change the theme color of the status bar.

/**
     * 是否为亮色
     * 可以单独传入 色值 判断是否为亮色
     */
    fun isLightColor(@ColorInt color: Int) =
        (computeLuminance(color) + 0.05).pow(2.0) > 0.15

    /**
     * 是否为亮色
     * 自动获取状态栏的色纸 判断是否为亮色
     */
    fun isLightColor(activity:Activity):Boolean{
        val pixels = getStatusBarPixels(activity)
        val color = getAvgColor(pixels)
        val computeColor = computeLuminance(color) + 0.05
        return (computeColor.pow(2.0) > 0.15)
    }

    /**
     * 颜色亮度
     */
    private fun computeLuminance(@ColorInt color: Int) =
        0.2126 * linearizeColorComponent(Color.red(color)) +
                0.7152 * linearizeColorComponent(Color.green(color)) +
                0.0722 * linearizeColorComponent(Color.blue(color))

    /**
     * 线性化颜色分量
     */
    private fun linearizeColorComponent(colorComponent: Int) = (colorComponent / 255.0).let {
        if (it <= 0.03928) it / 12.92 else ((it + 0.055) / 1.055).pow(2.4)
    }

5.Performance optimization during application

/**
     * 性能优化:单线程池,更新阻塞时只做最后一次更新
     */
    private val executor by lazy {
        object : ThreadPoolExecutor(1, 1, 0, TimeUnit.MILLISECONDS, ArrayBlockingQueue(1)) {
            override fun execute(command: Runnable?) {
                queue.clear()
                super.execute(command)
            }
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        BarUtils.transparentStatusBar(this) // 需要沉浸状态栏,才能截屏至状态栏
        window.decorView.viewTreeObserver.addOnDrawListener(this)
    }

    override fun onDestroy() {
        window.decorView.viewTreeObserver.removeOnDrawListener(this)
        super.onDestroy()
    }

    override fun onDraw() {
        executor.execute {
            try {
                // 获取状态栏像素
                val pixels = getStatusBarPixels()
                // 计算平均色值
                val avgColor = getAvgColor(pixels)
                // 判断是否为亮色
                val isLight = isLightColor(avgColor)
                runOnUiThread {
                    // 设置 LightModel
                    if (!isDestroyed) BarUtils.setStatusBarLightMode(this, isLight)
                }
            } catch (_: Exception) {
            }
        }
    }

6. Overall code usage example

class MainActivity : AppCompatActivity(), ViewTreeObserver.OnDrawListener {

    /**
     * 性能优化:单线程池,更新阻塞时只做最后一次更新
     */
    private val executor by lazy {
        object : ThreadPoolExecutor(1, 1, 0, TimeUnit.MILLISECONDS, ArrayBlockingQueue(1)) {
            override fun execute(command: Runnable?) {
                queue.clear()
                super.execute(command)
            }
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        BarUtils.transparentStatusBar(this) // 需要沉浸状态栏,才能截屏至状态栏
        window.decorView.viewTreeObserver.addOnDrawListener(this)
    }

    override fun onDestroy() {
        window.decorView.viewTreeObserver.removeOnDrawListener(this)
        super.onDestroy()
    }

    override fun onDraw() {
        executor.execute {
            try {
                // 获取状态栏像素
                val pixels = getStatusBarPixels()
                // 计算平均色值
                val avgColor = getAvgColor(pixels)
                // 判断是否为亮色
                val isLight = isLightColor(avgColor)
                runOnUiThread {
                    // 设置 LightModel
                    if (!isDestroyed) BarUtils.setStatusBarLightMode(this, isLight)
                }
            } catch (_: Exception) {
            }
        }
    }

    /**
     * 获取状态栏像素
     */
    private fun getStatusBarPixels() = window.decorView.let {
        it.isDrawingCacheEnabled = true
        it.buildDrawingCache()
        // 截屏
        val screenBitmap = it.getDrawingCache()
        val width = screenBitmap.width
        val height = BarUtils.getStatusBarHeight()
        val pixels = IntArray(width * height)
        // 获取状态栏区域像素
        screenBitmap.getPixels(pixels, 0, width, 0, 0, width, height)
        it.destroyDrawingCache()
        pixels
    }

    /**
     * 获取平均色值
     */
    private fun getAvgColor(pixels: IntArray): Int {
        var r = 0L
        var g = 0L
        var b = 0L
        pixels.forEach {
            r += Color.red(it)
            g += Color.green(it)
            b += Color.blue(it)
        }
        r /= pixels.size
        g /= pixels.size
        b /= pixels.size
        return Color.rgb(r.toInt(), g.toInt(), b.toInt())
    }

    /**
     * 是否为亮色
     * 可以单独传入 色值 判断是否为亮色
     */
    fun isLightColor(@ColorInt color: Int) =
        (computeLuminance(color) + 0.05).pow(2.0) > 0.15

    /**
     * 是否为亮色
     * 自动获取状态栏的色纸 判断是否为亮色
     */
    fun isLightColor(activity:Activity):Boolean{
        val pixels = getStatusBarPixels(activity)
        val color = getAvgColor(pixels)
        val computeColor = computeLuminance(color) + 0.05
        return (computeColor.pow(2.0) > 0.15)
    }

    /**
     * 颜色亮度
     */
    private fun computeLuminance(@ColorInt color: Int) =
        0.2126 * linearizeColorComponent(Color.red(color)) +
                0.7152 * linearizeColorComponent(Color.green(color)) +
                0.0722 * linearizeColorComponent(Color.blue(color))

    /**
     * 线性化颜色分量
     */
    private fun linearizeColorComponent(colorComponent: Int) = (colorComponent / 255.0).let {
        if (it <= 0.03928) it / 12.92 else ((it + 0.055) / 1.055).pow(2.4)
    }
}

The above is that the theme of the status bar automatically changes according to the background color of the status bar. You can also pass in the color value yourself to determine whether it is a bright color and set the theme of the status bar.

Guess you like

Origin blog.csdn.net/weitao_666/article/details/132105650