Android主題色無縫切換方案:Databinding下實現(xiàn)控件皮膚無縫切換

無縫切換主題色

這個功能一直都是Android的開發(fā)經(jīng)常遇到的。我逛的最多的B站App就是一個很好地例子,他們的皮膚切換是無縫的,那我們自己能不能自己也來搞一個,當(dāng)然是可以的,雖然有重復(fù)造輪子,但是我們還是需要不斷鍛煉自己的開發(fā)水平,嘗試不同的方案。
Github可以找到一個不錯的皮膚切換庫:Android-Skin-Loader
基于LayoutInflaterFactory的皮膚切換,涉及到LayoutInflater 提供了setFactory(LayoutInflater.Factory factory)和setFactory2(LayoutInflater.Factory2 factory) 的知識點(diǎn),這里不展開。
看完這個庫的源碼我想了個問題,我能不能另外開辟一條簡單的路去走呢,這時候撿起了手中的Databinding。
所以本文章是基于Databinding下進(jìn)行的,受限于框架使用,所以相比于Android-Skin-Loader,我建議使用Android-Skin-Loader因為它更加靈活,本文章提到的方案開辟的路開窄了,特別是不支持Databinding的項目,諸如在舊項目上迭代的不適用本方案,不喜歡Databinding框架的同學(xué)也不太適用,因為有很多模板代碼,而且是用Kotlin進(jìn)行開發(fā)的,如果不想項目引入額外的Kotlin也不適用。這次這條路真的開窄了。

先介紹使用方法

本庫的測試環(huán)境是基于我一個玩安卓項目進(jìn)行的:https://github.com/ShowMeThe/MaterialWanAndroid
本庫地址,單獨(dú)使用skinlib的內(nèi)容:https://github.com/ShowMeThe/SkinManager/tree/master/skinlib

先放效果(Gif 有點(diǎn)卡)


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 {
           //主題切換的監(jiān)聽

        }
//設(shè)置當(dāng)前主題,不設(shè)置會默認(rèn)拿了addStyle里面的第一個index,即index是0那個設(shè)置
SkinManager.currentStyle =  ""

分別配置主題的名字Key和對應(yīng)的Value,Value是Style里面對應(yīng)的資源

 <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>()

其中g(shù)etJson方法如下:

        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
    }
}

這兩個方法各位可以自己寫,我這里提供參考而已
由于當(dāng)時寫的時候,路開窄了而且不想引入太多額外的庫,對Widget判斷是直接采用包名的提取,所以只支持AndroidX和官方那個MaterialDesign的Widget庫
于是添加了一個自定義插件的方法IPlugin<View>,舉SwipeRefreshLayout這個Widget作為例子:

class RefreshPlugin : IPlugin<SwipeRefreshLayout> {
 //對應(yīng)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)
        }
    }

// 對應(yīng)Json參數(shù),colors對應(yīng)Json中colorObjects字段
    override fun individuate(
        view: SwipeRefreshLayout,
        attrName: String,
        colors: ArrayList<String>?
    ) {
       
        }
    }
}

這個Json對應(yīng)

{
  "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"
  ]
}

以上都是舉一些例子,內(nèi)容都需要根據(jù)實際進(jìn)行修改,這里只是探討一個方案。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容