Android 主题色无缝切换方案:Databinding下实现控件皮肤无缝切换
code小生 一个专注大前端领域的技术平台 公众号回复 Android
加入安卓技术群
作者:showMethe
链接:https://www.jianshu.com/p/9e0c4b0af0dc
声明:本文已获 showMethe
授权发表,转发等请联系原作者授权
无缝切换主题色
这个功能一直都是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
本库地址,单独使用skinlib的内容: https://github.com/ShowMeThe/SkinManager/tree/master/skinlib
先放效果(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里面对应的资源
@color/colorAccent @color/colorAccent @drawable/shape_drawer_head_bg @color/color_5f4fc3f7 @color/colorAccent @color/colorAccent @color/colorAccent @color/colorAccent @color/colorAccent @color/colorAccent @color/colorAccent @color/colorAccent @drawable/shape_blue_cursor @color/colorAccent @color/colorAccent @color/colorAccent
同时增加对Json的解析,因为不是所有的主题都要写在style,可能遇到需要后台下载解析的情况
val json = AssetFile.getJson(this,"orange.json") val colorEntity = json.fromJson()
其中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 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 { //对应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? ) { } } }
这个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" ] }
以上都是举一些例子,内容都需要根据实际进行修改,这里只是探讨一个方案。