Share
Embed
Developing Themes with Style
The Android Theming system is powerful but easy to misuse. Proper use of it can make themes and styles easier to maintain, make branding updates less scary and make it straightforward to support dark modes.
This talk will start with a crash course in themes and styles. We’ll cover the platform, AppCompat and MaterialComponents functionality that you should know about and build upon. We’ll look at how to support both light and dark modes without going crazy. We’ll show how and when to create your own theme attributes, how to tint drawables, how to theme sub-sections of your screen and how to isolate your theme dependant resources to make them maintainable.
Nick Butcher
July 03, 2019
Transcript
269 slides
-
styles View Attributes <style name="Widget.Plaid.InlineActionButton" parent="…"> <item name="android:gravity">center_horizontal!"item> <item name="android:textAppearance">@style/TextAppearance.CommentAuthor!"item>
<item name="android:drawablePadding">@dimen/spacing_micro!"item> !"style> -
styles Resources <style name="Widget.Plaid.InlineActionButton" parent="…"> <item name="android:gravity">center_horizontal!"item> <item name="android:textAppearance">@style/TextAppearance.CommentAuthor!"item> <item
name="android:drawablePadding">@dimen/spacing_micro!"item> !"style> -
<style name="Widget.Plaid.InlineActionButton" parent="…"> <item name="android:gravity">center_horizontal!"item> <item name="android:textAppearance">@style/TextAppearance.CommentAuthor!"item> <item name="android:drawablePadding">@dimen/spacing_micro!"item> !"style>
styles Attribute Boolean Color Dimension Drawable Float Font Fraction String Resources -
themes Theme Attributes <style name="Plaid" parent="…"> <item name=" z colorPrimary
">@color/teal_500!"item> <item name="colorSecondary">@color/pink_200!"item> <item name="android:windowBackground">@color/background!"item> !"style> -
Widget.RadioButton Widget.RadioButton.Dark Widget.RadioButton.Pro Widget.RadioButton.Pro.Dark Widget.CheckBox Widget.CheckBox.Dark Widget.CheckBox.Pro Widget.CheckBox.Pro.Dark Widget.Switch Widget.Switch.Dark
Widget.Switch.Pro Widget.Switch.Pro.Dark Widget.Button Widget.Button.Dark Widget.Button.Pro Widget.Button.Pro.Dark -
Widget.Button Widget.Button.Dark Widget.Button.Pro Widget.Button.Pro.Dark Widget.Switch Widget.Switch.Dark Widget.Switch.Pro Widget.Switch.Pro.Dark Widget.CheckBox Widget.CheckBox.Dark
Widget.CheckBox.Pro Widget.CheckBox.Pro.Dark Widget.RadioButton Widget.RadioButton.Dark Widget.RadioButton.Pro Widget.RadioButton.Pro.Dark -
view attributes theme attributes Configuration specific to a single view
Semantic variables: provided/varied by theme May be used widely -
styles themes Map<view attribute, value> Associated with & applied to
single view Map<theme attribute, value> Associated with a Context Applied to a hierarchy Overlays ancestors -
themes Activity ViewGroup View View View ViewGroup <activity !!$ android:theme="@style/Theme.App.Foo"!#
<View !!$ android:theme="@style/MyTheme" !# -
Theme.MaterialComponents.Light - Theme.Owl.Pink themes <style> <item name="colorPrimary"… <item name="colorSecondary"… <item
name="toolbarStyle"… <item name="materialButtonStyle"… !!$ -
Theme.AppCompat.Light - Theme.MaterialComponents.Light !% Theme.Owl.Pink themes <style> <item name="colorPrimary"… <item
name="colorSecondary"… <item name="toolbarStyle"… <item name="materialButtonStyle"… <item name="buttonStyle"… <item name="selectableItemBackground"… !!$ -
Theme.Material.Light - Theme.AppCompat.Light !% Theme.MaterialComponents.Light !!& Theme.Owl.Pink themes <style> <item
name="colorPrimary"… <item name="colorSecondary"… <item name="toolbarStyle"… <item name="materialButtonStyle"… <item name="buttonStyle"… <item name="selectableItemBackground"… <item name="textCursorDrawable"… !!$ -
eme.Material.Light Theme.AppCompat.Light Theme.MaterialComponents.Light & Theme.Owl.Pink themes Theme.Material.Light - Theme.AppCompat.Light !%
Theme.MaterialComponents.Light !!& Theme.Owl.Blue tyle> <item name="colorPrimary"… <item name="colorSecondary"… <item name="toolbarStyle"… <item name="materialButtonStyle"… <item name="buttonStyle"… <item name="selectableItemBackground"… <item name="textCursorDrawable"… !!$ <style> <item name="colorPrimary"… <item name="colorSecondary"… <item name="toolbarStyle"… <item name="materialButtonStyle"… <item name="buttonStyle"… <item name="selectableItemBackground <item name="textCursorDrawable"… !"style> -
eme.Material.Light Theme.AppCompat.Light Theme.MaterialComponents.Light & Theme.Owl.Pink themes Theme.Material.Light - Theme.AppCompat.Light !%
Theme.MaterialComponents.Light !!& Theme.Owl.Blue tyle> <item name="colorPrimary"… <item name="colorSecondary"… <item name="toolbarStyle"… <item name="materialButtonStyle"… <item name="buttonStyle"… <item name="selectableItemBackground"… <item name="textCursorDrawable"… !!$ <style> <item name="colorPrimary"… <item name="colorSecondary"… <item name="toolbarStyle"… <item name="materialButtonStyle"… <item name="buttonStyle"… <item name="selectableItemBackground <item name="textCursorDrawable"… !"style> -
Theme.Material.Light - Theme.AppCompat.Light !% Theme.MaterialComponents.Light !!& Theme.Owl.Pink themes ThemeOverlay.Owl.Blue <style>
<item name="colorPrimary"… <item name="colorSecondary"… <item name="toolbarStyle"… <item name="materialButtonStyle"… <item name="buttonStyle"… <item name="selectableItemBackground"… <item name="textCursorDrawable"… !"style> <style name="ThemeOverlay.Owl.Blue" parent=""> <item name="colorPrimary"… <item name="colorSecondary"… !"style> -
@ColorInt fun Context.getThemeColor( @AttrRes themeAttrId: Int ): Int { return
obtainStyledAttributes( intArrayOf(themeAttrId) ).use { it.getColor(0, Color.MAGENTA) } } C R A F T Y T I P -
val themedContext = ContextThemeWrapper( context, R.style.ThemeOverlay_Owl_Blue ) val inflater =
LayoutInflater.from(themedContext) val view = inflater.inflate(!!$) C R A F T Y T I P -
styles themes Map<view attribute, value> Associated with & applied to
single view Map<theme attribute, value> Associated with a Context Applied to a hierarchy Overlays ancestors -
styles themes Map<view attribute, value> Associated with & applied to
single view Part of view attribute resolution Map<theme attribute, value> Associated with a Context Applied to a hierarchy Overlays ancestors -
public Button(Context context, AttributeSet attrs) { this(context, attrs, android.R.attr.buttonStyle); }
BUTTON <item name="buttonStyle">@style/Widget.Material.Light.Button!"item> -
C R A F T Y T I P <style
name="Theme.App"> <item name="toolbarStyle">@style/Widget.App.Toolbar!"item> !!$ Make styling opt out -
C R A F T Y T I P class
MyTextView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr: Int = 0, @StyleRes defStyleRes: Int = 0 ) : AppCompatTextView(context, attrs, defStyleAttr) -
C R A F T Y T I P class
MyTextView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr: Int = 0, @StyleRes defStyleRes: Int = 0 ) : AppCompatTextView(context, attrs, defStyleAttr) -
C R A F T Y T I P class
MyTextView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr: Int = 0, @StyleRes defStyleRes: Int = 0 ) : AppCompatTextView(context, attrs, defStyleAttr) -
C R A F T Y T I P class
MyTextView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr: Int = android.R.attr.textViewStyle, @StyleRes defStyleRes: Int = 0 ) : AppCompatTextView(context, attrs, defStyleAttr) -
View Style Default Style Theme <style name="Theme.App"> <item name="android: forceDarkAllowed">true
!"item> !!$ C R A F T Y T I P -
styles themes Map<view attribute, value> Associated with & applied to
single view Part of view attribute resolution Map<theme attribute, value> Associated with a Context Applied to a hierarchy Overlays ancestors -
styles themes <style name="Widget.App.Foo"> <item name="android:background"> ?attr/colorSurface !"item> !!$ <style!#
<style name="Theme.App"> <item name="toolbarStyle"> @style/Widget.App.Toolbar !"item> !!$ <style!# -
<resources> <color name="brand_blue">#01579B</color> </resources> Color tag Colors Select a color
from the Material palette -
<resources> <color name="brand_blue">#01579B</color> <color name="main_brand_color"> @color/brand_blue </color> </resources> Doesn't need
to be in same file Color tag Colors -
Colors Color state lists Stateful wrapper around colors Use same
state system as drawables BUTTON -
BUTTON Colors Color state lists Stateful wrapper around colors Use
same state system as drawables state_pressed = true -
Colors Color state lists Stateful wrapper around colors Use same
state system as drawables BUTTON state_enabled = false -
Colors Color state lists <selector> <item android:color="@color/brand_color_bright" android:state_checked="true" /> <item
android:color="@color/brand_color" /> </selector> res/color/csl_brand_checked.xml -
Colors Color state lists <selector> <item android:color="@color/brand_color_bright" android:state_checked="true" /> <item
android:color="@color/brand_color" /> </selector> -
Colors Color state lists <selector> <item android:color="@color/brand_color_bright" android:state_checked="true" /> <item
android:color="@color/brand_color" /> </selector> Default color -
Colors Color state lists Tip: order the items from most-specific
to least-specific CSL selects the first item which matches the state -
Colors Color state lists Tip: order the items from most-specific
to least-specific CSL selects the first item which matches the state <selector> <item android:color="@color/brand_color_bright" android:state_checked="true" /> <item android:color="@color/brand_color" /> </selector> -
Colors Color state lists Tip: order the items from most-specific
to least-specific CSL selects the first item which matches the state <selector> <item android:color="@color/brand_color" /> <item android:color="@color/brand_color_bright" android:state_checked="true" /> </selector> Will always be selected as it matches all states -
So why are colors so important? They're the basis of
(good) styling on Android -
<shape android:shape="rectangle" android:tint="@color/btn_colored_background_material"> <corners android:radius="?attr/buttonCornerRadius" /> <solid android:color="@color/white" /> <padding
android:left="@dimen/button_padding_horizontal_material" android:top="@dimen/button_padding_vertical_material" android:right="@dimen/button_padding_horizontal_material" android:bottom="@dimen/button_padding_vertical_material" /> </shape> -
Drawable tinting Added in API 21 (Lollipop) Tints a solid
color using a specified blending mode Can be stateful if using a ColorStateList Supported < API 21 in VectorDrawableCompat and DrawableCompat -
Fill != Tint Fill (dst) Tint (src) Result #FFFFFFFF SRC_IN
#FFFFFFFF null -
Fill != Tint Fill (dst) Tint (src) Result #FFFFFFFF #FF5384EC
SRC_IN #FF5384EC -
Fill != Tint Fill (dst) Tint (src) Result #FFFF00FF #FF5384EC
SRC_IN #FF5384EC -
Fill != Tint Fill (dst) Tint (src) Result #80FF00FF #FF5384EC
SRC_IN #805384EC -
Fill != Tint Fill (dst) Tint (src) Result #FFFF00FF #805384EC
SRC_IN #805384EC -
Fill != Tint Fill (dst) Tint (src) Result #805384EC SRC_IN
#405384EC #80FF00FF -
Fill != Tint Fill (dst) Tint (src) Result ?attr/colorControlNormal #B3FFFFFF
SRC_IN #B3FFFFFF #FFFFFF -
Fill != Tint Fill (dst) Tint (src) Result ?attr/colorControlNormal #B3FFFFFF
SRC_IN #5AFFFFFF #80FFFFFF -
Fill != Tint ADD CLEAR DARKEN DST DST_ATOP DST_IN DST_OUT
DST_OVER LIGHTEN MULTIPLY OVERLAY SCREEN SRC SRC_ATOP SRC_IN SRC_OUT SRC_OVER XOR -
Recommendation For drawables with a single color (e.g. icons, backgrounds)
#1 Fill using a solid color (e.g. black) #2 Tint using your chosen color/attribute -
<shape android:shape="rectangle" android:tint="@color/btn_colored_background_material"> <solid android:color="@color/white" /> </shape> <selector> <item android:state_enabled="false"
android:alpha="?attr/disabledAlpha" android:color="?attr/colorButtonNormal" /> <item android:color="?attr/colorAccent" /> </selector> -
ColorStateList Or use AppCompat: Alpha modulation added in API 21
(android:alpha) Can reference theme attributes from API 23 AppCompatResources.getColorStateList() -
Single item ColorStateLists Tip <!-- colorOnPrimary * 25% alpha -->
<color name="color_on_primary_25">...</color> <!-- colorOnPrimary * 50% alpha --> <color name="color_on_primary_50">...</color> Repeat for any colors you need an alpha variant of -
Single item ColorStateLists Tip <selector> <item android:alpha="0.25" android:color="?attr/colorOnPrimary" /> </selector>
res/color/color_on_primary_25.xml -
Single item ColorStateLists Tip <selector> <item android:alpha="0.25" android:color="?attr/colorOnPrimary" /> </selector>
res/color/color_on_primary_25.xml <selector> <item android:alpha="0.5" android:color="?attr/colorOnPrimary" /> </selector> res/color/color_on_primary_50.xml -
View tinting Views can override drawable tints <ImageView app:tint="@color/hot_pink" app:srcCompat="@drawable/ic_bug"
/> Can also call : ImageView.setImageTintList() -
View tinting Views can override drawable tints <ImageView app:tint="?attr/colorAccent" app:srcCompat="@drawable/ic_bug"
/> Can also call : ImageView.setImageTintList() -
Using colors as backgrounds Gotcha <color name="purple_200">...</color> <ImageView android:background="@color/purple_200" />
Internally, the color is wrapped into a ColorDrawable -
Gotcha <selector> <item android:alpha="0.25" android:color="?attr/colorOnPrimary" /> </selector> <ImageView android:background="@color/on_primary_25" />
res/color/on_primary_25.xml <ImageView android:background="@color/on_primary_25" /> Crash Using colors as backgrounds -
<selector> <item android:alpha="0.25" android:color="?attr/colorOnPrimary" /> </selector> <ImageView android:background="@drawable/rectangle" app:backgroundTint="@color/on_primary_25" />
res/color/on_primary_25.xml res/drawable/rectangle.xml <shape> <solid android:color="#FFFFFF" /> </shape> Using colors as backgrounds Gotcha -
ACCEPT REJECT Reset Settings This will reset your device to
factory settings. -
T H E M E S & S T Y
L E S Organisation -
T H E M E S & S T Y
L E S Organisation Chris' opinionated rules on: -
<resources> <color name="color_primary">...</color> <color name="color_primary_dark">...</color> <color name="color_accent">...</color> </style> <style name="Theme.App"
parent="…"> <item name="colorPrimary">@color/color_primary</item> <item name="colorPrimaryDark">@color/color_primary_dark</item> <item name="colorAccent">@color/color_accent</item> </style> Nein -
<style name="GreenToolbar" parent="Widget.AppCompat.Toolbar"> <item name="android:background">...</item> </style> <style name="Toolbar" parent="Widget.AppCompat.Toolbar"> <item
name="contentInsetStart">...</item> </style> -
#1 Use literal names for resources #2 Use consistent names
Rule #3 Layer your themes for re-use -
<style name="Theme.AppName"> ... <!-- Added in API 27 --> <item
name="android:windowLightNavigationBar">true</item> </style> values/themes.xml Something something error API level -
<style name="Theme.AppName"> <!-- Many many attributes --> <!-- Added in
API 27 --> <item name="android:windowLightNavigationBar">true</item> </style> <style name="Theme.AppName"> <!-- Many many attributes --> <!-- Added in API 27 --> <item name="android:windowLightNavigationBar">true</item> </style> values/themes.xml -
values/themes.xml <style name="Theme.AppName"> <!-- Many many attributes --> <!-- Added
in API 27 --> <item name="android:windowLightNavigationBar">true</item> </style> values-v27/themes.xml <style name="Theme.AppName"> <!-- Many many attributes --> <!-- Added in API 27 --> <item name="android:windowLightNavigationBar">true</item> </style> -
<style name="Theme.AppName"> <!-- Many many attributes --> </style> values/themes.xml <style
name="Theme.AppName"> <!-- Many many attributes --> <!-- Added in API 27 --> <item name="android:windowLightNavigationBar">true</item> </style> values-v27/themes.xml We now have multiple copies of the same theme -
We want re-use as much as possible The ideal solution
would be something like: @mixin -
The only tool we really have is inheritance, which means...
Layering This is how AppCompat is built We want re-use as much as possible -
Platform.Theme.MyApp Use only to set API level specific attributes <style
name="Platform.Theme.AppName" parent="Platform.V14.Theme.AppName" /> values/themes.xml <style name="Platform.V27.Theme.AppName" parent="Platform.V14.Theme.AppName"> <!-- Attributes specific to v27 --> </style> values-v27/themes.xml <style name="Platform.V14.Theme.AppName" parent="Theme.MaterialComponents" /> <style name="Platform.Theme.AppName" parent="Platform.V27.Theme.AppName" /> -
Platform.Theme.MyApp Use only to set API level specific attributes <style
name="Platform.Theme.AppName" parent="Platform.V14.Theme.AppName" /> values/themes.xml <style name="Platform.V27.Theme.AppName" parent="Platform.V14.Theme.AppName"> <!-- Attributes specific to v27 --> </style> values-v27/themes.xml <style name="Platform.V14.Theme.AppName" parent="Theme.MaterialComponents" /> <style name="Platform.Theme.AppName" parent="Platform.V27.Theme.AppName" /> -
Platform.Theme.MyApp Base.Theme.MyApp Everything common across your themes <style name="Base.Theme.AppName" parent="Platform.Theme.AppName">
<!-- Things common across app --> <item name="textAppearanceHeadline1">...</item> <item name="editTextStyle">...</item> </style> values/themes.xml Usually have one of these -
Platform.Theme.MyApp Base.Theme.MyApp Entry point Can have multiple of these, but
KISS Theme.MyApp <style name="Theme.AppName" parent="Base.Theme.AppName" /> values/themes.xml values-night/themes.xml <style name="Theme.AppName" parent="Base.Theme.AppName"> <!-- Overrides for dark theme --> </style> -
Theme.MyApp Platform Base.Theme.MyApp Platform Platform Theme.MyApp values-night values Platform.V27 Platform.V21
Platform.V14 values values-v21 values-v27 -
#1 Use literal names for resources #2 Use consistent names
#3 Layer your themes for re-use Rule #4 Split files based on purpose -
<resources xmlns:tools="http://schemas.android.com/tools"> <!-- Base application theme. --> <style name="AppTheme" parent="Theme.MaterialComponents.Light">
<!-- Customize your theme here. --> <item name="colorPrimary">@color/indigo</item> <item name="colorAccent">@color/indigo</item> <item name="colorControlActivated">?colorPrimary</item> <item name="toolbarStyle">@style/Widget.Toolbar</item> <item name="tabStyle">@style/Widget.IOSched.Tabs</item> <item name="android:textViewStyle">@style/Widget.IOSched.TextView</item> <item name="android:windowBackground">@color/white</item> <item name="android:statusBarColor">@color/status_bar</item> <item name="android:windowLightStatusBar" tools:targetApi="m">true</item> <item name="android:navigationBarColor">@color/indigo_dark</item> <item name="android:textColorTertiary">@color/text_color_tertiary</item> <item name="sessionListKeyline">@dimen/session_keyline</item> <item name="eventFilterViewStyle">@style/Widget.IOSched.EventFilters</item> styles.xml -
<style name="AppTheme.Speaker" parent="AppTheme.LightNav"> <item name="android:windowSharedElementEnterTransition">@transition/speaker_shared_enter <item name="sessionListKeyline">@dimen/margin_normal</item> </style> <style name="AppTheme.PopupTheme"
parent="ThemeOverlay.MaterialComponents.Light" /> <style name="Widget.AppBar" parent="@style/Widget.Design.AppBarLayout"> <item name="popupTheme">@style/AppTheme.PopupTheme</item> <item name="android:background">@color/white</item> <item name="android:elevation">8dp</item> <item name="android:orientation">vertical</item> </style> <style name="Widget.Toolbar" parent="Widget.AppCompat.Toolbar"> <item name="titleTextAppearance">@style/TextAppearance.IOSched.ToolbarTitle</item> <item name="popupTheme">@style/AppTheme.PopupTheme</item> </style> <style name="TextAppearance.IOSched.Body" parent="@style/TextAppearance.AppCompat.Body1"> <item name="android:textColor">?android:textColorSecondary</item> </style> <style name="TextAppearance.IOSched.ToolbarTitle" parent="@style/TextAppearance.Widget.AppCo <item name="android:fontFamily">@font/google_sans</item> styles.xml -
</style> <style name="Widget.Toolbar" parent="Widget.AppCompat.Toolbar"> <item name="titleTextAppearance">@style/TextAppearance.IOSched.ToolbarTitle</item> <item name="popupTheme">@style/AppTheme.PopupTheme</item> </style> <style
name="TextAppearance.IOSched.Body" parent="@style/TextAppearance.AppCompat.Body1"> <item name="android:textColor">?android:textColorSecondary</item> </style> <style name="TextAppearance.IOSched.ToolbarTitle" parent="@style/TextAppearance.Widget.AppCo <item name="android:fontFamily">@font/google_sans</item> </style> <style name="TextAppearance.IOSched.SessionTitle" parent="@style/TextAppearance.AppCompat.Su <item name="android:fontFamily">@font/google_sans</item> <item name="android:textStyle">bold</item> </style> <style name="TextAppearance.IOSched.ListSecondary" parent="@style/TextAppearance.AppCompat.B <item name="android:textColor">?android:textColorSecondary</item> </style> <style name="TextAppearance.IOSched.ReservationStatus" parent="@style/TextAppearance.IOSched styles.xml -
They've stuck to the rule of keeping each resource type
in seperate files styles.xml <style> in styles.xml <dimen> in dimens.xml <string> in strings.xml -
styles.xml They've stuck to the rule of keeping each resource
type in seperate files Not a bad idea, but it quickly leads to huge files -
Split files based on purpose themes.xml Themes and ThemeOverlays type.xml
TextAppearances, text size dimensions styles.xml Widget styles. Only. Easy mode dimens.xml colors.xml strings.xml -
Split files based on purpose Hard mode themes.xml type.xml styles.xml
dimens.xml colors.xml strings.xml shape.xml All resources used for shape appearance sys_ui.xml All booleans, colors used to control system-ui -
#1 Use literal names for resources #2 Use consistent names
#3 Layer your themes for re-use #4 Split files based on purpose -
colorOnSurface #000000 colorOnSecondary #000000 colorOnPrimary #ffffff colorError #b00020 colorSurface #ffffff
colorSecondaryVariant #ffc000 colorSecondary #ffde03 colorPrimaryVariant #0035c9 colorPrimary #0336ff -
colorOnSurface #000000 colorSurface #ffffff colorOnSecondary #000000 colorSecondary #ffde03 colorOnPrimary #ffffff
colorPrimary #0336ff colorError #b00020 colorSecondaryVariant #ffc000 colorPrimaryVariant #0035c9 -
<style name="Theme.Owl.Blue" parent="Theme.MaterialComponents.Light"> <item name="colorPrimary">@color/owl_blue_700!"item> <item name="colorPrimaryVariant">@color/owl_blue_800!"item> <item name="colorSecondary">@color/owl_yellow_500!"item> <item
name="colorSecondaryVariant">@color/owl_yellow_400!"item> !"style> -
<style name="Theme.Owl.Blue" parent="Theme.MaterialComponents.Light"> <item name="colorPrimary">@color/owl_blue_700!"item> <item name="colorPrimaryVariant">@color/owl_blue_800!"item> <item name="colorSecondary">@color/owl_yellow_500!"item> <item
name="colorSecondaryVariant">@color/owl_yellow_400!"item> !"style> -
<item name="textAppearanceHeadline1">@style/TextAppearance.Owl.Headline1!"item> <item name="textAppearanceHeadline2">@style/TextAppearance.Owl.Headline2!"item> <item name="textAppearanceHeadline3">@style/TextAppearance.Owl.Headline3!"item> <item name="textAppearanceHeadline4">@style/TextAppearance.Owl.Headline4!"item> <item name="textAppearanceHeadline5">@style/TextAppearance.Owl.Headline5!"item>
<item name="textAppearanceHeadline6">@style/TextAppearance.Owl.Headline6!"item> <item name="textAppearanceSubtitle1">@style/TextAppearance.Owl.Subtitle1!"item> <item name="textAppearanceSubtitle2">@style/TextAppearance.Owl.Subtitle2!"item> <item name=" textAppearanceBody1" >@style/TextAppearance.Owl.Body1!"item> <item name="textAppearanceBody2">@style/TextAppearance.Owl.Body2!"item> <item name="textAppearanceButton">@style/TextAppearance.Owl.Button!"item> <item name="textAppearanceCaption">@style/TextAppearance.Owl.Caption!"item> <item name="textAppearanceOverline">@style/TextAppearance.Owl.Overline!"item> -
<style name="Widget.MaterialComponents.BottomSheet" parent="!!$"> <item name="shapeAppearance"> ?attr/shapeAppearanceLargeComponent!"item> <item name="shapeAppearanceOverlay"> @style/ShapeAppearanceOverlay.MaterialComponents.BottomSheet!"item> !"style>
<style name="ShapeAppearanceOverlay.MaterialComponents.BottomSheet" parent=""> <item name="cornerSizeBottomRight">0dp!"item> <item name="cornerSizeBottomLeft">0dp!"item> !"style> -
val sheetBackground = MaterialShapeDrawable( ShapeAppearanceModel( lessonsSheet.context, R.style.ShapeAppearance_Owl_MinimizedSheet, 0 !( no
overlay !) ) ) sheetBackground.interpolation = ... -
val sheetBackground = MaterialShapeDrawable( ShapeAppearanceModel( lessonsSheet.context, R.style.ShapeAppearance_Owl_MinimizedSheet, 0 !( no
overlay !) ) ) sheetBackground.interpolation = ... -
- <item name="colorPrimary">@color/baseline_purple_500!"item> - <item name="colorSecondary">@color/baseline_purple_600!"item> + <item name="colorPrimary">@color/owl_blue_700!"item> +
<item name="colorSecondary">@color/owl_yellow_500!"item> - <item name="shapeAppearanceSmallComponent">@style/ShapeAppearance. - <item name="shapeAppearanceMediumComponent">@style/ShapeAppearance - <item name="shapeAppearanceLargeComponent">@style/ShapeAppearance. + <item name="shapeAppearanceSmallComponent">@style/ShapeAppearance. + <item name="shapeAppearanceMediumComponent">@style/ShapeAppearance + <item name="shapeAppearanceLargeComponent">@style/ShapeAppearance. - <item name="textAppearanceHeadline1">@style/TextAppearance.Baselin - <item name="textAppearanceHeadline2">@style/TextAppearance.Baselin -
- <item name="colorPrimary">@color/baseline_purple_500!"item> - <item name="colorSecondary">@color/baseline_purple_600!"item> + <item name="colorPrimary">@color/owl_blue_700!"item> +
<item name="colorSecondary">@color/owl_yellow_500!"item> - <item name="shapeAppearanceSmallComponent">@style/ShapeAppearance. - <item name="shapeAppearanceMediumComponent">@style/ShapeAppearance - <item name="shapeAppearanceLargeComponent">@style/ShapeAppearance. + <item name="shapeAppearanceSmallComponent">@style/ShapeAppearance. + <item name="shapeAppearanceMediumComponent">@style/ShapeAppearance + <item name="shapeAppearanceLargeComponent">@style/ShapeAppearance. - <item name="textAppearanceHeadline1">@style/TextAppearance.Baselin - <item name="textAppearanceHeadline2">@style/TextAppearance.Baselin j.mp/material-theme-builder -
DayNight First, tell AppCompat what mode to use Can be
done by calling either: AppCompatDelegate.setDefaultNightMode() getDelegate().setLocalNightMode() -
<style name="Theme.MyApp" parent="Theme.MaterialComponents.DayNight"> <item name="colorPrimary">@color/indigo_500</item> <item name="colorSecondary">@color/pink_a200</item> </style> <color name="indigo_500">...</color>
<color name="indigo_200">...</color> <color name="pink_a200">...</color> What do we set? -
We need something to alias between the colors <color name="color_primary">@color/indigo_500</color>
values/colors.xml <color name="color_primary">@color/indigo_200</color> values-night/colors.xml -
Theme.MyApp Platform Base.Theme.MyApp Platform Platform Theme.MyApp values-night values Platform.V27 Platform.V21
Platform.V14 values values-v21 values-v27 -
values/themes.xml values-night/themes.xml <style name="Theme.MyApp" parent="Base.Theme.MyApp"> <item name="colorPrimary">@color/indigo_500</item> </style> <style name="Theme.MyApp"
parent="Base.Theme.MyApp"> <item name="colorPrimary">@color/indigo_200</item> </style> -
<style name="Theme.MyApp" parent="Base.Theme.MyApp"> <item name="colorPrimary">@color/indigo_500</item> </style> <style name="Theme.MyApp" parent="Base.Theme.MyApp"> <item
name="colorPrimary">@color/indigo_200</item> </style> values/themes.xml values-night/themes.xml -
Don’t use bright colors for large surfaces because they can
emit too much brightness. “ -
<resources> <attr name="colorPrimarySurface" format="color" /> <attr name="colorOnPrimarySurface" format="color" /> </resources>
values/attrs.xml <style name="Theme.MyApp" parent="Base.Theme.MyApp"> <item name="colorPrimarySurface">?attr/colorPrimary</item> <item name="colorOnPrimarySurface">?attr/colorOnPrimary</item> </style> values/themes.xml values-night/themes.xml <style name="Theme.MyApp" parent="Base.Theme.MyApp"> <item name="colorPrimarySurface">?attr/colorSurface</item> <item name="colorOnPrimarySurface">?attr/colorOnSurface</item> </style> -
<style name="Widget.Owl.BottomNavigationView" parent="Widget.MaterialComponents.BottomNavigationView.Colored"> <item name="android:background">?attr/colorPrimarySurface</item> </style> <style name="Base.Theme.Owl" parent="Platform.Theme.Owl"> <!--
Things common across app --> <item name="bottomNavigationStyle"> @style/Widget.Owl.BottomNavigationView </item> </style> -
TL;DR Understand the difference between themes and styles Prefer theme
attributes when referencing resources Tint drawables rather than filling Name your resources consistently Layer your themes to easily vary its contents Use Material Theming to express your brand -
Nick Butcher @crafty @chrisbanes Chris Banes Thanks! Join us at
the Google booth for any questions