vlambda博客
学习文章列表

Android 主题色无缝切换方案:Databinding下实现控件皮肤无缝切换

无缝切换主题色

这个功能一直都是Android的开发经常遇到的。我逛的最多的B站App就是一个很好地例子,他们的皮肤切换是无缝的,那我们自己能不能自己也来搞一个,当然是可以的,虽然有重复造轮子,但是我们还是需要不断锻炼自己的开发水平,尝试不同的方案。

Github可以找到一个不错的皮肤切换库:https://github.com/fengjundev/Android-Skin-Loader

基于LayoutInflaterFactory的皮肤切换,涉及到LayoutInflater 提供了setFactory(LayoutInflater.Factory factory)和setFactory2(LayoutInflater.Factory2 factory) 的知识点,这里不展开。

看完这个库的源码我想了个问题,我能不能另外开辟一条简单的路去走呢,这时候捡起了手中的Databinding。

所以本文章是基于Databinding下进行的,受限于框架使用,所以相比于Android-Skin-Loader,我建议使用Android-Skin-Loader因为它更加灵活,本文章提到的方案开辟的路开窄了,特别是不支持Databinding的项目,诸如在旧项目上迭代的不适用本方案,不喜欢Databinding框架的同学也不太适用,因为有很多模板代码,而且是用Kotlin进行开发的,如果不想项目引入额外的Kotlin也不适用。这次这条路真的开窄了。

先介绍使用方法

本库的测试环境是基于我一个玩安卓项目进行的:https://github.com/ShowMeThe/MaterialWanAndroid

先放效果(Gif 有点卡)

1.gif

在Application初始化

val themes_name = arrayListOf("BlueTheme","RedTheme","PurpleTheme","OrangeTheme","YellowTheme")
      SkinManager.init(this).addStyle(
            themes_name[0] to R.style.MaterialTheme_Blue,
            themes_name[1] to R.style.MaterialTheme_Red,
            themes_name[2] to R.style.MaterialTheme_Purple)
            .build()
    SkinManager.getInstant().setOnStyleChangeListener {
           //主题切换的监听

        }
//设置当前主题,不设置会默认拿了addStyle里面的第一个index,即index是0那个设置
SkinManager.currentStyle =  ""

分别配置主题的名字Key和对应的Value,Value是Style里面对应的资源

 <style name="MaterialTheme.Blue">
         <item name="theme_text_color">@color/colorAccent</item>
         <item name="theme_viewGroup_backgroundColor">@color/colorAccent</item>
         <item name="theme_viewGroup_background">@drawable/shape_drawer_head_bg</item>
         <item name="theme_button_rippleColor">@color/color_5f4fc3f7</item>
         <item name="theme_button_iconTint">@color/colorAccent</item>
         <item name="theme_button_textColor">@color/colorAccent</item>
         <item name="theme_button_strokeColor">@color/colorAccent</item>
         <item name="theme_bottom_navigation_iconTint">@color/colorAccent</item>
         <item name="theme_bottom_navigation_textColor">@color/colorAccent</item>
         <item name="theme_imageView_tint">@color/colorAccent</item>
         <item name="theme_card_strokeColor">@color/colorAccent</item>
         <item name="theme_floating_backgroundColor">@color/colorAccent</item>
         <item name="theme_edit_cursorDrawable">@drawable/shape_blue_cursor</item>
         <item name="theme_inputLayout_boxColor">@color/colorAccent</item>
         <item name="theme_inputLayout_hintColor">@color/colorAccent</item>
         <item name="theme_edit_highlightColor">@color/colorAccent</item>
    
</style>

同时增加对Json的解析,因为不是所有的主题都要写在style,可能遇到需要后台下载解析的情况

 val json = AssetFile.getJson(this,"orange.json")
 val colorEntity = json.fromJson<ColorEntity>()

其中getJson方法如下:

        fun getJson(context: Context, fileName: String): String {
            val stringBuilder = StringBuilder()
            try {
                val assetManager = context.assets
                val bf = BufferedReader(InputStreamReader(assetManager.open(fileName)))
                var line: String
                bf.use {
                    while (true) {
                        line = bf.readLine() ?:break
                        stringBuilder.append(line)
                    }
                }

            } catch (e: IOException) {
                e.printStackTrace()
            }
            return stringBuilder.toString()
        }

fromJson方法入下

inline fun <reified T> String?.fromJson(): T? {
    if (isNullOrEmpty()) { //判断空和null
        return null
    }
    return try {
        val clazz = T::class.java
        gson.fromJson(this, clazz) //Gson解析
    } catch (e: JsonSyntaxException) {
        e.printStackTrace()
        null
    }
}

这两个方法各位可以自己写,我这里提供参考而已

由于当时写的时候,路开窄了而且不想引入太多额外的库,对Widget判断是直接采用包名的提取,所以只支持AndroidX和官方那个MaterialDesign的Widget库 于是添加了一个自定义插件的方法IPlugin ,举SwipeRefreshLayout这个Widget作为例子:

class RefreshPlugin : IPlugin<SwipeRefreshLayout{
 //对应style的
    override fun individuate(view: SwipeRefreshLayout, attrName: String) {
        when (attrName) {
            themes_name[0] -> view.setColorSchemeResources(R.color.colorAccent)
            themes_name[1] -> view.setColorSchemeResources(R.color.color_304ffe)
            themes_name[2] -> view.setColorSchemeResources(R.color.color_6200ea)
        }
    }

// 对应Json参数,colors对应Json中colorObjects字段
    override fun individuate(
        view: SwipeRefreshLayout,
        attrName: String,
        colors: ArrayList<String>?
    )
 {
       
        }
    }
}

这个Json对应

{
  "theme_viewGroup_background""FBC02D",
  "theme_viewGroup_backgroundColor""FBC02D",
  "theme_card_strokeColor""FBC02D",
  "theme_text_color""FBC02D",
  "theme_button_textColor""FBC02D",
  "theme_button_rippleColor""2cFDD835",
  "theme_button_iconTint""FBC02D",
  "theme_button_strokeColor""FBC02D",
  "theme_bottom_navigation_iconTint""FBC02D",
  "theme_bottom_navigation_textColor""FBC02D",
  "theme_imageView_tint""FBC02D",
  "theme_floating_backgroundColor""FBC02D",
  "theme_edit_cursorDrawable""FBC02D",
  "theme_edit_highlightColor""FBC02D",
  "theme_inputLayout_boxColor""FBC02D",
  "theme_inputLayout_hintColor""FBC02D",
  "colorObjects": [
    "FBC02D",
    "2cFDD835"
  ]
}

以上都是举一些例子,内容都需要根据实际进行修改,这里只是探讨一个方案。

相关阅读








如果你有写博客的好习惯

点个在看,小生感恩❤️