Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Leveraging themes and styles to implement dark mode on Android

Leveraging themes and styles to implement dark mode on Android

Presentation at DevFest Nürnberg, 2019 (https://devfestnuremberg.com/)

Abstract:
In recent times, light and dark modes have become popular in apps and as a result, users are starting to expect this feature in the apps they use.

In Android 10, users are now able to set their device theme to dark or light. In order to be able to deliver a consistent experience for users across many apps, it’s good for these apps to respond appropriately to the theme set by the user on their device.

In order to implement this feature in our apps, there are changes we need to make to our app.

In this talk, we will look at how we can collaborate with our designers and support this feature in our apps. We will cover topics ranging from adopting a design system, to leveraging material design components, making use of styles and themes, the process of migration from no-design-system to using a design system.

By the end of this talk, we would have covered in practical steps, what we need and how to go about supporting dark mode in our apps in incremental steps.

Resources & Links in the presentation
* Android Dev Summit, 2019: https://youtu.be/Owkf8DhAOSo?t=168
* “Consistency is Key - Working with a Design System” - Maria Neumayer - https://www.youtube.com/watch?v=THYqC3ACadQ
* Material Design Components on GitHub: https://github.com/material-components/material-components-android
* https://jorgecastillo.dev/dependency-inversion-on-android-theming
* https://material.io/develop/android/theming/color/

9ab0b3b080e75e0c03a0c643333f8b93?s=128

Segun Famisa

December 14, 2019
Tweet

Transcript

  1. Light or Dark using themes and styles to implement “dark

    mode” on Android
  2. segunfamisa segunfamisa.com

  3. Introduction

  4. Dark theme was recently introduced in

  5. Why should we care?

  6. Why should we care? Can reduce power usage on some

    devices
  7. Why should we care? Can reduce power usage on some

    devices Gentle on the eyes in low-light conditions
  8. Why should we care? Can reduce power usage on some

    devices Gentle on the eyes in low-light conditions More and more apps are supporting it
  9. Why should we care? Can reduce power usage on some

    devices Gentle on the eyes in low-light conditions More and more apps are supporting it Users will soon start to expect it in your app
  10. None
  11. None
  12. None
  13. None
  14. Key Concepts

  15. Styles & themes

  16. styles

  17. styles “map of View attributes to values” - Nick Butcher

    (Android Dev Summit 2019) https://youtu.be/Owkf8DhAOSo?t=168
  18. styles.xml <!-- style for form input field --> <style name="Widget.Form.Input">

    <item name="android:textColor">@color/text_color_gray</item> <item name="android:textSize">@dimen/normal_text</item> ... <item name="android:maxLines">1</item> </style>
  19. styles.xml <!-- style for form input field --> <style name="Widget.Form.Input">

    <item name="android:textColor">@color/text_color_gray</item> <item name="android:textSize">@dimen/normal_text</item> ... <item name="android:maxLines">1</item> </style> View attributes
  20. styles.xml <!-- style for form input field --> <style name="Widget.Form.Input">

    <item name="android:textColor">@color/text_color_gray</item> <item name="android:textSize">@dimen/normal_text</item> ... <item name="android:maxLines">1</item> </style> Values
  21. themes

  22. themes “map of theme attributes to values” - Nick Butcher

    (Android Dev Summit 2019) https://youtu.be/Owkf8DhAOSo?t=168
  23. themes <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> ... <item

    name="colorAccent">@color/colorAccent</item> </style>
  24. themes <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> ... <item

    name="colorAccent">@color/colorAccent</item> </style> theme attributes
  25. themes <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> ... <item

    name="colorAccent">@color/colorAccent</item> </style> Values
  26. Themes Styles

  27. Themes Styles Collection of attributes that specify the appearance for

    a single View
  28. Themes Collection of attributes applied to an entire app, activity,

    or view hierarchy—not just an individual view Styles Collection of attributes that specify the appearance for a single View
  29. Themes Collection of attributes applied to an entire app, activity,

    or view hierarchy—not just an individual view ViewGroups pass down themes to their descendants Styles Collection of attributes that specify the appearance for a single View
  30. Design Systems

  31. A design system helps to scale designs by making reusable

    components, widgets, and styles.
  32. A design system helps to scale designs by making reusable

    components, widgets, and styles. “Design systems” is a whole topic on its own.
  33. A design system helps to scale designs by making reusable

    components, widgets, and styles. “Design systems” is a whole topic on its own. “Consistency is Key - Working with a Design System” - Maria Neumayer, Deliveroo https://www.youtube.com/watch?v=THYqC3ACadQ
  34. A design system helps to scale designs by making reusable

    components, widgets, and styles. “Design systems” is a whole topic on its own. “Consistency is Key - Working with a Design System” - Maria Neumayer, Deliveroo https://www.youtube.com/watch?v=THYqC3ACadQ Material Design is a design system too
  35. Implementing Dark Theme

  36. Simple way

  37. Material Design Components https://github.com/material-components/material-components-android

  38. Theme colour attributes in Material Design

  39. Theme colour attributes in Material Design

  40. None
  41. colorPrimary colorOnPrimary

  42. colorSecondary colorOnSecondary

  43. None
  44. Theme typography attributes in Material Design

  45. None
  46. None
  47. None
  48. Understanding attributes

  49. Attributes are like interfaces, and the referenced colours are like

    implementations. Understanding attributes
  50. Attributes are like interfaces, and the referenced colours are like

    implementations. Allows for inversion of control. Only change the implementations to change the value. https://jorgecastillo.dev/dependency-inversion-on-android-theming Understanding attributes
  51. <!-- attribute from Material Design Components lib--> <attr name="colorOnSurface" format="color|reference"

    />
  52. <!-- attribute from Material Design Components lib--> <attr name="colorOnSurface" format="color|reference"

    /> <!-- set up in values/theme.xml --> <style name="MyTheme"..> <item name="colorSurface">@color/white</item> <item name="colorOnSurface">@color/blue_500</item> </style>
  53. <!-- attribute from Material Design Components lib--> <attr name="colorOnSurface" format="color|reference"

    /> <!-- set up in values/theme.xml --> <style name="MyTheme"..> <item name="colorSurface">@color/white</item> <item name="colorOnSurface">@color/blue_500</item> </style> <!-- use in a view --> <TextView ... android:textColor="?attr/colorOnSurface"/>
  54. <!-- attribute from Material Design Components lib--> <attr name="colorOnSurface" format="color|reference"

    /> <!-- set up in values/theme.xml --> <style name="MyTheme"..> <item name="colorSurface">@color/white</item> <item name="colorOnSurface">@color/blue_500</item> </style> <!-- use in a view --> <TextView ... android:textColor="?attr/colorOnSurface"/>
  55. <!-- attribute from Material Design Components lib--> <attr name="colorOnSurface" format="color|reference"

    /> <!-- set up in values/theme.xml --> <style name="MyTheme"..> <item name="colorSurface">@color/white</item> <item name="colorOnSurface">@color/blue_500</item> </style> <!-- use in a view --> <TextView ... android:textColor="?attr/colorOnSurface"/> <!-- attribute from Material Design Components lib--> <attr name="colorOnSurface" format="color|reference" /> <!-- set up in values-night/theme.xml --> <style name="MyTheme"..> <item name="colorSurface">@color/black</item> <item name="colorOnSurface">@color/red_500</item> </style> <!-- use in a view --> <TextView ... android:textColor="?attr/colorOnSurface"/>
  56. <!-- attribute from Material Design Components lib--> <attr name="colorOnSurface" format="color|reference"

    /> <!-- set up in values/theme.xml --> <style name="MyTheme"..> <item name="colorSurface">@color/white</item> <item name="colorOnSurface">@color/blue_500</item> </style> <!-- use in a view --> <TextView ... android:textColor="?attr/colorOnSurface"/> <!-- attribute from Material Design Components lib--> <attr name="colorOnSurface" format="color|reference" /> <!-- set up in values-night/theme.xml --> <style name="MyTheme"..> <item name="colorSurface">@color/black</item> <item name="colorOnSurface">@color/red_500</item> </style> <!-- use in a view --> <TextView ... android:textColor="?attr/colorOnSurface"/>
  57. None
  58. Fit your design into the material design system

  59. Look at how you can fit your design into existing

    material design system framework Fit your design into the material design system
  60. Look at how you can fit your design into existing

    material design system framework Identify what could be the colorPrimary, colorSecondary, colorOnPrimary, etc. Fit your design into the material design system
  61. https://material.io/develop/android/theming/color/

  62. https://material.io/develop/android/theming/color/

  63. Colour naming

  64. <color name="main_blue">..</color> Colour naming

  65. <color name="main_blue">..</color> <color name="main_blue_light">...</color> Colour naming

  66. <color name="main_blue">..</color> <color name="main_blue_light">...</color> <color name="main_blue_very_light">..</color> Colour naming

  67. <color name="main_blue">..</color> <color name="main_blue_light">...</color> <color name="main_blue_very_light">..</color> <color name="main_blue_so_very_light">..</color> Colour naming

  68. <color name="main_blue">..</color> <color name="main_blue_light">...</color> <color name="main_blue_very_light">..</color> <color name="main_blue_so_very_light">..</color> <color name="main_blue_extremely_so_very_light">..</color>

    Colour naming
  69. <color name="main_blue">..</color> <color name="main_blue_light">...</color> <color name="main_blue_very_light">..</color> <color name="main_blue_so_very_light">..</color> <color name="main_blue_extremely_so_very_light">..</color>

    <color name="main_blue_really_extremely_so_very_light">..</color> Colour naming
  70. Instead, it’s recommended to name your colours by shade

  71. Instead, it’s recommended to name your colours by shade <color

    name="main_blue_700">..</color> <color name="main_blue_500">...</color> <color name="main_blue_300">..</color> <color name="main_blue_200">..</color> <color name="main_blue_100">..</color> <color name="main_blue_50">..</color>
  72. Instead, it’s recommended to name your colours by shade <color

    name="main_blue_700">..</color> <color name="main_blue_500">...</color> <color name="main_blue_300">..</color> <color name="main_blue_200">..</color> <color name="main_blue_100">..</color> <color name="main_blue_50">..</color>
  73. None
  74. Putting it all together

  75. Theming with Material Design Components

  76. <!-- values/theme.xml --> <style name="MyTheme" parent="Theme.MaterialComponents.DayNight.Bridge"/> Theming with Material Design

    Components
  77. <!-- values/theme.xml --> <style name="MyTheme" parent="Theme.MaterialComponents.DayNight.Bridge"/> Theming with Material Design

    Components Make your theme extend DayNight from MDC
  78. <!-- values/theme.xml --> <style name="MyTheme" parent="Theme.MaterialComponents.DayNight.Bridge"/> Theming with Material Design

    Components Bridge: inherits from AppCompat and still brings new attributes from MDC
  79. <!-- values/theme.xml --> <style name="MyTheme" parent="Theme.MaterialComponents.DayNight.Bridge"> <item name="colorPrimary">@color/main_blue_500</item> <item name="colorPrimaryVariant">@color/main_blue_300</item>

    <item name="colorSecondary">@color/main_red_500</item> <item name="colorSecondaryVariant">@color/main_red_300</item> ... <item name="colorOnPrimary">@color/white</item> </style> Theming with Material Design Components
  80. <!-- values-night/theme.xml --> <style name="MyTheme" parent="Theme.MaterialComponents.DayNight.Bridge"> <item name="colorPrimary">@color/main_blue_300</item> <item name="colorPrimaryVariant">@color/main_blue_500</item>

    <item name="colorSecondary">@color/main_red_300</item> <item name="colorSecondaryVariant">@color/main_red_500</item> ... <item name="colorOnPrimary">@color/white</item> </style> Theming with Material Design Components
  81. <!-- values/theme.xml --> <style name="MyTheme" parent="..."> <item name="colorPrimary">...</item> <item name="colorPrimaryVariant">...</item>

    <item name="colorSecondary">...</item> <item name="colorSecondaryVariant">...</item> ... <item name="colorOnPrimary">...</item> </style> <!-- values-night/theme.xml --> <style name="MyTheme" parent="..."> <item name="colorPrimary">...</item> <item name="colorPrimaryVariant">...</item> <item name="colorSecondary">...</item> <item name="colorSecondaryVariant">...</item> ... <item name="colorOnPrimary">...</item> </style>
  82. <application ... android:theme="@style/MyTheme"> <activity>...</activity> ... </application> Theming with Material Design

    Components Theme applied to application
  83. <application ... <activity ... android:theme="@style/MyAppTheme.Special"> ... </activity> ... </application> Theming

    with Material Design Components Theme applied to activity
  84. application colorPrimary activity ViewGroup/View @style/Theme.Blue @style/Theme.Green @style/Theme.Red

  85. Prefer attributes to direct values

  86. Prefer attributes to direct values <TextView android:id="@+id/article_source_text" ... android:textColor="@color/light_grey"/> <TextView

    android:id="@+id/article_title" ... android:textColor="@color/dark_blue/> <TextView android:id="@+id/article_description" ... android:textColor="@color/light_grey"/>
  87. Prefer attributes to direct values <TextView android:id="@+id/article_source_text" ... android:textColor="@color/light_grey"/> <TextView

    android:id="@+id/article_title" ... android:textColor="@color/dark_blue/> <TextView android:id="@+id/article_description" ... android:textColor="@color/light_grey"/> We can’t easily create a night mode equivalent with these direct values
  88. <TextView android:id="@+id/article_source_text" ... android:textColor="?android:attr/textColorPrimary"/> <TextView android:id="@+id/article_title" ... android:textColor="?attr/colorOnSurface/> <TextView android:id="@+id/article_description"

    ... android:textColor="?android:attr/textColorSecondary"/> Prefer attributes to direct values
  89. <TextView android:id="@+id/article_source_text" ... android:textColor="?android:attr/textColorPrimary"/> <TextView android:id="@+id/article_title" ... android:textColor="?attr/colorOnSurface”/> <TextView android:id="@+id/article_description"

    ... android:textColor="?android:attr/textColorSecondary"/> Prefer attributes to direct values
  90. Handle icons and images

  91. Handle icons and images res ├── drawable │ ├── ...

    │ └── ic_back.png └── drawable-night ├── ... └── ic_back.png
  92. Handle icons and images res ├── drawable │ ├── ...

    │ └── ic_back.png └── drawable-night ├── ... └── ic_back.png
  93. VectorDrawable

  94. VectorDrawable <vector android:width="24dp" android:height="24dp" android:viewportWidth="24.0" android:viewportHeight="24.0"> <path android:fillColor="#FF000000" android:pathData="M20,11H7.8...-2z"/> </vector>

    light dark
  95. VectorDrawable <vector android:width="24dp" android:height="24dp" android:viewportWidth="24.0" android:viewportHeight="24.0"> <path android:fillColor="#FF000000" android:pathData="M20,11H7.8...-2z"/> </vector>

    light dark
  96. VectorDrawable <!-- values/theme.xml --> <style name="MyTheme"> ... <item name="colorOnPrimary">@color/black</item> </style>

  97. VectorDrawable <!-- values/theme.xml --> <style name="MyTheme"> ... <item name="colorOnPrimary">@color/black</item> </style>

    <!-- values-night/theme.xml --> <style name="MyTheme"> ... <item name="colorOnPrimary">@color/white</item> </style>
  98. VectorDrawable <vector android:width="24dp" android:height="24dp" android:viewportWidth="24.0" android:viewportHeight="24.0"> <path android:fillColor="?attr/colorOnPrimary" android:pathData="M20,11H7.8...-2z"/> </vector>

  99. VectorDrawable <vector android:width="24dp" android:height="24dp" android:viewportWidth="24.0" android:viewportHeight="24.0"> <path android:fillColor="?attr/colorOnPrimary" android:pathData="M20,11H7.8...-2z"/> </vector>

  100. VectorDrawable <vector android:width="24dp" android:height="24dp" android:viewportWidth="24.0" android:viewportHeight="24.0"> <path android:fillColor="?attr/colorOnPrimary" android:pathData="M20,11H7.8...-2z"/> </vector>

    light dark
  101. PNG icons <!-- Layout --> <ImageView android:src="@drawable/ic_arrow_back" ... app:tint="?attr/colorOnPrimary"/>

  102. PNG icons <!-- Layout --> <ImageView android:src="@drawable/ic_arrow_back" ... app:tint="?attr/colorOnPrimary"/>

  103. None
  104. Setting view attributes programmatically

  105. val textColor = ContextCompat.getColor(this, R.color.color_black) titleView.setTextColor(textColor) Setting view attributes programmatically

  106. Setting view attributes programmatically val textColor = ContextCompat.getColor(this, R.color.color_black) titleView.setTextColor(textColor)

    val textColor = getAttributeColor(R.attr.colorOnPrimary) titleView.setTextColor(textColor)
  107. val textColor = ContextCompat.getColor(this, R.color.color_black) titleView.setTextColor(textColor) val textColor = getColorAttribute(R.attr.colorOnPrimary)

    titleView.setTextColor(textColor) Setting view attributes programmatically
  108. Setting view attributes programmatically val textColor = getColorAttribute(R.attr.colorOnPrimary) ... @ColorInt

    fun Context.getColorAttribute(@AttrRes attr: Int): Int { return obtainStyledAttributes(intArrayOf(attr)) .use { it.getColor(0, Color.RED) } }
  109. Setting view attributes programmatically val textColor = getColorAttribute(R.attr.colorOnPrimary) ... @ColorInt

    fun Context.getColorAttribute(@AttrRes attr: Int): Int { return obtainStyledAttributes(intArrayOf(attr)) .use { it.getColor(0, Color.RED) } } Android Annotations: Lint warning when we pass a different kind of “integer”
  110. None
  111. Additional tips

  112. Extend Material Design Components with custom attributes Additional tips

  113. <!-- new attribute --> <attr name="specialAccentColor" format="color|reference" /> Extend Material

    Design Components with custom attributes Additional tips
  114. <!-- new attribute --> <attr name="specialAccentColor" format="color|reference" /> <!-- set

    up in theme --> <style name="MyTheme"..> <item name="specialAccentColor">@color/main_blue_350</item> </style> Extend Material Design Components with custom attributes Additional tips
  115. Extend Material Design Components with custom attributes Additional tips <!--

    new attribute --> <attr name="specialAccentColor" format="color|reference" /> <!-- set up in theme --> <style name="MyTheme"..> <item name="specialAccentColor">@color/main_blue_350</item> </style> <!-- use in a view --> <TextView ... android:textColor="?attr/specialAccentColor"/>
  116. Extend Material Design Components with custom attributes Use AppCompatDelegate for

    compatibility with old versions of android Additional tips
  117. Takeaways

  118. Takeaways • Be careful of which context you use •

    Styles are for single views, and themes are for multiple views or activities or for the application • Prefer using attributes over direct reference to values - esp. Colours • Get familiar with attributes from MDC library • Use AVDs for icons when possible
  119. Thank you!