Pro Yearly is on sale from $80 to $50! »

The Curious Case of Android Button - Yelp Engineering

The Curious Case of Android Button - Yelp Engineering

Slides for the talk "The Curious Case of Android Button" by Nicola Corti, Android Engineer @ Yelp (Hamburg).

We interact with buttons every day to get coffee, order pizza, or start a new Gradle build. They may look simple and straightforward, but they have a fundamental role in our environment: create a sense of power.

We all love to feel empowered and we all hate being frustrated by bad design. Your users know this as well, they want to feel immediately rewarded when they interact with your UI, and they will quickly drop your app if they get annoyed.

At Yelp, we get tens of millions button clicks a day. At that kind of scale, every pixel matters. We tune every single aspect of our buttons to make them simple and powerful, but adjusting every single shadow cast, click animation, or color layer can be tricky. In this talk, we will provide a deep dive into the Android framework and the Support Library to understand how buttons are rendered.

3dc29e8cfc6ef333e2b41a1b0e826b57?s=128

Nicola Corti

April 24, 2018
Tweet

Transcript

  1. The Curious Case of Android Button Nicola Corti @cortinico nco@yelp.com

  2. None
  3. Yelp Mission Connecting people with great local businesses.

  4. About me Nicola Corti Android @ BizCore @cortinico Community Lover

    !✈
  5. CC-by hisgett

  6. None
  7. None
  8. None
  9. Consistency First

  10. Quick Button’s History

  11. None
  12. None
  13. Skeuomorphic UI • Android 2.3 - iOS 6 • Easy

    of use
  14. Flat UI • Windows Phone 7 (2010) • iOS 7+

    (2013) • Streamlined & lighter • Responsive UI
  15. Almost Flat (aka Flat 2.0) • Google Material Design (2014)

    • Shadows and
 Subtle Gradients
  16. How to craft your buttons?

  17. None
  18. Shape

  19. Rectangular Shadows Corners More about rounded corners: http://bit.ly/roundedcorner

  20. Credits: Material Design - Elevation & Shadows

  21. None
  22. Key + Ambient = Result

  23. None
  24. Brand Platform CC kevint3141

  25. Time to deep dive!

  26. What’s actually an Android Button?

  27. What’s actually an Android Button? java.lang.Object ↳ android.view.View ↳ android.widget.TextView

    ↳ android.widget.Button ↳ android.support.v7.widget.AppCompatButton
  28. What’s actually an Android Button? java.lang.Object ↳ android.view.View ↳ android.widget.TextView

    ↳ android.widget.Button ↳ android.support.v7.widget.AppCompatButton
  29. What’s actually an Android Button? java.lang.Object ↳ android.view.View ↳ android.widget.TextView

    ↳ android.widget.Button ↳ android.support.v7.widget.AppCompatButton
  30. What’s actually an Android Button? java.lang.Object ↳ android.view.View ↳ android.widget.TextView

    ↳ android.widget.Button ↳ android.support.v7.widget.AppCompatButton
  31. What’s actually an Android Button? java.lang.Object ↳ android.view.View ↳ android.widget.TextView

    ↳ android.widget.Button ↳ android.support.v7.widget.AppCompatButton
  32. What’s actually an Android Button? java.lang.Object (554) ↳ android.view.View (26488)

    ↳ android.widget.TextView (11969) ↳ android.widget.Button (182) ↳ android.support.v7.widget.AppCompatButton (187)
  33. Just a Button

  34. Just a Button <Button />

  35. Just a Button <Button android:layout_width="wrap_content" android:layout_height="wrap_content" />

  36. Just a Button <android.support.v7.widget.AppCompatButton android:layout_width="wrap_content" android:layout_height="wrap_content" />

  37. Just a Button /** * * <p>This will automatically be

    used when you use {@link Button} in your layouts */ public class AppCompatButton extends Button implements TintableBackgroundView, AutoSizeableTextView {
  38. Just a Button /** * * <p>This will automatically be

    used when you use {@link Button} in your layouts */ public class AppCompatButton extends Button implements TintableBackgroundView, AutoSizeableTextView {
  39. Just a Button /** * … * * <p>This will

    automatically be used when you use {@link Button} in your layouts * … */ public class AppCompatButton extends Button implements TintableBackgroundView, AutoSizeableTextView {
  40. 4 defaults styles for a Button

  41. 4 defaults styles for a Button <style name=“Widget.AppCompat.Button"/> <style name="Widget.AppCompat.Button.Colored"/>

    <style name="Widget.AppCompat.Button.Borderless"/> <style name="Widget.AppCompat.Button.Borderless.Colored"/>
  42. 4 defaults styles for a Button <style name=“Widget.AppCompat.Button"/> <style name="Widget.AppCompat.Button.Colored"/>

    <style name="Widget.AppCompat.Button.Borderless"/> <style name="Widget.AppCompat.Button.Borderless.Colored"/> API 21+
  43. 4 defaults styles for a Button <style name=“Widget.AppCompat.Button” parent="Base.Widget.AppCompat.Button"/> <style

    name="Widget.AppCompat.Button.Borderless"/> <style name="Widget.AppCompat.Button.Borderless.Colored"/> <style name="Widget.AppCompat.Button.Colored"/> <style name="Base.Widget.AppCompat.Button" parent="android:Widget.Material.Button"/> API 21+
  44. data/res/values/styles_material.xml

  45. data/res/values/styles_material.xml <!-- Bordered ink button --> <style name="Widget.Material.Button"> </style>

  46. data/res/values/styles_material.xml <!-- Bordered ink button --> <style name="Widget.Material.Button"> <item name="background">@drawable/btn_default_material</item>

    <item name="textAppearance">?attr/textAppearanceButton</item> <item name="minHeight">48dip</item> <item name="minWidth">88dip</item> <item name="stateListAnimator">@anim/ button_state_list_anim_material</item> <item name="focusable">true</item> <item name="clickable">true</item> <item name="gravity">center_vertical|center_horizontal</item> </style>
  47. data/res/values/styles_material.xml <!-- Bordered ink button --> <style name="Widget.Material.Button"> <item name="background">@drawable/btn_default_material</item>

    <item name="textAppearance">?attr/textAppearanceButton</item> <item name="minHeight">48dip</item> <item name="minWidth">88dip</item> <item name="stateListAnimator">@anim/ button_state_list_anim_material</item> <item name="focusable">true</item> <item name="clickable">true</item> <item name="gravity">center_vertical|center_horizontal</item> </style>
  48. data/res/values/styles_material.xml <!-- Bordered ink button --> <style name="Widget.Material.Button"> <item name="background">@drawable/btn_default_material</item>

    <item name="textAppearance">?attr/textAppearanceButton</item> <item name="minHeight">48dip</item> <item name="minWidth">88dip</item> <item name="stateListAnimator">@anim/ button_state_list_anim_material</item> <item name="focusable">true</item> <item name="clickable">true</item> <item name="gravity">center_vertical|center_horizontal</item> </style>
  49. data/res/values/styles_material.xml <!-- Bordered ink button --> <style name="Widget.Material.Button"> <item name="background">@drawable/btn_default_material</item>

    <item name="textAppearance">?attr/textAppearanceButton</item> <item name="minHeight">48dip</item> <item name="minWidth">88dip</item> <item name="stateListAnimator">@anim/ button_state_list_anim_material</item> <item name="focusable">true</item> <item name="clickable">true</item> <item name="gravity">center_vertical|center_horizontal</item> </style>
  50. data/res/drawable/btn_default_material.xml

  51. data/res/drawable/btn_default_material.xml <ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="?attr/colorControlHighlight"> <item android:drawable="@drawable/btn_default_mtrl_shape" /> </ripple>

  52. data/res/drawable/btn_default_material.xml <ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="?attr/colorControlHighlight"> <item android:drawable="@drawable/btn_default_mtrl_shape" /> </ripple>

  53. data/res/drawable/btn_default_material.xml <ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="?attr/colorControlHighlight"> <item android:drawable="@drawable/btn_default_mtrl_shape" /> </ripple>

  54. <ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="?attr/colorControlHighlight"> <inset android:insetLeft="4dp" android:insetTop="6dp" android:insetRight="4dp" android:insetBottom="6dp"> ... </inset>

    </ripple>
  55. <ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="?attr/colorControlHighlight"> <inset android:insetLeft="4dp" android:insetTop="6dp" android:insetRight="4dp" android:insetBottom="6dp"> <shape android:shape="rectangle"

    android:tint="?attr/colorButtonNormal"> ... </shape> </inset> </ripple>
  56. <ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="?attr/colorControlHighlight"> <inset android:insetLeft="4dp" android:insetTop="6dp" android:insetRight="4dp" android:insetBottom="6dp"> <shape android:shape="rectangle"

    android:tint="?attr/colorButtonNormal"> ... </shape> </inset> </ripple>
  57. <ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="?attr/colorControlHighlight"> <inset android:insetLeft="4dp" android:insetTop="6dp" android:insetRight="4dp" android:insetBottom="6dp"> <shape android:shape="rectangle"

    android:tint="?attr/colorButtonNormal"> ... </shape> </inset> </ripple>
  58. <ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="?attr/colorControlHighlight"> <inset android:insetLeft="4dp" android:insetTop="6dp" android:insetRight="4dp" android:insetBottom="6dp"> <shape android:shape="rectangle"

    android:tint="?attr/colorButtonNormal"> <corners android:radius="2dp" /> ... </shape> </inset> </ripple>
  59. <ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="?attr/colorControlHighlight"> <inset android:insetLeft="4dp" android:insetTop="6dp" android:insetRight="4dp" android:insetBottom="6dp"> <shape android:shape="rectangle"

    android:tint="?attr/colorButtonNormal"> <corners android:radius="2dp" /> <solid android:color="@color/white" /> ... </shape> </inset> </ripple>
  60. <ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="?attr/colorControlHighlight"> <inset android:insetLeft="4dp" android:insetTop="6dp" android:insetRight="4dp" android:insetBottom="6dp"> <shape android:shape="rectangle"

    android:tint="?attr/colorButtonNormal"> <corners android:radius="2dp" /> <solid android:color="@color/white" /> <padding android:left="8dp" android:top="4dp" android:right="8dp" android:bottom="4dp" /> </shape> </inset> </ripple>
  61. <ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="?attr/colorControlHighlight"> <inset android:insetLeft="@dimen/button_inset_horizontal_material" android:insetTop="@dimen/button_inset_vertical_material" android:insetRight="@dimen/button_inset_horizontal_material" android:insetBottom="@dimen/button_inset_vertical_material"> <shape android:shape="rectangle"

    android:tint="?attr/colorButtonNormal"> <corners android:radius="@dimen/control_corner_material" /> <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> </inset> </ripple>
  62. Typography

  63. Typography • Use a verb • On Android, buttons are

    ALL CAPS • Principle of Least astonishment
  64. Typography

  65. Typography <Button android:layout_width="wrap_content" android:layout_height="wrap_content"/>

  66. Typography <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Log In"/>

  67. Typography <Button style="@style/Base.Widget.AppCompat.Button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Log In"/>

  68. res/values/styles_material.xml

  69. res/values/styles_material.xml <!-- Bordered ink button --> <style name="Widget.Material.Button"> <item name="background">@drawable/btn_default_material</item>

    <item name="textAppearance">?attr/textAppearanceButton</item> <item name="minHeight">48dip</item> <item name="minWidth">88dip</item> <item name="stateListAnimator">@anim/ button_state_list_anim_material</item> <item name="focusable">true</item> <item name="clickable">true</item> <item name="gravity">center_vertical|center_horizontal</item> </style> <style name="TextAppearance.Material.Button"> <item name="textSize">@dimen/text_size_button_material</item> <item name="fontFamily">@string/font_family_button_material</item>
  70. res/values/styles_material.xml <!-- Bordered ink button --> <style name="Widget.Material.Button"> <item name="background">@drawable/btn_default_material</item>

    <item name="textAppearance">?attr/textAppearanceButton</item> <item name="minHeight">48dip</item> <item name="minWidth">88dip</item> <item name="stateListAnimator">@anim/ button_state_list_anim_material</item> <item name="focusable">true</item> <item name="clickable">true</item> <item name="gravity">center_vertical|center_horizontal</item> </style> <style name="TextAppearance.Material.Button"> <item name="textSize">@dimen/text_size_button_material</item> <item name="fontFamily">@string/font_family_button_material</item>
  71. <style name="Widget.Material.Button"> <item name="background">@drawable/btn_default_material</item> <item name="textAppearance">?attr/textAppearanceButton</item> <item name="minHeight">48dip</item> <item name="minWidth">88dip</item>

    <item name="stateListAnimator">@anim/ button_state_list_anim_material</item> <item name="focusable">true</item> <item name="clickable">true</item> <item name="gravity">center_vertical|center_horizontal</item> </style> <style name="TextAppearance.Material.Button"> <item name="textSize">@dimen/text_size_button_material</item> <item name="fontFamily">@string/font_family_button_material</item> <item name="textAllCaps">true</item> <item name=“textColor">?attr/textColorPrimary</item> </style>
  72. <style name="Widget.Material.Button"> <item name="background">@drawable/btn_default_material</item> <item name="textAppearance">?attr/textAppearanceButton</item> <item name="minHeight">48dip</item> <item name="minWidth">88dip</item>

    <item name="stateListAnimator">@anim/ button_state_list_anim_material</item> <item name="focusable">true</item> <item name="clickable">true</item> <item name="gravity">center_vertical|center_horizontal</item> </style> <style name="TextAppearance.Material.Button"> <item name="textSize">@dimen/text_size_button_material</item> <item name="fontFamily">@string/font_family_button_material</item> <item name="textAllCaps">true</item> <item name=“textColor">?attr/textColorPrimary</item> </style>
  73. Hierarchy

  74. Hierarchy • First, define your color palette • Use contrast

    to define hierarchy • Primary: high • Negative: medium • Secondary: low
  75. None
  76. None
  77. Hierarchy

  78. Hierarchy <style name=“Widget.AppCompat.Button"/> <style name="Widget.AppCompat.Button.Colored"/> <style name="Widget.AppCompat.Button.Borderless"/> <style name="Widget.AppCompat.Button.Borderless.Colored"/>

  79. Hierarchy <style name=“Widget.AppCompat.Button"/> <style name="Widget.AppCompat.Button.Colored"/> <style name="Widget.AppCompat.Button.Borderless"/> <style name="Widget.AppCompat.Button.Borderless.Colored"/> API

    23+
  80. Hierarchy <style name=“Widget.AppCompat.Button"/> <style name=“Widget.AppCompat.Button.Colored” parent=“Base.Widget.AppCompat.Button.Colored”/> <style name="Widget.AppCompat.Button.Borderless"/> <style name=“Widget.AppCompat.Button.Borderless.Colored"/>

    <style name=“Base.Widget.AppCompat.Button.Colored" parent=“android:Widget.Material.Button.Colored”/> API 23+
  81. data/res/values/styles_material.xml

  82. data/res/values/styles_material.xml <!-- Bordered ink button --> <style name="Widget.Material.Button"> ... </style>

  83. data/res/values/styles_material.xml <!-- Bordered ink button --> <style name="Widget.Material.Button"> </style> <!--

    Colored bordered ink button --> <style name="Widget.Material.Button.Colored"> </style>
  84. data/res/values/styles_material.xml <!-- Bordered ink button --> <style name="Widget.Material.Button"> </style> <!--

    Colored bordered ink button --> <style name="Widget.Material.Button.Colored"> <item name="background">@drawable/btn_colored_material</item> <item name="textAppearance">@style/ TextAppearance.Material.Widget.Button.Colored</item> </style>
  85. data/res/values/styles_material.xml <!-- Bordered ink button --> <style name="Widget.Material.Button"> </style> <!--

    Colored bordered ink button --> <style name="Widget.Material.Button.Colored"> <item name="background">@drawable/btn_colored_material</item> <item name="textAppearance">@style/ TextAppearance.Material.Widget.Button.Colored</item> </style>
  86. data/res/values/styles_material.xml <!-- Bordered ink button --> <style name="Widget.Material.Button"> </style> <!--

    Colored bordered ink button --> <style name="Widget.Material.Button.Colored"> <item name="background">@drawable/btn_colored_material</item> <item name="textAppearance">@style/ TextAppearance.Material.Widget.Button.Colored</item> </style>
  87. data/res/values/styles_material.xml <!-- Bordered ink button --> <style name="Widget.Material.Button"> </style> <!--

    Colored bordered ink button --> <style name="Widget.Material.Button.Colored"> <item name="background">@drawable/btn_colored_material</item> <item name="textAppearance">@style/ TextAppearance.Material.Widget.Button.Colored</item> </style>
  88. data/res/drawable/btn_colored_material.xml

  89. <inset xmlns:android="http://schemas.android.com/apk/res/android" android:insetLeft="@dimen/button_inset_horizontal_material" android:insetTop="@dimen/button_inset_vertical_material" android:insetRight="@dimen/button_inset_horizontal_material" android:insetBottom="@dimen/button_inset_vertical_material"> <ripple android:color="?attr/colorControlHighlight"> <item> <shape

    android:shape="rectangle" android:tint="@color/btn_colored_background_material"> <corners android:radius="@dimen/control_corner_material" /> <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> </item> </ripple> </inset> data/res/drawable/btn_colored_material.xml
  90. <inset xmlns:android="http://schemas.android.com/apk/res/android" android:insetLeft="@dimen/button_inset_horizontal_material" android:insetTop="@dimen/button_inset_vertical_material" android:insetRight="@dimen/button_inset_horizontal_material" android:insetBottom="@dimen/button_inset_vertical_material"> <ripple android:color="?attr/colorControlHighlight"> <item> <shape

    android:shape="rectangle" android:tint="@color/btn_colored_background_material"> <corners android:radius="@dimen/control_corner_material" /> <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> </item> </ripple> </inset> data/res/drawable/btn_colored_material.xml
  91. data/res/color/btn_colored_background_material.xml

  92. data/res/color/btn_colored_background_material.xml <selector xmlns:android="http://schemas.android.com/apk/res/ android"> </selector>

  93. data/res/color/btn_colored_background_material.xml <selector xmlns:android="http://schemas.android.com/apk/res/ android"> <item /> <item /> </selector>

  94. data/res/color/btn_colored_background_material.xml <selector xmlns:android="http://schemas.android.com/apk/res/ android"> <item android:state_enabled="false" /> <item /> </selector>

  95. data/res/color/btn_colored_background_material.xml <selector xmlns:android="http://schemas.android.com/apk/res/ android"> <item android:state_enabled="false" android:color="?attr/colorButtonNormal" /> <item />

    </selector>
  96. data/res/color/btn_colored_background_material.xml <selector xmlns:android="http://schemas.android.com/apk/res/ android"> <item android:state_enabled="false" android:alpha="?attr/disabledAlpha" android:color="?attr/colorButtonNormal" /> <item

    /> </selector>
  97. data/res/color/btn_colored_background_material.xml <selector xmlns:android="http://schemas.android.com/apk/res/ android"> <item android:state_enabled="false" android:alpha="?attr/disabledAlpha" android:color="?attr/colorButtonNormal" /> <item

    android:color="?attr/colorAccent" /> </selector>
  98. data/res/color/btn_colored_background_material.xml <selector xmlns:android="http://schemas.android.com/apk/res/ android"> <item android:state_enabled="false" android:alpha="?attr/disabledAlpha" android:color="?attr/colorButtonNormal" /> <item

    android:color="?attr/colorAccent" /> </selector>
  99. Secondary color? • Use a ThemeOverlay (XML only) • Use

    backgroundTint
  100. Secondary Color

  101. Secondary Color <android.support.v7.widget.AppCompatButton style="@style/Base.Widget.AppCompat.Button.Colored" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <android.support.v7.widget.AppCompatButton style="@style/Base.Widget.AppCompat.Button.Colored" android:layout_width="wrap_content" android:layout_height="wrap_content"/>

  102. Secondary Color <android.support.v7.widget.AppCompatButton style="@style/Base.Widget.AppCompat.Button.Colored" android:layout_width="wrap_content" android:layout_height="wrap_content" android:theme=“@style/MyRedButton"/> <android.support.v7.widget.AppCompatButton style="@style/Base.Widget.AppCompat.Button.Colored" android:layout_width="wrap_content"

    android:layout_height="wrap_content"/>
  103. Secondary Color <android.support.v7.widget.AppCompatButton style="@style/Base.Widget.AppCompat.Button.Colored" android:layout_width="wrap_content" android:layout_height="wrap_content" android:theme=“@style/MyRedButton"/> <android.support.v7.widget.AppCompatButton style="@style/Base.Widget.AppCompat.Button.Colored" android:layout_width="wrap_content"

    android:layout_height="wrap_content" android:backgroundTint=“@color/red"/>
  104. Secondary Color <android.support.v7.widget.AppCompatButton style="@style/Base.Widget.AppCompat.Button.Colored" android:layout_width="wrap_content" android:layout_height="wrap_content" android:theme=“@style/MyRedButton"/> <android.support.v7.widget.AppCompatButton style="@style/Base.Widget.AppCompat.Button.Colored" android:layout_width="wrap_content"

    android:layout_height="wrap_content" android:backgroundTint=“@color/red"/>
  105. Secondary Color <android.support.v7.widget.AppCompatButton style="@style/Base.Widget.AppCompat.Button.Colored" android:layout_width="wrap_content" android:layout_height="wrap_content" android:theme=“@style/MyRedButton"/> <android.support.v7.widget.AppCompatButton style="@style/Base.Widget.AppCompat.Button.Colored" android:layout_width="wrap_content"

    android:layout_height="wrap_content" android:backgroundTint=“@color/red"/>
  106. Secondary Color <android.support.v7.widget.AppCompatButton style="@style/Base.Widget.AppCompat.Button.Colored" android:layout_width="wrap_content" android:layout_height="wrap_content" android:theme=“@style/MyRedButton"/> <android.support.v7.widget.AppCompatButton style="@style/Base.Widget.AppCompat.Button.Colored" android:layout_width="wrap_content"

    android:layout_height="wrap_content" android:backgroundTint=“@color/red"/>
  107. Secondary Color <android.support.v7.widget.AppCompatButton style="@style/Base.Widget.AppCompat.Button.Colored" android:layout_width="wrap_content" android:layout_height="wrap_content" android:theme=“@style/MyRedButton"/> <android.support.v7.widget.AppCompatButton style="@style/Base.Widget.AppCompat.Button.Colored" android:layout_width="wrap_content"

    android:layout_height="wrap_content" android:backgroundTint=“@color/red"/> <style name="MyRedButton" />
  108. Secondary Color <android.support.v7.widget.AppCompatButton style="@style/Base.Widget.AppCompat.Button.Colored" android:layout_width="wrap_content" android:layout_height="wrap_content" android:theme=“@style/MyRedButton"/> <android.support.v7.widget.AppCompatButton style="@style/Base.Widget.AppCompat.Button.Colored" android:layout_width="wrap_content"

    android:layout_height="wrap_content" android:backgroundTint=“@color/red"/> <style name="MyRedButton" parent=“ThemeOverlay.AppCompat.Light"/>
  109. Secondary Color <android.support.v7.widget.AppCompatButton style="@style/Base.Widget.AppCompat.Button.Colored" android:layout_width="wrap_content" android:layout_height="wrap_content" android:theme=“@style/MyRedButton"/> <android.support.v7.widget.AppCompatButton style="@style/Base.Widget.AppCompat.Button.Colored" android:layout_width="wrap_content"

    android:layout_height="wrap_content" android:backgroundTint=“@color/red"/> <style name="MyRedButton" parent="ThemeOverlay.AppCompat.Light"> <item name=“android:colorAccent">@color/red</item> </style>
  110. Secondary Color <android.support.v7.widget.AppCompatButton style="@style/Base.Widget.AppCompat.Button.Colored" android:layout_width="wrap_content" android:layout_height="wrap_content" android:theme=“@style/MyRedButton"/> <android.support.v7.widget.AppCompatButton style="@style/Base.Widget.AppCompat.Button.Colored" android:layout_width="wrap_content"

    android:layout_height="wrap_content" android:backgroundTint=“@color/red"/> <style name="MyRedButton" parent="ThemeOverlay.AppCompat.Light"> <item name=“android:colorAccent">@color/red</item> </style>
  111. All works fine but…? .isEnabled = false with ThemeOverlay with

    backgroundTint with ThemeOverlay with backgroundTint
  112. Secondary Color <android.support.v7.widget.AppCompatButton style="@style/Base.Widget.AppCompat.Button.Colored" android:layout_width="wrap_content" android:layout_height="wrap_content" android:theme=“@style/MyRedButton"/> <android.support.v7.widget.AppCompatButton style="@style/Base.Widget.AppCompat.Button.Colored" android:layout_width="wrap_content"

    android:layout_height="wrap_content" android:backgroundTint=“@color/red"/>
  113. data/res/color/btn_colored_background_material.xml <selector xmlns:android="http://schemas.android.com/apk/res/ android"> <item android:state_enabled="false" android:alpha="?attr/disabledAlpha" android:color="?attr/colorButtonNormal" /> <item

    android:color="?attr/colorAccent" /> </selector>
  114. data/res/color/btn_colored_background_material.xml <selector xmlns:android="http://schemas.android.com/apk/res/ android"> <item android:state_enabled="false" android:alpha="?attr/disabledAlpha" android:color="?attr/colorButtonNormal" /> <item

    android:color="@color/red" /> </selector>
  115. All works fine but…? • You need a ColorStateList
 for

    the backgroundTint • Delegate creations of drawables/CSL
 to a UtilClass
  116. Feedback

  117. Feedback • Buttons are alive! • They have states (ColorStateList/StateListDrawable)

    • Use Ripples (they handle states) • Animate Shadows (StateListAnimator)
  118. None
  119. None
  120. None
  121. Credits: Vadim Gomov - Dribbble Z-Value = Elevation + TranslationZ

  122. Shadow

  123. Shadow <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:elevation="2dp"/>

  124. Shadow <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:elevation="2dp"/>

  125. Shadow <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:elevation="8dp"/>

  126. Shadow <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:stateListAnimator="@null" android:elevation="8dp"/> Why?

  127. res/values/styles_material.xml

  128. res/values/styles_material.xml <!-- Bordered ink button --> <style name="Widget.Material.Button"> <item name="background">@drawable/btn_default_material</item>

    <item name="textAppearance">?attr/textAppearanceButton</item> <item name="minHeight">48dip</item> <item name="minWidth">88dip</item> <item name="stateListAnimator">@anim/ button_state_list_anim_material</item> <item name="focusable">true</item> <item name="clickable">true</item> <item name="gravity">center_vertical|center_horizontal</item> </style>
  129. res/values/styles_material.xml <!-- Bordered ink button --> <style name="Widget.Material.Button"> <item name="background">@drawable/btn_default_material</item>

    <item name="textAppearance">?attr/textAppearanceButton</item> <item name="minHeight">48dip</item> <item name="minWidth">88dip</item> <item name="stateListAnimator">@anim/ button_state_list_anim_material</item> <item name="focusable">true</item> <item name="clickable">true</item> <item name="gravity">center_vertical|center_horizontal</item> </style>
  130. data/res/anim/button_state_list_anim_material.xml

  131. <selector xmlns:android="http://schemas.android.com/apk/res/android"> </selector> data/res/anim/button_state_list_anim_material.xml

  132. <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" android:state_enabled=“true” /> <!-- rest state -->

    <item android:state_enabled=“true” /> <item /> </selector> data/res/anim/button_state_list_anim_material.xml
  133. <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" android:state_enabled="true"> <set> <objectAnimator android:propertyName="translationZ" android:duration="@integer/button_pressed_animation_duration" android:valueTo="@dimen/button_pressed_z_material"

    android:valueType="floatType"/> <objectAnimator android:propertyName="elevation" android:duration="0" android:valueTo="@dimen/button_elevation_material" android:valueType="floatType"/> </set> </item> <!-- rest state --> <item android:state_enabled=“true” /> <item /> </selector> data/res/anim/button_state_list_anim_material.xml 2dp + 6dp
  134. <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" android:state_enabled="true"> <set> <objectAnimator android:propertyName="translationZ" android:duration="@integer/button_pressed_animation_duration" android:valueTo="@dimen/button_pressed_z_material"

    android:valueType="floatType"/> <objectAnimator android:propertyName="elevation" android:duration="0" android:valueTo="@dimen/button_elevation_material" android:valueType="floatType"/> </set> </item> <!-- rest state --> <item android:state_enabled=“true” /> <item /> </selector> data/res/anim/button_state_list_anim_material.xml 2dp + 6dp
  135. <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" android:state_enabled="true"> <set> <objectAnimator android:propertyName="translationZ" android:duration="@integer/button_pressed_animation_duration" android:valueTo="@dimen/button_pressed_z_material"

    android:valueType="floatType"/> <objectAnimator android:propertyName="elevation" android:duration="0" android:valueTo="@dimen/button_elevation_material" android:valueType="floatType"/> </set> </item> <!-- rest state --> <item android:state_enabled=“true” /> <item /> </selector> data/res/anim/button_state_list_anim_material.xml 2dp + 6dp
  136. <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" android:state_enabled=“true” /> <!-- rest state -->

    <item android:state_enabled="true" /> <item /> </selector> data/res/anim/button_state_list_anim_material.xml 2dp + 6dp
  137. <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" android:state_enabled=“true” /> <!-- rest state -->

    <item android:state_enabled="true"> <set> <objectAnimator android:propertyName="translationZ" android:duration="@integer/button_pressed_animation_duration" android:valueTo="0" android:startDelay="@integer/button_pressed_animation_delay" android:valueType="floatType"/> <objectAnimator android:propertyName="elevation" android:duration="0" android:valueTo="@dimen/button_elevation_material" android:valueType="floatType" /> </set> </item> <item /> </selector> data/res/anim/button_state_list_anim_material.xml 2dp + 6dp 2dp
  138. <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" android:state_enabled=“true” /> <!-- rest state -->

    <item android:state_enabled="true"> <set> <objectAnimator android:propertyName="translationZ" android:duration="@integer/button_pressed_animation_duration" android:valueTo="0" android:startDelay="@integer/button_pressed_animation_delay" android:valueType="floatType"/> <objectAnimator android:propertyName="elevation" android:duration="0" android:valueTo="@dimen/button_elevation_material" android:valueType="floatType" /> </set> </item> <item /> </selector> data/res/anim/button_state_list_anim_material.xml 2dp + 6dp 2dp
  139. <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" android:state_enabled=“true” /> <!-- rest state -->

    <item android:state_enabled="true"> <set> <objectAnimator android:propertyName="translationZ" android:duration="@integer/button_pressed_animation_duration" android:valueTo="0" android:startDelay="@integer/button_pressed_animation_delay" android:valueType="floatType"/> <objectAnimator android:propertyName="elevation" android:duration="0" android:valueTo="@dimen/button_elevation_material" android:valueType="floatType" /> </set> </item> <item /> </selector> data/res/anim/button_state_list_anim_material.xml 2dp + 6dp 2dp
  140. <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" android:state_enabled=“true” /> <!-- rest state -->

    <item android:state_enabled=“true” /> <item /> </selector> data/res/anim/button_state_list_anim_material.xml 2dp + 6dp 2dp
  141. <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" android:state_enabled=“true” /> <!-- rest state -->

    <item android:state_enabled=“true” /> <item> <set> <objectAnimator android:propertyName="translationZ" android:duration="0" android:valueTo="0" android:valueType="floatType"/> <objectAnimator android:propertyName="elevation" android:duration="0" android:valueTo="0" android:valueType="floatType"/> </set> </item> </selector> data/res/anim/button_state_list_anim_material.xml 2dp + 6dp 2dp 0dp
  142. <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" android:state_enabled=“true” /> <!-- rest state -->

    <item android:state_enabled=“true” /> <item> <set> <objectAnimator android:propertyName="translationZ" android:duration="0" android:valueTo="0" android:valueType="floatType"/> <objectAnimator android:propertyName="elevation" android:duration="0" android:valueTo="0" android:valueType="floatType"/> </set> </item> </selector> data/res/anim/button_state_list_anim_material.xml 2dp + 6dp 2dp 0dp
  143. <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" android:state_enabled=“true” /> <!-- rest state -->

    <item android:state_enabled=“true” /> <item> <set> <objectAnimator android:propertyName="translationZ" android:duration="0" android:valueTo="0" android:valueType="floatType"/> <objectAnimator android:propertyName="elevation" android:duration="0" android:valueTo="0" android:valueType="floatType"/> </set> </item> </selector> data/res/anim/button_state_list_anim_material.xml 2dp + 6dp 2dp 0dp
  144. Takeaways

  145. Consistency first!

  146. Don’t be afraid to deep dive

  147. Buttons are alive!

  148. Thanks! CC-by hisgett

  149. We are hiring! www.yelp.com/careers/

  150. Nicola Corti @cortinico nco@yelp.com @YelpEngineering github.com/yelp yelp.com/careers engineeringblog.yelp.com