1. Introdução ao modo de exibição da barra de status
O sistema Android oferece dois modos de exibição: modo claro e modo escuro
Modelo claro: a cor geral é brilhante, ou seja, o fundo é claro e o texto e outros conteúdos são escuros.
Modelo Escuro: A cor geral é mais escura, ou seja, o fundo é escuro e o texto e outros conteúdos são claros.
2. Defeitos na barra de status do Android
A cor do conteúdo da barra de status do Android (sem cor de fundo) é apenas preto e branco. Quando está no modo claro, é um tema preto, e quando está no modo escuro, é um tema branco. Claro, ter apenas duas cores não é uma falha.
Quando a cor de fundo da barra de status muda, precisamos definir manualmente o modo de exibição para alterar a cor do seu conteúdo. Isso é extremamente inconveniente, especialmente quando a cor de fundo da barra de status é gradiente.
Por exemplo:
Quando o WeChat é baixado, a cor de fundo da barra de status continua mudando, mas a cor do conteúdo não muda com o tempo, conforme mostrado na caixa vermelha na figura.
3. Ideação de solução
Na verdade, a cor do conteúdo da barra de status é completamente adaptável. A lógica é muito simples. Quando o fundo estiver escuro, o conteúdo da barra de status ficará claro; quando o fundo estiver claro, o conteúdo da barra de status ficará escuro. Portanto, não há é uma ideia geral. Cada vez que a interface é desenhada:
-
Obtenha pixels da barra de status
-
Calcule seu valor médio de cor
-
Determine se é uma cor brilhante
-
Se for uma cor brilhante, defina-a para o modo claro, caso contrário, defina-a para o modo escuro.
4. Implementação de lógica de código específica
/**
* 获取状态栏像素
*/
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())
}
O código acima obtém automaticamente o valor da cor da barra de status do celular e calcula o valor médio.
Em seguida, o método a seguir pode passar diretamente o valor da cor para alterar dinamicamente a cor do tema da barra de status.
/**
* 是否为亮色
* 可以单独传入 色值 判断是否为亮色
*/
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. Otimização de desempenho durante a aplicação
/**
* 性能优化:单线程池,更新阻塞时只做最后一次更新
*/
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. Exemplo geral de uso de código
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)
}
}
O acima é que o tema da barra de status muda automaticamente de acordo com a cor de fundo da barra de status. Você também pode passar o valor da cor para determinar se é uma cor brilhante e definir o tema da barra de status.