At present, it is only used as a trial scheme, without enough tests for verification, so use with caution!!

Seamlessly switch theme colors

This feature has always been a common feature of Android development. The WEBSITE B App I visit most is a good example. Their skin switching is seamless, so can we make one ourselves? Of course, we can. Github can find a good library for skin switching: Android-skin-loader Skin switch based on LayoutInflaterFactory, LayoutInflater setFactory(LayoutInflater.Factory Factory) and setFactory2(LayoutInflater.Factory2 Factory). After looking at the source code of the library, I wondered if I could take another simple path and pick up the Databinding in my hand. Therefore, this article is based on Databinding and is limited by the framework. Therefore, I recommend using Android-skin-loader as opposed to Android-skin-loader because it is more flexible and the solution mentioned in this article has limitations. In particular, projects that do not support Databinding, such as iterating over old projects, are not suitable for this scenario. Students who do not like the Databinding framework are also not suitable because there is a lot of template code and it is developed in Kotlin. It is also not suitable if the project does not want to introduce additional Kotlin because they do not want to try the scenario.

First introduced

The library test environment is based on my a play Android project: github.com/ShowMeThe/M… Skinlib library address, can be copied separately: github.com/ShowMeThe/S… The Gif format is as follows:

Class in 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.s tyle. MaterialTheme_Purple). The build () SkinManager. GetInstant () setOnStyleChangeListener {/ / theme switch monitor} Skinmanager.currentstyle = skinManager.currentStyle = skinManager.currentStyle = skinManager.currentStyle = skinManager.currentStyle = skinManager.currentStyle = skinManager.currentStyle = skinManager.currentStyle = skinManager.currentStyle = skinManager.currentStyle = skinManager.currentStyle = skinManager.currentStyle = skinManager.currentStyle""
Copy the code

Configure the topic name Key and corresponding Value respectively. Value is the corresponding resource in 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>
Copy the code

Also add Json parsing, because not all topics need to be written in style, you may encounter situations that require background download parsing

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

The getJson method is as follows:

        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()
        }
Copy the code

FromJson method

inline fun <reified T> String? .fromJson(): T? {if(isNullOrEmpty()) {// Determine null and nullreturn null
    }
    returnTry {val clazz = T:: Java gson. FromJson (this, clazz) // gson catch (e: JsonSyntaxException) { e.printStackTrace() null } }Copy the code

You can write these two methods by yourself, but I provide a reference here. Because at the time of writing, the path is narrow and I don’t want to introduce too many additional libraries, so I decide to directly extract the package name for the Widget. We added a custom Widget IPlugin to the MaterialDesign Widget library that only supports AndroidX and SwipeRefreshLayout.

Class RefreshPlugin: IPlugin<SwipeRefreshLayout> {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) Override Fun individuate(view: SwipeRefreshLayout, attrName: String, colors: ArrayList<String>? ) {}}}Copy the code

This Json corresponds to

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

conclusion

This library is based on the Kotlin + DataBinding extension function that can use @bindAdapter annotation in the layout file XML, and then use the corresponding key to traverse to find the corresponding setting properties of different types of view. Because there is a lot of traversal, there will be performance problems. Overall performance is a defect. And if the project has a lot of custom layout or a lot of pictures, it is difficult to do this kind of solid color replacement, so this is just a small experiment, after all, try! How do you know if you don’t try