Theme concatenation via gradle plugin
buildscript {
repositories {
jcenter()
}
dependencies {
classpath "com.github.ouchadam.themr:<latest-version>"
}
}
apply plugin: 'com.android.application'
apply plugin: 'com.github.ouchadam.themr'
res/values/themr.xml
<style name="PaletteLight">
<item name="brandColor">#008577</item>
</style>
<style name="PaletteDark">
<item name="brandColor">#000000</item>
</style>
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">?attr/brandColor</item>
</style>
build.gradle
themr {
combinations = ["AppTheme": ["PaletteLight", "PaletteDark"]]
}
override fun onCreate(savedInstanceState: Bundle?) {
setTheme(ThemR.get(R.style.PaletteDark, R.style.AppTheme))
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
A gradle plugin to generate combinations of themes based on a color palette. This is done by simplying inlining a palette style into a theme style.
The example above will generate
<style name="PaletteLight_AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="brandColor">#008577</item>
<item name="colorPrimary">?attr/brandColor</item>
</style>
<style name="PaletteDark_AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="brandColor">#000000</item>
<item name="colorPrimary">?attr/brandColor</item>
</style>
When an app uses mulitple themes
<style name="HomeTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/blue</item>
<item name="android:textColor">@color/black</item>
</style>
<style name="DetailsTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/red</item>
<item name="android:textColor">@color/white</item>
</style>
and different palettes are required such as a dark mode, management starts to get tricky, especially when more palettes are introduced. This can be achieved by copying all of the themes for each palette, rearchitecting the theme hierarchy or by programatically applying the colours.
<style name="HomeTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/blue</item>
<item name="android:textColor">@color/black</item>
</style>
<style name="DarkHomeTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/dark_blue</item>
<item name="android:textColor">@color/grey</item>
</style>
fun bindView(view) {
val themeWrapper = createThemeWrapperFor(Configuration.DARK_MODE) // find all the dark mode attributes
view.text.setTextColor(themeWrapper.colorPrimary)
}
themr
reduces the need for this boiler plate by allowing the palettes and themes to be decoupled and the combinations auto generated.
Combinations are declared as part of the plugin extension
themr {
combinations = [
"HomeTheme": ["LightMode", "DarkMode"],
"DetailsTheme": ["LightMode", "DarkMode"]
]
}
<style name="LightMode">
<item name="brandColor">@color/blue</item>
<item name="brandColorSecondary">@color/red</item>
<item name="brandTextColor">@color/black</item>
<item name="brandTextColorInverse">@color/white</item>
</style>
<style name="DarkMode">
<item name="brandColor">@color/dark_blue</item>
<item name="brandColorSecondary">@color/blue</item>
<item name="brandTextColor">@color/grey</item>
<item name="brandTextColorInverse">@color/black</item>
</style>
<style name="HomeTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">?attr/brandColor</item>
<item name="android:textColor">?attr/brandTextColor</item>
</style>
<style name="DetailsTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">?attr/brandColorSecondary</item>
<item name="android:textColor">?attr/brandTextColorInverse</item>
</style>
themr
will generate styles to buildDir/generated/res/themr
which the app can then consume via id, a resources.getIdentifier
lookup
or via generated helper ThemR.get(paletteId, themeId)
{Palette}_{Theme}
LightMode_HomeTheme
DarkMode_HomeTheme
LightMode_DetailsTheme
DarkMode_DetailsTheme