25

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

 3 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzIxNzU1Nzk3OQ%3D%3D&%3Bmid=2247490312&%3Bidx=1&%3Bsn=ecdb3c12034b0789e328a8ee8aaf78cb
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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 有点卡)

nQzINvj.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"
  ]
}

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK