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

Themes, styles & more: from zero to hero

Themes, styles & more: from zero to hero

Haven’t you ever wanted to globally change the appearance of your EditTexts or the font face of all TextViews in your Android application? Themes and styles have always been at the heart of Android development when it comes to UI design. They have been around since API 1 after all and a lot of the Android features are built around them. But do you even know how themes and styles works? What is an attribute? And can you explain the difference between a style, a theme, a text appearance or a theme overlay?

If you have never heard of styles and themes or are not entirely sure you can answer the questions above, this presentation is made for you. This talk will dive deep into the Android styling and theming system. You will learn how to leverage it to customize your Android application, reduce duplication in your code and make your styles cleaner, more manageable and more reusable.

Cyril Mottier

April 23, 2018
Tweet

More Decks by Cyril Mottier

Other Decks in Programming

Transcript

  1. Themes, styles & more
    From zero to hero
    @cyrilmottier

    View full-size slide

  2. Yes, I’ll talk about boring XML
    while everybody talks about
    the trendy stuff like Kotlin.

    View full-size slide

  3. Yes, I’ll talk about boring XML
    while everybody talks about
    the trendy stuff like Kotlin. Sorry.

    – The guy talking to you

    View full-size slide

  4. AAPT 2
    \eɪeɪpiːtiː ˈtuː\
    AAPT stands for Android Asset Packaging Tool. It compiles application
    resources (e.g. AndroidManifest.xml, XML files, etc.) into binary assets.
    AAPT also allow clients to view, create, and update Android archives.

    View full-size slide

  5. The mother of all

    A key-value store
    with zero or more pairs

    View full-size slide

  6. The mother of all

    A key-value store
    with zero or more pairs

    View full-size slide

  7. <br/><item name="android:minHeight"><br/>@dimen/grid_size_300<br/></item><br/>

    View full-size slide

  8. <br/><item name="android:minHeight"><br/>@dimen/grid_size_300<br/></item><br/>
    Style’s name
    in upper camel case
    name="ListItem"

    View full-size slide

  9. <br/><item name="android:minHeight"><br/>@dimen/grid_size_300<br/></item><br/>
    Key-value
    pair

    @dimen/grid_size_300

    View full-size slide

  10. <br/><item name="android:minHeight"><br/>@dimen/grid_size_300<br/></item><br/>
    name="android:minHeight"
    Key

    View full-size slide

  11. <br/><item name="android:minHeight"><br/>@dimen/grid_size_300<br/></item><br/>
    @dimen/grid_size_300
    Value

    View full-size slide

  12. Keys in styles are called
    attributes

    View full-size slide

  13. Carry type information
    Belong to a namespace
    Hold optional constraints
    Keys in styles are called
    attributes

    View full-size slide

  14. ”any” by default
    (i.e. when undefined)
    Attributes
    Format types

    View full-size slide

  15. boolean
    color
    dimension
    float
    string
    integer
    enum
    flags
    reference
    fraction
    Attributes
    Format types

    View full-size slide

  16. boolean
    color
    dimension
    float
    string
    integer
    enum
    flags
    reference
    fraction
    Resource types
    Attributes
    Format types

    View full-size slide

  17. boolean
    color
    dimension
    float
    string
    integer
    enum
    flags
    reference
    fraction
    Special types
    Attributes
    Format types

    View full-size slide

  18. xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
    Attribute namespace
    Android framework namespace

    View full-size slide

  19. xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
    Attribute namespace
    Android framework namespace
    xmlns:android="http://schemas.android.com/apk/res/android"

    View full-size slide

  20. xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
    Attribute namespace
    Android framework namespace
    android
    android
    android

    View full-size slide

  21. Attribute namespace
    Global namespace schema
    xmlns:app="http://schemas.android.com/apk/"
    app:layout_constraintTop_toTopOf="parent" />

    View full-size slide

  22. Attribute namespace
    Global namespace schema
    xmlns:app="http://schemas.android.com/apk/"
    app:layout_constraintTop_toTopOf="parent" />
    xmlns:app="http://schemas.android.com/apk/"
    app

    View full-size slide

  23. Attribute namespace
    Global namespace schema
    xmlns:app="http://schemas.android.com/apk/res-auto"
    app:layout_constraintTop_toTopOf="parent" />
    xmlns:app="http://schemas.android.com/apk/res-auto"
    app

    View full-size slide

  24. Attribute namespace
    Global namespace schema
    xmlns:app="http://schemas.android.com/apk/res-auto"
    app:layout_constraintTop_toTopOf="parent" />
    xmlns:app="http://schemas.android.com/apk/res-auto"
    Type “appNs” in
    Android Studio
    app

    View full-size slide

  25. format="reference|color" />

    View full-size slide

  26. format="reference|color" />
    name="background"
    Attribute’s name

    View full-size slide

  27. format="reference|color" />
    Pipe-separated
    list of types
    format="reference|color"

    View full-size slide

  28. format="integer"
    min="1"
    max="12" />

    View full-size slide

  29. format="integer"
    min="1"
    max="12" />
    Upper/lower bounds only
    with integer typed attributes
    min="1"
    max="12"

    View full-size slide

  30. format="dimension|enum">



    View full-size slide

  31. Android comes with a bunch of attributes

    View full-size slide

  32. textColor
    actionBarStyle
    windowReturnTransition
    text
    src
    Naming
    General convention

    View full-size slide

  33. textColor
    actionBarStyle
    windowReturnTransition
    text
    src
    Naming
    General convention
    Lower
    camel case

    View full-size slide

  34. Naming
    Exc. #1: layout attributes
    layout_width
    layout_alignBaseline
    layout_marginHorizonal
    layout_weight
    layout_x

    View full-size slide

  35. Naming
    Exc. #1: layout attributes
    layout_width
    layout_alignBaseline
    layout_marginHorizonal
    layout_weight
    layout_x
    Prefixed with “layout_”
    then lower camel case

    View full-size slide

  36. state_focused
    state_checked
    state_enabled
    state_drag_hovered
    state_window_focused
    Naming
    Exc. #2: state attributes

    View full-size slide

  37. state_focused
    state_checked
    state_enabled
    state_drag_hovered
    state_window_focused
    Naming
    Exc. #2: state attributes
    Prefixed with “state_”
    then snake case

    View full-size slide

  38. Learn the rules like a pro,
    so you can break them
    like an artist.

    – Pablo Picasso

    View full-size slide

  39. hand_hour
    hand_minute
    ConstraintLayout
    Readability and ease of use
    AnalogClock
    Legacy & deprecated
    layout_constraintGuide_end
    layout_constraintHeight_max
    layout_constraintTop_toTopOf

    View full-size slide

  40. Styles can inherit from each other creating a
    style hierarchy

    View full-size slide

  41. parent="@style/Style.Wars" />

    View full-size slide

  42. Explicit parent
    parent="@ Style.Wars"
    style/ />

    View full-size slide

  43. Explicit parent
    parent="@Style.Wars" />

    View full-size slide

  44. Style.Wars"
    Explicit parent
    parent=" />

    View full-size slide


  45. Implicit parent
    Style.Wars

    View full-size slide

  46. parent="android:Theme.Material.Light" />

    View full-size slide

  47. parent="android:Theme.Material.Light" />
    Namespaced parent
    android:

    View full-size slide

  48. parent="" />

    View full-size slide

  49. parent="" />
    Parentless style
    parent=""

    View full-size slide

  50. Shorter is better
    No parent | Implicit parent | Shorter parent form

    View full-size slide

  51. is a universe of styles
    Android

    View full-size slide

  52. Text appearances
    is a universe of styles
    Android
    Themes
    Theme overlays
    Styles

    View full-size slide

  53. Style
    A bag of attributes used to style a View

    View full-size slide


  54. xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/list_selector"
    android:minHeight="@dimen/grid_size_400"
    android:paddingBottom="@dimen/spacing_100"
    android:paddingLeft="@dimen/spacing_200"
    android:paddingRight="@dimen/spacing_200"
    android:paddingTop="@dimen/spacing_100"
    android:text="@string/about" />

    View full-size slide


  55. xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/list_selector"
    android:minHeight="@dimen/grid_size_400"
    android:paddingBottom="@dimen/spacing_100"
    android:paddingLeft="@dimen/spacing_200"
    android:paddingRight="@dimen/spacing_200"
    android:paddingTop="@dimen/spacing_100"
    android:text="@string/about" />
    android:background="@drawable/list_selector"
    android:minHeight="@dimen/grid_size_400"
    android:paddingBottom="@dimen/spacing_100"
    android:paddingLeft="@dimen/spacing_200"
    android:paddingRight="@dimen/spacing_200"
    android:paddingTop="@dimen/spacing_100"

    View full-size slide

  56. <br/><item name="android:background">@drawable/list_selector</item><br/><item name="android:minHeight">@dimen/grid_size_400</item><br/><item name="android:paddingBottom">@dimen/spacing_100</item><br/><item name="android:paddingLeft">@dimen/spacing_200</item><br/><item name="android:paddingRight">@dimen/spacing_200</item><br/><item name="android:paddingTop">@dimen/spacing_100</item><br/>

    View full-size slide


  57. xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/list_selector"
    android:minHeight="@dimen/grid_size_400"
    android:paddingBottom="@dimen/spacing_100"
    android:paddingLeft="@dimen/spacing_200"
    android:paddingRight="@dimen/spacing_200"
    android:paddingTop="@dimen/spacing_100"
    android:text="@string/about" />

    View full-size slide


  58. xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    style="@style/ListItem"
    android:text="@string/about" />

    View full-size slide


  59. xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    style="@style/ListItem"
    android:text="@string/about" />

    View full-size slide


  60. xmlns:android="http://schemas.android.com/apk/res/android"
    style="@style/ListItem"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/about" />

    View full-size slide

  61. TextAppearance
    Text styling

    View full-size slide

  62. parent="TextAppearance.AppCompat">

    @font/museo_sans_rounded_500

    @color/blue_dark
    @dimen/font_size_large

    View full-size slide

  63. parent="TextAppearance.AppCompat">

    @font/museo_sans_rounded_500

    @color/blue_dark
    @dimen/font_size_large

    TextAppearance.
    Prefixed by
    “TextAppearance.”

    View full-size slide

  64. val tv = findViewById(R.id.text)
    tv.setTextAppearance(R.style.TextAppearance_Title)

    View full-size slide

  65. val tv = findViewById(R.id.text)
    tv.setTextAppearance(R.style.TextAppearance_Title)
    Modify TextView’s
    text appearance
    tv.setTextAppearance(R.style.TextAppearance_Title)

    View full-size slide

  66. val spannable = SpannableString("Styles rock!")
    spannable.setSpan(
    TextAppearanceSpan(context, R.style.TextAppearance_Title),
    0, spannable.length,
    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
    textView.text = spannable

    View full-size slide

  67. val spannable = SpannableString("Styles rock!")
    spannable.setSpan(
    TextAppearanceSpan(context, R.style.TextAppearance_Title),
    0, spannable.length,
    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
    textView.text = spannable
    TextAppearanceSpan(context, R.style.TextAppearance_Title)
    Create a Span using the
    given text appearance

    View full-size slide

  68. TextAppearance
    Supported attributes
    textColor
    textSize
    textStyle
    typeface
    fontFamily
    textColorHighlight
    textColorHint
    textColorLink
    textAllCaps
    shadowColor
    shadowDx
    shadowDy
    shadowRadius
    elegantTextHeight
    letterSpacing
    fontFeatureSettings

    View full-size slide

  69. TextAppearance
    TextAppearanceSpan attributes
    textColor
    textSize
    textStyle
    typeface
    fontFamily
    textColorHighlight
    textColorHint
    textColorLink
    textAllCaps
    shadowColor
    shadowDx
    shadowDy
    shadowRadius
    elegantTextHeight
    letterSpacing
    fontFeatureSettings

    View full-size slide

  70. style="@style/FormHeader"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textAppearance="@style/TextAppearance.Title"
    android:textColor="@color/colorAccent" />

    View full-size slide

  71. style="@style/FormHeader"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textAppearance="@style/TextAppearance.Title"
    android:textColor=“@color/blue_cerulean” />
    android:textAppearance="@style/TextAppearance.Title"
    Set TextView’s
    text appearance

    View full-size slide

  72. style="@style/FormHeader"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textAppearance="@style/TextAppearance.Title"
    android:textColor="@color/blue_cerulean" />
    android:textAppearance="@style/TextAppearance.Title"
    Least priority
    (applied first)

    View full-size slide

  73. style="@style/FormHeader"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textAppearance="@style/TextAppearance.Title"
    android:textColor="@color/blue_cerulean" />
    style="@style/FormHeader"
    Applied then

    View full-size slide

  74. style="@style/FormHeader"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textAppearance="@style/TextAppearance.Title"
    android:textColor="@color/blue_cerulean" />
    android:textColor="@color/blue_cerulean"
    Highest priority

    View full-size slide

  75. Theme
    Activity-level styling

    View full-size slide

  76. Theme
    Activity-level styling
    fun Context.setTheme(resId: Int)
    val Context.theme: Resources.Theme

    View full-size slide

  77. Theme
    Activity-level styling
    fun Context.setTheme(resId: Int)
    val Context.theme: Resources.Theme
    fun Context.setTheme(resId: Int)
    Apply the theme
    to the receiver’s Context

    View full-size slide

  78. Theme
    Activity-level styling
    fun Context.setTheme(resId: Int)
    val Context.theme: Resources.Theme
    fun Context.setTheme(resId: Int)
    Must be called before
    any View is inflated

    View full-size slide

  79. android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/Theme.City">


    View full-size slide

  80. android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/Theme.City">


    android:theme="@style/Theme.City"
    Application’s
    default theme

    View full-size slide

  81. android:name=".MainActivity"
    android:theme="@style/Theme.City.Deluxe" />

    View full-size slide

  82. android:name=".MainActivity"
    android:theme="@style/Theme.City.Deluxe" />
    Activity specific theme
    android:theme="@style/Theme.City.Deluxe"

    View full-size slide

  83. Colors
    android:colorPrimary
    android:textColorPrimary

    View full-size slide

  84. Window configuration
    android:windowBackground
    android:windowDisablePreview
    Colors
    android:colorPrimary
    android:textColorPrimary

    View full-size slide

  85. Window configuration
    android:windowBackground
    android:windowDisablePreview
    Colors
    android:colorPrimary
    android:textColorPrimary
    Default widget styles
    android:textViewStyle
    android:progressBarStyle

    View full-size slide

  86. Window configuration
    android:windowBackground
    android:windowDisablePreview
    Colors
    android:colorPrimary
    android:textColorPrimary
    Default widget styles
    android:textViewStyle
    android:progressBarStyle
    Drawables
    android:selectableItemBackground
    android:listDivider

    View full-size slide

  87. Drawables
    android:selectableItemBackground
    android:listDivider
    Preferences styles
    android:switchPreferenceStyle
    android:editTextPreferenceStyle
    Window configuration
    android:windowBackground
    android:windowDisablePreview
    Colors
    android:colorPrimary
    android:textColorPrimary
    Default widget styles
    android:textViewStyle
    android:progressBarStyle

    View full-size slide

  88. Text appearances
    android:textAppearanceSmall
    android:textAppearanceLargeInverse
    Preferences styles
    android:switchPreferenceStyle
    android:editTextPreferenceStyle
    Drawables
    android:selectableItemBackground
    android:listDivider
    Window configuration
    android:windowBackground
    android:windowDisablePreview
    Colors
    android:colorPrimary
    android:textColorPrimary
    Default widget styles
    android:textViewStyle
    android:progressBarStyle

    View full-size slide

  89. Text appearances
    android:textAppearanceSmall
    android:textAppearanceLargeInverse
    Preferences styles
    android:switchPreferenceStyle
    android:editTextPreferenceStyle
    Misc.
    android:dialogTheme
    android:disabledAlpha
    Drawables
    android:selectableItemBackground
    android:listDivider
    Window configuration
    android:windowBackground
    android:windowDisablePreview
    Colors
    android:colorPrimary
    android:textColorPrimary
    Default widget styles
    android:textViewStyle
    android:progressBarStyle

    View full-size slide

  90. Theme attributes
    can be referenced in layouts, styles, etc.

    View full-size slide

  91. <br/><item name=“android:background”><br/>@drawable/list_selector<br/></item><br/><item name="android:minHeight">@dimen/grid_size_400</item><br/><item name="android:paddingBottom">@dimen/spacing_100</item><br/><item name="android:paddingLeft">@dimen/spacing_200</item><br/><item name="android:paddingRight">@dimen/spacing_200</item><br/><item name="android:paddingTop">@dimen/spacing_100</item><br/>

    View full-size slide

  92. <br/><item name=“android:background”><br/>?android:attr/selectableItemBackground<br/></item><br/><item name="android:minHeight">@dimen/grid_size_400</item><br/><item name="android:paddingBottom">@dimen/spacing_100</item><br/><item name="android:paddingLeft">@dimen/spacing_200</item><br/><item name="android:paddingRight">@dimen/spacing_200</item><br/><item name="android:paddingTop">@dimen/spacing_100</item><br/>

    View full-size slide

  93. ?android:attr/selectableItemBackground

    View full-size slide

  94. Indicates a
    theme lookup
    ?android:attr/selectableItemBackground
    ?

    View full-size slide

  95. Theme attribute
    namespace
    ?android:attr/selectableItemBackground
    android:

    View full-size slide

  96. Attribute
    lookup
    ?android:attr/selectableItemBackground
    attr/

    View full-size slide

  97. Theme attribute
    actual name
    ?android:attr/selectableItemBackground
    selectableItemBackground

    View full-size slide

  98. ?android:selectableItemBackground

    View full-size slide

  99. ?colorPrimary

    View full-size slide

  100. fun Context.obtainStyledAttributes(
    set: AttributeSet,
    attrs: IntArray,
    defStyleAttr: Int,
    defStyleRes: Int
    ): TypedArray
    How it works
    Theming is not so magical after all…

    View full-size slide

  101. Theme overlays
    View-level theming

    View full-size slide

  102. android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:theme="@style/ThemeOverlay.Bazinga">


    View full-size slide

  103. android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:theme="@style/ThemeOverlay.Bazinga">


    Override the current theme
    with the given theme overlay
    android:theme="@style/ThemeOverlay.Bazinga"

    View full-size slide

  104. Few notes
    about theme overlays
    Based on ContextThemeWrapper
    A bag of theme attributes
    Propagated to descendants

    View full-size slide

  105. Compatibility
    Theming on different API levels

    View full-size slide

  106. with AppCompat
    Theme overlays
    API 21
    API 14 API 23

    View full-size slide

  107. with AppCompat
    Theme overlays
    Theme attributes
    API 21
    API 14 API 23

    View full-size slide

  108. with AppCompat
    Theme overlays
    Theme attributes
    in layout & styles
    API 21
    API 14 API 23

    View full-size slide

  109. with AppCompat
    Theme overlays
    Theme attributes
    in layout & styles
    in Drawables
    API 21
    API 14
    VD & AVD only
    API 23

    View full-size slide

  110. with AppCompat
    Theme overlays
    Theme attributes
    in layout & styles
    in Drawables
    in ColorStateList
    API 21
    API 14
    with AppCompat
    with AppCompat
    VD & AVD only
    API 23

    View full-size slide

  111. Theming & tinting
    in the real life

    View full-size slide

  112. layout/list_item_user.xml
    android:id="@+id/title"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textAppearance="@style/TextAppearance.XXH1"
    android:textColor="?zenlyListItemTextColorPrimary" />
    android:id="@+id/subtitle"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textAppearance="@style/TextAppearance.XH3"
    android:textColor="?zenlyListItemTextColorSecondary" />

    View full-size slide

  113. layout/list_item_user.xml
    android:id="@+id/title"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textAppearance="@style/TextAppearance.XXH1"
    android:textColor="?zenlyListItemTextColorPrimary" />
    android:id="@+id/subtitle"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textAppearance="@style/TextAppearance.XH3"
    android:textColor="?zenlyListItemTextColorSecondary" />
    android:textColor="?zenlyListItemTextColorPrimary"
    android:textColor="?zenlyListItemTextColorSecondary"

    View full-size slide

  114. android:id="@+id/search_icon"
    android:layout_width="@dimen/grid_size_350"
    android:layout_height="@dimen/grid_size_350"
    android:background="?selectableItemBackgroundBorderless"
    android:scaleType="centerInside"
    android:src="@drawable/ic_search"
    app:tint="?colorControlNormal" />
    layout/fragment_dashboard.xml

    View full-size slide

  115. android:id="@+id/search_icon"
    android:layout_width="@dimen/grid_size_350"
    android:layout_height="@dimen/grid_size_350"
    android:background="?selectableItemBackgroundBorderless"
    android:scaleType="centerInside"
    android:src="@drawable/ic_search"
    app:tint="?colorControlNormal" />
    layout/fragment_dashboard.xml
    android:background="?selectableItemBackgroundBorderless"
    app:tint="?colorControlNormal" />

    View full-size slide

  116. values/themes.xml values/theme_overlays.xml
    <br/><item name=“colorControlNormal"><br/>@color/blue_dark<br/></item><br/><item name=“zenlyListItemTextColorPrimary"><br/>@color/white<br/></item><br/><item name=“zenlyListItemTextColorSecondary"><br/>@color/blue_heavy<br/></item><br/><item name=“android:listDivider"><br/>@drawable/divider_blue_medium<br/></item><br/>
    <br/><item name="colorControlNormal"><br/>@color/gray_medium<br/></item><br/><item name="zenlyListItemTextColorPrimary"><br/>@color/blue_dark<br/></item><br/><item name="zenlyListItemTextColorSecondary"><br/>@color/gray_medium<br/></item><br/><item name="android:listDivider"><br/>@drawable/divider_gray_light<br/></item><br/>

    View full-size slide

  117. <br/><item name="colorControlNormal"><br/>@color/gray_medium<br/></item><br/><item name="zenlyListItemTextColorPrimary"><br/>@color/blue_dark<br/></item><br/><item name="zenlyListItemTextColorSecondary"><br/>@color/gray_medium<br/></item><br/><item name="android:listDivider"><br/>@drawable/divider_gray_light<br/></item><br/>
    values/themes.xml values/theme_overlays.xml
    <br/><item name=“colorControlNormal"><br/>@color/blue_dark<br/></item><br/><item name=“zenlyListItemTextColorPrimary"><br/>@color/white<br/></item><br/><item name=“zenlyListItemTextColorSecondary"><br/>@color/blue_heavy<br/></item><br/><item name=“android:listDivider"><br/>@drawable/divider_blue_medium<br/></item><br/>
    @color/gray_medium
    @color/blue_dark
    @color/gray_medium
    @drawable/divider_gray_light
    @color/blue_dark
    @color/white
    @color/blue_heavy
    @drawable/divider_blue_medium

    View full-size slide

  118. android:id="@+id/dashboard_bottom_sheet"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:theme="@style/ThemeOverlay.Zenly.Light"
    app:behavior_hideable="true"
    app:behavior_peekHeight="360dp"
    app:layout_behavior="android.support.design.widget.BottomSheetBehavior">


    layout/fragment_dashboard.xml

    View full-size slide

  119. android:id="@+id/dashboard_bottom_sheet"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:theme="@style/ThemeOverlay.Zenly.Light"
    app:behavior_hideable="true"
    app:behavior_peekHeight="360dp"
    app:layout_behavior="android.support.design.widget.BottomSheetBehavior">


    layout/fragment_dashboard.xml
    android:theme="@style/ThemeOverlay.Zenly.Light"

    View full-size slide

  120. Normal mode Night mode

    View full-size slide

  121. values/themes.xml
    <br/><item name="colorPrimary">@color/blue</item><br/><item name="colorPrimaryDark">@color/blue_dark</item><br/><item name="colorAccent">@color/green</item><br/><item name="colorControlNormal">@color/blue_dark</item><br/><item name="zenlyColorBackground">@color/blue</item><br/><item name="zenlyListItemTextColorPrimary">@color/white</item><br/><item name="zenlyListItemTextColorSecondary">@color/blue_heavy</item><br/><!-- ... --><br/>

    View full-size slide

  122. values-night/themes.xml
    <br/><item name="colorPrimary">@color/blue_dark</item><br/><item name="colorPrimaryDark">@color/blue_black</item><br/><item name="colorAccent">@color/green</item><br/><item name="colorControlNormal">@color/blue_heavy</item><br/><item name="zenlyColorBackground">@color/blue_dark</item><br/><item name="zenlyListItemTextColorPrimary">@color/white</item><br/><item name="zenlyListItemTextColorSecondary">@color/white_o50</item><br/><!-- ... --><br/>

    View full-size slide

  123. @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
    final int uiMode = AppPrefs.from(this).getUiMode();
    if (uiMode == AppPrefs.UI_MODE_NIGHT) {
    getDelegate().setLocalNightMode(
    AppCompatDelegate.MODE_NIGHT_YES);
    } else {
    getDelegate().setLocalNightMode(
    AppCompatDelegate.MODE_NIGHT_NO);
    }
    super.onCreate(savedInstanceState);
    // ...
    }

    View full-size slide

  124. Organizing styles
    Good practices for XML clean architecture

    View full-size slide

  125. <br/><item name="android:navigationBarColor">@color/black_o30</item><br/><item name="android:statusBarColor">@color/black_o30</item><br/><item name="android:windowBackground">@color/white</item><br/><item name="android:windowTranslucentNavigation">true</item><br/>
    values/themes.xml

    View full-size slide

  126. <br/><item name="android:navigationBarColor">@color/black_o30</item><br/><item name="android:statusBarColor">@color/black_o30</item><br/><item name="android:windowBackground">@color/white</item><br/><item name="android:windowTranslucentNavigation">true</item><br/>
    @color/black_o30
    @color/black_o30
    true
    values/themes.xml
    Lint warnings

    View full-size slide

  127. <br/><item name="android:windowBackground">@color/white</item><br/>
    values/themes.xml
    <br/><item name="android:navigationBarColor">@color/black_o30</item><br/><item name="android:statusBarColor">@color/black_o30</item><br/><item name="android:windowBackground">@color/white</item><br/><item name="android:windowTranslucentNavigation">true</item><br/>
    values-v21/themes.xml

    View full-size slide

  128. <br/><item name="android:windowBackground">@color/white</item><br/>
    values/themes.xml
    <br/><item name="android:navigationBarColor">@color/black_o30</item><br/><item name="android:statusBarColor">@color/black_o30</item><br/><item name="android:windowBackground">@color/white</item><br/><item name="android:windowTranslucentNavigation">true</item><br/>
    parent="Theme.AppCompat.Light.NoActionBar"
    parent="Theme.AppCompat.Light.NoActionBar"
    Parent style
    duplication
    values-v21/themes.xml

    View full-size slide

  129. <br/><item name="android:windowBackground">@color/white</item><br/>
    values/themes.xml
    <br/><item name="android:navigationBarColor">@color/black_o30</item><br/><item name="android:statusBarColor">@color/black_o30</item><br/><item name="android:windowBackground">@color/white</item><br/><item name="android:windowTranslucentNavigation">true</item><br/>
    Items
    duplication
    @color/white
    @color/white
    values-v21/themes.xml

    View full-size slide

  130. Theme.City
    values values-v21 values-v23
    Base.Theme.City Base.Theme.City Base.Theme.City
    Base.V23.Theme.City
    Base.V21.Theme.City
    Base.V1.Theme.City

    View full-size slide

  131. Theme.City
    values values-v21 values-v23
    Base.Theme.City Base.Theme.City Base.Theme.City
    Base.V23.Theme.City
    Base.V21.Theme.City
    Base.V1.Theme.City

    View full-size slide

  132. Theme.City
    values values-v21 values-v23
    Base.Theme.City Base.Theme.City Base.Theme.City
    Base.V23.Theme.City
    Base.V21.Theme.City
    Base.V1.Theme.City

    View full-size slide

  133. Theme.City
    values values-v21 values-v23
    Base.Theme.City Base.Theme.City Base.Theme.City
    Base.V23.Theme.City
    Base.V21.Theme.City
    Base.V1.Theme.City

    View full-size slide

  134. Theme.City
    values values-v21 values-v23
    Base.Theme.City Base.Theme.City Base.Theme.City
    Base.V23.Theme.City
    Base.V21.Theme.City
    Base.V1.Theme.City

    View full-size slide

  135. Theme.City
    values values-v21 values-v23
    Base.Theme.City Base.Theme.City Base.Theme.City
    Base.V23.Theme.City
    Base.V21.Theme.City
    Base.V1.Theme.City
    on API 22

    View full-size slide

  136. Theme.City
    values values-v21 values-v23
    Base.Theme.City Base.Theme.City Base.Theme.City
    Base.V23.Theme.City
    Base.V21.Theme.City
    Base.V1.Theme.City
    on API 26

    View full-size slide

  137. values/themes_base.xml
    parent="Theme.AppCompat.Light.NoActionBar">
    @color/white


    View full-size slide

  138. <br/><item name="android:navigationBarColor">@color/black_o30</item><br/><item name="android:statusBarColor">@color/black_o30</item><br/><item name="android:windowTranslucentNavigation">true</item><br/>

    values-v21/themes_base.xml

    View full-size slide

  139. values/themes.xml

    View full-size slide

  140. Prefix Filename
    text_appearances.xml
    themes.xml
    theme_overlays.xml
    TextAppearance.
    Theme.
    ThemeOverlay.
    Text appearances
    Themes
    Theme overlays
    styles_widget.xml
    Widget.
    Widget styles
    styles.xml
    -
    Custom styles

    View full-size slide

  141. Prefix Filename
    text_appearances_base.xml
    themes_base.xml
    theme_overlays_base.xml
    Base.TextAppearance.
    Base.Theme.
    Base.ThemeOverlay.
    Base text appearances
    Base themes
    Base theme overlays
    styles_widget_base.xml
    Base.Widget.
    Base widget styles
    styles_base.xml
    Base.
    Base custom styles

    View full-size slide

  142. Thank you!
    @cyrilmottier
    cyrilmottier.com

    View full-size slide