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

Efficient Android Layouts (GOTO Conference)

Daniel Lew
October 03, 2016

Efficient Android Layouts (GOTO Conference)

Talk given about Android layouts at GOTO Conference Copenhagen in 2016.

Daniel Lew

October 03, 2016
Tweet

More Decks by Daniel Lew

Other Decks in Programming

Transcript

  1. Efficient Android Layouts
    Dan Lew

    View Slide

  2. “Give me the place to stand, and I shall move the earth.”
    ~Archimedes

    View Slide

  3. “Give me a standing desk, and I shall write an Android app.”
    ~Me

    View Slide

  4. ViewGroups

    View Slide

  5. RelativeLayout
    ConstraintLayout
    LinearLayout
    FrameLayout
    Complex
    Simple

    View Slide

  6. RelativeLayout / ConstraintLayout
    • Position views relative to each other
    • RelativeLayout: Slow
    • ConstraintLayout: Alpha

    View Slide

  7. LinearLayout
    • Stack views vertically/horizontally
    • Weight distribution

    View Slide

  8. But I <3 RelativeLayout
    • LinearLayout == sometimes slow
    • RelativeLayout == always slow
    • ConstraintLayout == savior
    • Profile!

    View Slide

  9. FrameLayout
    • Positioning based on parent bounds
    • Overlapping Views
    • Clickable item backgrounds
    • Toggle container

    View Slide

  10. View Slide

  11. View Slide

  12. xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="wrap_content"

    android:orientation="horizontal"

    >


    layout="@layout/avatar_view"

    android:layout_width="48dp"

    android:layout_height="48dp"

    />




    View Slide

  13. xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="wrap_content"

    android:orientation="horizontal"

    >


    layout="@layout/avatar_view"

    android:layout_width="48dp"

    android:layout_height="48dp"

    />




    View Slide

  14. xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="wrap_content"

    android:orientation="horizontal"

    >


    android:id="@+id/avatar_view"

    android:layout_width="48dp"

    android:layout_height="48dp"

    />




    View Slide

  15. public class AvatarView extends FrameLayout {


    ImageView icon;

    TextView initials;


    public AvatarView(Context context, AttributeSet attrs) {

    super(context, attrs);


    LayoutInflater.from(context).inflate(R.layout.view_avatar, this);


    icon = (ImageView) findViewById(R.id.icon);

    initials = (TextView) findViewById(R.id.initials);

    }


    public void bind(Member member) {

    // ...Load icon into ImageView...

    // OR

    // ...Setup initials in TextView...

    }

    }

    View Slide

  16. android:layout_width="match_parent"

    android:layout_height="match_parent"

    >


    android:id="@+id/initials"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    />


    android:id="@+id/icon"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    />



    View Slide

  17. AvatarView
    (FrameLayout)
    FrameLayout
    ImageView
    TextView

    View Slide

  18. public class AvatarView extends FrameLayout {


    ImageView icon;

    TextView initials;


    public AvatarView(Context context, AttributeSet attrs) {

    super(context, attrs);


    LayoutInflater.from(context).inflate(R.layout.view_avatar, this);


    icon = (TextView) findViewById(R.id.icon);

    initials = (TextView) findViewById(R.id.initials);

    }


    public void bind(Member member) {

    // ...Load icon into ImageView...

    // OR

    // ...Setup initials in TextView...

    }

    }

    View Slide

  19. >


    android:id="@+id/initials"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    />


    android:id="@+id/icon"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    />



    View Slide

  20. AvatarView
    (Framelayout)
    ImageView
    TextView

    View Slide

  21. Custom Drawing

    View Slide

  22. Step #1: onMeasure()
    (…sometimes you can skip this…)

    View Slide

  23. onMeasure()
    • onMeasure() signature
    void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    • measureSpec - packed integer
    int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    int heightSize = MeasureSpec.getSize(heightMeasureSpec);

    View Slide

  24. MeasureSpec
    • EXACTLY - Must be that size
    • AT_MOST - Maximum width
    • UNDEFINED - Ideal width

    View Slide

  25. onMeasure()
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    int width;
    if (widthMode == MeasureSpec.EXACTLY) {
    width = widthSize;
    }
    else {
    int desiredWidth = 500; // Whatever calculation you want
    if (widthMode == MeasureSpec.AT_MOST) {
    width = Math.min(desiredWidth, widthSize);
    }
    else {
    width = desiredWidth;
    }
    }
    // ...to be continued...
    }

    View Slide

  26. onMeasure()
    @Override
    protected void onMeasure(int widthMeasureSpec,
    int heightMeasureSpec) {
    int width;
    int height;
    // ...Calculate width and height...
    setMeasuredDimension(width, height);
    }

    View Slide

  27. Step #2: onDraw()
    (this is up to you)

    View Slide

  28. Custom Drawables
    • onMeasure() -> getIntrinsicHeight() / getIntrinsicWidth()
    • onDraw() -> draw()
    • https://speakerdeck.com/cyrilmottier/mastering-android-drawables

    View Slide

  29. Styles

    View Slide

  30. Styles
    • No style

    • Style



    
<br/><item name="android:background">#FF0000</item>
<br/>

    View Slide

  31. Efficient
    • Semantically identical Views
    • All styled Views should change at
    once

    View Slide

  32. Not Efficient
    • Single-use styles
    • Coincidentally using the same attributes
    android:id="@+id/title"

    android:textColor="@color/blue_200"

    android:textColorHint=“@color/grey_500" />


    android:id="@+id/body"

    android:textColor="@color/blue_200"

    android:textColorHint=“@color/grey_500" />

    View Slide

  33. Not Efficient
    • Single-use styles
    • Coincidentally using the same attributes
    android:id="@+id/title"

    android:textColor="@color/blue_200"

    android:textColorHint=“@color/grey_500" />


    android:id="@+id/body"

    android:textColor="@color/blue_200"

    android:textColorHint=“@color/grey_500" />

    View Slide

  34. Not Efficient
    • Single-use styles
    • Coincidentally using the same attributes
    android:id="@+id/title"

    android:textColor="@color/blue_200"

    android:textColorHint=“@color/grey_500" />


    android:id="@+id/body"

    android:textColor="@color/blue_200"

    android:textColorHint=“@color/grey_500" />

    View Slide

  35. static final int NUM_COLUMNS = 3;


    static final int NUM_RETRIES = 3;
    static final int NUM_THREE = 3;

    View Slide

  36. // static final int NUM_COLUMNS = 3;


    // static final int NUM_RETRIES = 3;

    static final int NUM_THREE = 3;

    View Slide

  37. Themes

    View Slide

  38. Themes
    • Affect multiple Views at once
    • Default styles
    • Configure system-created Views

    View Slide

  39. • Application
    android:theme="@style/Theme.AppCompat">
    • Activity
    android:theme=“@style/Theme.AppCompat.Light”>
    • View
    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"

    app:popupTheme="@style/ThemeOverlay.AppCompat.Light"

    />

    View Slide

  40. AppCompat
    • Material on all devices
    • Baseline themes/styles
    • Enables View theming pre-Lollipop

    View Slide

  41. 
<br/><item name="colorPrimary">#F00</item>
<br/><item name="colorPrimaryDark">#0F0</item>
<br/><item name="colorControlNormal">#00F</item>
<br/>

    View Slide

  42. 
<br/><item name="buttonStyle">@style/MyButton</item>
<br/><item name="android:spinnerItemStyle">@style/MySpinnerItem</item>
<br/>
<br/><item name="android:textAppearance">@style/MyText</item>
<br/><item name="android:textAppearanceInverse">@style/MyTextInverse</item>
<br/>

    View Slide

  43. 
<br/><item name="selectableItemBackground">@drawable/bg</item>
<br/>


    View Slide

  44. Resources

    View Slide

  45. v24
    port
    xxxhdpi
    w411dp
    h731dp
    en_US

    View Slide

  46. View Slide

  47. View Slide

  48. View Slide

  49. View Slide

  50. Resource Qualifier System
    • Define alternative resources for device configurations
    • Android automatically picks correct resource

    View Slide

  51. View Slide

  52. values-sw600dp-port
    values-port
    values-sw600dp
    values
    not sw600dp or portrait
    not sw600dp
    not portrait

    View Slide

  53. Resources as code
    • Resource == parameter
    • Parameter <-- device configuration

    View Slide

  54. int square() {

    return 8 * 8;

    }

    int squareLarge() {

    return 16 * 16;

    }
    int square(int num) {

    return num * num;

    }
    VS

    View Slide

  55. getResources().getBoolean(R.bool.is_portrait)
    false
    true

    View Slide

  56. setContentView(R.layout.activity_main)

    View Slide

  57. xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    >









    View Slide

  58. xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    >









    View Slide

  59. android:layout_width="wrap_content"

    android:layout_height="match_parent"

    android:textSize="24sp"

    android:textColor="#FF00FF"

    />
    android:layout_width="wrap_content"

    android:layout_height="match_parent"

    android:textSize="16sp"

    android:textColor="#FF00FF"

    />

    View Slide

  60. android:layout_width="wrap_content"

    android:layout_height="match_parent"

    android:textSize="@dimen/welcome_text_size"

    android:textColor="#FF00FF"

    />
    24sp
    16sp

    View Slide

  61. 
<br/><item name="android:textSize">24sp</item><br/><item name="android:textColor">#FF00FF</item>
<br/>
    
<br/><item name="android:textSize">16sp</item><br/><item name="android:textColor">#FF00FF</item>
<br/>

    View Slide

  62. 
<br/><item name="android:textSize">@dimen/welcome_text_size</item>
<br/><item name="android:textColor">#FF00FF</item>
<br/>
    24sp
    16sp

    View Slide

  63. activity_main.xml
    TextView
    [email protected]/WelcomeText
    some_include.xml some_include.xml
    portrait
    default
    textSize=16sp textSize=24sp
    sw600dp
    default

    View Slide

  64. Drawables

    View Slide

  65. Image: https://www.flickr.com/photos/ufv/8042499199
    Design
    “I need this”

    View Slide

  66. Design
    “Not enough”
    “I tweaked the color,

    here’s those assets again.”

    View Slide

  67. Assets as code

    View Slide

  68. Drawable XML
    • Built into Android
    • Simple shapes
    • State selectors
    • Layer lists

    View Slide

  69. View Slide

  70. Button Outline

    android:shape="rectangle"

    >




    android:width="1dp"

    android:color="@color/white"

    />





    View Slide

  71. Button Selector




















    View Slide


  72. android:color="@color/blue_200"

    >












    Button Selector (v21)

    View Slide

  73. Button Versions
    button_welcome_outline.xml
    button_welcome.xml button_welcome.xml (with ripple)

    View Slide

  74. Vector drawables

    View Slide

  75. VectorDrawable
    !=
    SVG
    Design
    :(

    View Slide

  76. SVG -> VectorDrawable
    • Android Studio: New Vector Asset
    • Victor: github.com/trello/victor
    android {

    sourceSets {

    main {

    svg.srcDir 'src/main/svg'

    }

    }

    }

    View Slide

  77. vs

    View Slide

  78. View Slide

  79. Tinting Images
    • XML
    • Simple
    drawable.setColorFilter(color, PorterDuff.Mode.SRC_IN);
    • Comprehensive
    Drawable wrappedDrawable = DrawableCompat.wrap(drawable);

    DrawableCompat.setTint(wrappedDrawable, color);
    Not backwards compatible

    View Slide

  80. Thank You!
    • @danlew42
    • danlew.net
    • speakerdeck.com/dlew

    View Slide