屏保多DHU下同步字体颜色

需求:不同DHU多屏场景下,设置同一系列屏保,屏保中间组件字体颜色需要动态读取背后壁纸主色亮度,根据背后亮度动态设置字体颜色

偏亮的=黑色,偏暗的=白色

1、取色

读取亮度需要先对bitmap解码,通过 Color.colorToHSV 方法读取亮度值

图片[1]-屏保多DHU下同步字体颜色-牛翰网

private fun generate(newMap: Bitmap): FloatArray {
        val hsvColorArray = FloatArray(3)
        Palette.from(newMap).generate().apply {
            val dominantColor = getDominantColor(Color.BLACK)
            Color.colorToHSV(dominantColor, hsvColorArray)
        }
        return hsvColorArray
    }

View Code

随后直接根据亮度值返回对应颜色

图片[1]-屏保多DHU下同步字体颜色-牛翰网

// hsvColorArray[2]
private fun getColor(num: Float = 0f) = if (num >= 0.7) {
        CommonUtils.COLOR_BLACK
    } else {
        CommonUtils.COLOR_WHITE
    }

View Code

2、裁剪

取色的bitmap是字体背后对应的区域,而不是整个壁纸,所以需要进行计算裁剪

图片[1]-屏保多DHU下同步字体颜色-牛翰网

private fun createRectBitmap(bitmap: Bitmap, isDim: Boolean): Bitmap {
        return if (isDim) {
            Bitmap.createBitmap(
                bitmap,
                dimWidth / 2 - rectWidth / 2, dimHeight / 2 - rectHeight / 2,
                rectWidth, rectHeight
            )
        } else {
            Bitmap.createBitmap(
                bitmap, mWidth / 2 - rectWidth / 2, rectTopMargin, rectWidth, rectHeight
            )
        }
    }

View Code

不同屏幕尺寸不一样,布局不一样,所以对应要裁剪的区域也不一样,这里需要统一适配

bitmap建议使用 Glide 加载,效果更高

对bitmap进行裁剪取色都属于耗时操作,需要放在子线程处理,缓存使用map存放,key使用屏幕的displayId

3、预加载

每个系列好几张图片,每个DHU可能有多个屏幕(比如CSD屏存在主副驾屏),加上DIM屏属于QNX系统,不太方便实现该功能,所以也需要CSD去实现同步

基于上面要求,缓存量很大,所以需要在初始化时使用多线程处理

图片[1]-屏保多DHU下同步字体颜色-牛翰网

object ColorPickManager {

    private const val TAG = "ColorPickManager"
    private val suitMap = hashMapOf<Int, MutableList<String>>()

    private val suitIdObserver = Observer<Int> {
        loadSuitData(it)
    }

    @JvmStatic
    fun init() {
        Log.d(TAG, "$TAG init ${VehicleManager.getVehicleType()}")
        initSuitData()
        LocalResourceManager.observeUsageSuitId(suitIdObserver)
    }

    private fun initSuitData() {
        val list = mutableListOf<DisplayParameter>()
        if (VehicleManager.isA()) {
            list.add(DisplayParameter.DISPLAY_PSD)
            list.add(DisplayParameter.DISPLAY_CSD)
            list.add(DisplayParameter.DISPLAY_TV)
            list.add(DisplayParameter.DISPLAY_DIM)
        } else if (VehicleManager.isB()) {
            list.add(DisplayParameter.DISPLAY_CONSOLE)
            list.add(DisplayParameter.DISPLAY_CSD)
            list.add(DisplayParameter.DISPLAY_TV)
            list.add(DisplayParameter.DISPLAY_DIM)
        } else {
            Log.w(TAG, "initSuitData 不支持的车型 ${VehicleManager.getVehicleType()}")
        }
        list.forEach {
            Log.d(TAG, "initSuitData ${it.displayName} suitMap[${it.displayId}]")
            suitMap[it.displayId] = mutableListOf()
        }
    }

    /** 当前屏保套系 */
    private fun loadSuitData(suitId: Int) {
        Log.d(TAG, "loadSuitData suitId=$suitId")
        MainScope().launch(Dispatchers.IO) {
            LocalResourceManager.getSuitsWithPicturesById(suitId)?.apply {
                Log.d(TAG, "loadSuitData suitMapSize=${suitMap.size}")
                suitMap.keys.forEach { addCacheData(it, this) }
            }
        }
    }

    private fun addCacheData(displayId: Int, suitsWithPictures: SuitWithPictures) {
        val suit = suitsWithPictures.suit
        val pictures = suitsWithPictures.pictures
        suitMap[displayId]?.clear()
        val pathList = mutableListOf<String>()
        if (ResourceLoadManager.isDynamic(suit.type)) {
            pathList.add(suit.path)
        } else if (ResourceLoadManager.TYPE_CUSTOM != suit.type) {
            pictures.forEach { pathList.add(convertPath(displayId, it.path)) }
        } else {
            pictures.forEach {
                val path = when (displayId) {
                    DisplayParameter.DISPLAY_TV.displayId -> {
                        ResourceLoadManager.convertPath2Tv(it.path)
                    }

                    DisplayParameter.DISPLAY_PSD.displayId -> {
                        ResourceLoadManager.convertPath2Psd(it.path)
                    }

                    else -> ResourceLoadManager.convertPath2Csd(it.path)
                }
                pathList.add(convertPath(displayId, path))
            }
        }
        pathList.forEach {
            Log.d(TAG, "addCacheData suitMap[$displayId] add $it")
        }
        suitMap[displayId]?.addAll(pathList)
        loadScreensaversFontColorData(displayId)
        loadAmbientLightData(displayId)
    }

    private fun convertPath(displayId: Int, path: String): String {
        return if (displayId == DisplayParameter.DISPLAY_DIM.displayId) {
            ResourceLoadManager.convertPath2Dim(path)
        } else path
    }

    @JvmStatic
    fun syncScreensaversColor(displayId: Int, index: Int) {
        AmbientLightColorPickManager.setAmbientLight(displayId, index)
        FontColorPickManager.syncScreensaversFontColor(displayId, index)
    }

    private fun loadScreensaversFontColorData(displayId: Int) {
        val list = suitMap[displayId]
        if (list.isNullOrEmpty()) {
            Log.w(TAG, "loadScreensaversFontColorData suitMap[$displayId] value is null")
            return
        }
        FontColorPickManager.loadScreensaversFontColorData(displayId, list)
    }

}

View Code

针对不同车型缓存对应屏幕,监听屏保变化,如果中途切换需要动态更新

4、同步

缓存处理好后,屏保通过计时器轮播,每次轮播从缓存中读取对应的颜色值同步到字体颜色,实现字体动态跟随屏保壁纸切换

5、多屏适配

不同屏幕尺寸不一样,动态获取背后矩形区域不利于提前缓存处理,因为需要布局加载完后才能拿到坐标宽高,而等进入屏保后在获取会导致字体默认显示颜色跟实际取色不一致,进入屏保导致字体颜色切换过程,如果提前缓存便没有这个过程

实现方案:拿到bitmap后拉伸到基准宽高(设计稿给的宽高),然后参考设计稿裁剪矩形区域,这样无论什么尺寸的屏幕,裁剪的区域都一样,颜色也一样,同步屏保时就可以保证没有偏差

优化:图片过大导致内存占比耗时过高等,将所有基准降低三倍处理(不能太低,否则主色会有偏差,导致颜色不对)

6、多屏预览

预览页要求可以预览所有屏幕的屏保,也包含组件字体颜色的预览,并且主副驾屏幕可以同时进入详情(同一个DHU)

字体会自动跟随壁纸亮度切换颜色

 

来源链接:https://www.cnblogs.com/LiuZhen/p/18948838

© 版权声明
THE END
支持一下吧
点赞13 分享
评论 抢沙发
头像
请文明发言!
提交
头像

昵称

取消
昵称表情代码快捷回复

    暂无评论内容