無縫切換主題色
這個功能一直都是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)卡)

在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)行修改,這里只是探討一個方案。