Mastering Android Drawables

Mastering Android Drawables

Over the past few years, the quality of apps in the Google Play Store has increased substantially. Developers are taking Android UI seriously and producing beautiful apps made of tons of graphics resources (Bitmaps, 9-patches, shapes, etc.) every day. To avoid developers the pain of handling all types of resources, the Android framework provides the Drawable abstraction.

In this session we will deep dive into the android.graphics.drawable package and how the framework uses it internally to render scalable and responsive UIs. The Drawable notion is essential to the creation of UIs on Android but developers usually only scratch the surface of it. The class will help you to understand how framework-provided and custom Drawables can be used to make both your code cleaner and your app better, smoother and more polished.

E9bf8f6d5480ea2a2623df7dccfd1f70?s=128

Cyril Mottier

March 28, 2013
Tweet

Transcript

  1. Mtg ANDROID DRAWABLES wh Cyril Moi

  2. Tnk y f comg

  3. @cyrilmottier cyrilmottier.com

  4. None
  5. hp://w.capaetra.com/jobs

  6. An troduc  ANDROID DRAWABLES?

  7. An abstrac   ty  c be drawn 

    a Cv Drawable „ „ Canvas
  8. View Ctry  a Drawable don’t al wh msu/yt, 

    ly draw(Canvas)
  9. None
  10. None
  11. getIntrinsicWidth() getIntrinsicHeight()

  12. None
  13. getBounds()

  14. None
  15. setState(int[])

  16. setLevel(int)

  17. Drawable Usg a from XML ...

  18. 1 <ImageView 2 android:id="@+id/image_view" 3 android:layout_width="wrap_content" 4 android:layout_height="wrap_content" 5 android:src="@drawable/ic_share"

    />
  19.  from Java ...

  20. 1 ImageView imageView = (ImageView) 2 findViewById(R.id.image_view); 3 4 imageView.setImageResource(R.drawable.ic_share);

  21. Tt’s a folks!

  22. Tt’s a folks! wt k ...

  23. T Drawab LOADING MECHANISM

  24. A sg meod  ru m a

  25. Drawable getDrawable(int) android.content.res.Resources In

  26. Drawable object? Wt   turn

  27. Drawable

  28. Drawable BitmapDrawable ColorDrawable

  29. Drawable BitmapDrawable DrawableContainer ColorDrawable

  30. Drawable BitmapDrawable DrawableContainer ColorDrawable StateListDrawable LayerDrawable

  31. Drawable BitmapDrawable DrawableContainer ColorDrawable StateListDrawable LayerDrawable

  32. getDrawable(int) impmtn T

  33. File ends with «.xml»

  34. Inflate from XML File ends with «.xml» YES

  35. Inflate from XML File ends with «.xml» Create from stream

    to jpg/png YES NO
  36. Inflate from XML File ends with «.xml» Create from stream

    to jpg/png Return the new Drawable YES NO
  37. Inflate from XML File ends with «.xml» Create from stream

    to jpg/png Return the new Drawable YES NO
  38. Let’s py!

  39. 1 @Override 2 public void onCreate(Bundle savedInstanceState) { 3 super.onCreate(savedInstanceState);

    4 5 Resources r = getResources(); 6 Drawable d1 = r.getDrawable(R.drawable.ic_launcher); 7 Drawable d2 = r.getDrawable(R.drawable.ic_launcher); 8 9 Log.d(LOG_TAG, "d1: " + d1); 10 Log.d(LOG_TAG, "d2: " + d2); 11 }
  40. 03-23 16:39:29.745: DEBUG/Activity(19742): d1: android.graphics.drawable.BitmapDrawable@417e70c8 03-23 16:39:29.745: DEBUG/Activity(19742): d2: android.graphics.drawable.BitmapDrawable@417e7130

    tputs
  41. 03-23 16:39:29.745: DEBUG/Activity(19742): d1: android.graphics.drawable.BitmapDrawable@417e70c8 03-23 16:39:29.745: DEBUG/Activity(19742): d2: android.graphics.drawable.BitmapDrawable@417e7130

    tputs
  42. 03-23 16:39:29.745: DEBUG/Activity(19742): d1: android.graphics.drawable.BitmapDrawable@417e70c8 03-23 16:39:29.745: DEBUG/Activity(19742): d2: android.graphics.drawable.BitmapDrawable@417e7130

    tputs
  43. None
  44. getDrawable(int) always turns a new Drawab

  45. but ...

  46. 1 @Override 2 public void onCreate(Bundle savedInstanceState) { 3 super.onCreate(savedInstanceState);

    4 5 Resources r = getResources(); 6 Drawable d1 = r.getDrawable(R.drawable.ic_launcher); 7 Drawable d2 = r.getDrawable(R.drawable.ic_launcher); 8 9 Log.d(LOG_TAG, "d1.cs: " + d1.getConstantState()); 10 Log.d(LOG_TAG, "d2.cs: " + d2.getConstantState()); 11 }
  47. 03-23 16:42:51.345: DEBUG/Activity(19742): d1.cs: android.graphics.drawable.BitmapDrawable$BitmapState@418078a8 03-23 16:42:51.345: DEBUG/Activity(19742): d2.cs: android.graphics.drawable.BitmapDrawable$BitmapState@418078a8

    tputs
  48. 03-23 16:42:51.345: DEBUG/Activity(19742): d1.cs: android.graphics.drawable.BitmapDrawable$BitmapState@418078a8 03-23 16:42:51.345: DEBUG/Activity(19742): d2.cs: android.graphics.drawable.BitmapDrawable$BitmapState@418078a8

    tputs
  49. 03-23 16:42:51.345: DEBUG/Activity(19742): d1.cs: android.graphics.drawable.BitmapDrawable$BitmapState@418078a8 03-23 16:42:51.345: DEBUG/Activity(19742): d2.cs: android.graphics.drawable.BitmapDrawable$BitmapState@418078a8

    tputs
  50. Drawables ConstantState s ir

  51. None
  52. See http://www.curious-creature.org/2009/05/02/drawable-mutations/ for more information ConstantState may be a probm

    somem
  53. Drawable.mutate() Cag ConstantState copi 

  54. None
  55. Some NICE GOTCHAS

  56. <selector /> StateListDrawable <level-list /> <layer-list /> <transition /> <color

    /> <shape /> <scale /> <clip /> <rotate /> <animation-list /> <inset /> <bitmap /> <nine-patch /> LevelListDrawable LayerDrawable TransitionDrawable ColorDrawable GradientDrawable ScaleDrawable ClipDrawable RotateDrawable AnimationDrawable InsetDrawable BitmapDrawable NinePatchDrawable
  57. <selector /> StateListDrawable <level-list /> <layer-list /> <transition /> <color

    /> <shape /> <scale /> <clip /> <rotate /> <animation-list /> <inset /> <bitmap /> <nine-patch /> LevelListDrawable LayerDrawable TransitionDrawable ColorDrawable GradientDrawable ScaleDrawable ClipDrawable RotateDrawable AnimationDrawable InsetDrawable BitmapDrawable NinePatchDrawable <stupid-tag /> Resources.NotFoundException
  58. 1 <?xml version="1.0" encoding="utf-8"?> 2 <bitmap 3 xmlns:android="http://schemas.android.com/apk/res/android" 4 android:src="@drawable/stripes_bitmap"

    />
  59. 1 <?xml version="1.0" encoding="utf-8"?> 2 <bitmap 3 xmlns:android="http://schemas.android.com/apk/res/android" 4 android:src="@drawable/stripes_bitmap"

    />
  60. 1 <?xml version="1.0" encoding="utf-8"?> 2 <bitmap 3 xmlns:android="http://schemas.android.com/apk/res/android" 4 android:src="@drawable/stripes_bitmap"

    5 android:gravity="center" />
  61. 1 <?xml version="1.0" encoding="utf-8"?> 2 <bitmap 3 xmlns:android="http://schemas.android.com/apk/res/android" 4 android:src="@drawable/stripes_bitmap"

    5 android:tileMode="repeat" />
  62. 1 <?xml version="1.0" encoding="utf-8"?> 2 <bitmap 3 xmlns:android="http://schemas.android.com/apk/res/android" 4 android:src="@drawable/stripes_bitmap"

    5 android:tileMode="mirror" />
  63. 1 <?xml version="1.0" encoding="utf-8"?> 2 <selector 3 xmlns:android="http://schemas.android.com/apk/res/android"> 4 <item

    android:state_pressed="true"> 5 <color android:color="@color/highlight" /> 6 </item> 7 <item> 8 <color android:color="@android:color/transparent" /> 9 </item> 10 </selector>
  64. 1 <?xml version="1.0" encoding="utf-8"?> 2 <selector 3 xmlns:android="http://schemas.android.com/apk/res/android"> 4 <item

    android:state_pressed="true"> 5 <color android:color="@color/highlight" /> 6 </item> 7 <item> 8 <color android:color="@android:color/transparent" /> 9 </item> 10 </selector> On Jly Bn:
  65. 1 <?xml version="1.0" encoding="utf-8"?> 2 <selector 3 xmlns:android="http://schemas.android.com/apk/res/android"> 4 <item

    android:state_pressed="true"> 5 <color android:color="@color/highlight" /> 6 </item> 7 <item> 8 <color android:color="@android:color/transparent" /> 9 </item> 10 </selector> On Ggbad:
  66. None
  67. ColorDrawable  not cli prr  Heycomb

  68. 1 <?xml version="1.0" encoding="utf-8"?> 2 <selector 3 xmlns:android="http://schemas.android.com/apk/res/android"> 4 5

    <item android:state_pressed="true"> 6 <shape android:shape="rectangle"> 7 <solid android:color="@color/highlight" /> 8 </shape> 9 </item> 10 <item> 11 <color android:color="@android:color/transparent" /> 12 </item> 13 </selector> Use a solid col GraditDrawab std
  69. android.graphics.drawable Have a lk  Wt me?

  70. Advc DRAWABLE USAGE

  71. Usg a Drawable  a custom View  sy

  72. 1 public class DrawableView extends View { 2 private Drawable

    mDrawable; 3 4 public DrawableView(Context context) { super(context); } 5 6 public void setDrawable(Drawable d) { 7 if (d != mDrawable) { 8 mDrawable = d; 9 if (d != null) { 10 updateDrawableBounds(); 11 } 12 } 13 } 14 15 /** Update Drawable bounds with Drawable.setBounds(int, int, int, int) */ 16 private void updateDrawableBounds() { /* ... */ } 17 18 @Override 19 protected void onDraw(Canvas canvas) { 20 super.onDraw(canvas); 21 if (mDrawable != null) { 22 mDrawable.draw(canvas); 23 } 24 } 25 }
  73. AnimationDrawable Let’s use  wh  new View

  74. C e y wi ...

  75. None
  76. Drawable.Callback T sw:

  77. 1 public interface Callback { 2 public void invalidateDrawable(Drawable who);

    3 4 public void scheduleDrawable(Drawable who, 5 Runnable what, 6 long when); 7 8 public void unscheduleDrawable(Drawable who, 9 Runnable what); 10 }
  78. 1 public class View implements Drawable.Callback, 2 KeyEvent.Callback, 3 AccessibilityEventSource

    { 4 5 /** android.view.View code */ 6 7 }
  79. verifyDrawable(Drawable) Just use

  80. 1 public void setDrawable(Drawable d) { 2 if (d !=

    mDrawable) { 3 if (mDrawable != null) { 4 mDrawable.setCallback(null); 5 } 6 mDrawable = d; 7 if (d != null) { 8 d.setCallback(this); 9 updateDrawableBounds(); 10 } 11 } 12 } 13 14 @Override 15 protected boolean verifyDrawable(Drawable who) { 16 return super.verifyDrawable(who) || who == mDrawable; 17 } Ungt  pvus caback  avoid aks
  81. 1 public void setDrawable(Drawable d) { 2 if (d !=

    mDrawable) { 3 if (mDrawable != null) { 4 mDrawable.setCallback(null); 5 } 6 mDrawable = d; 7 if (d != null) { 8 d.setCallback(this); 9 updateDrawableBounds(); 10 } 11 } 12 } 13 14 @Override 15 protected boolean verifyDrawable(Drawable who) { 16 return super.verifyDrawable(who) || who == mDrawable; 17 } Regt  cut view   new caback
  82. 1 public void setDrawable(Drawable d) { 2 if (d !=

    mDrawable) { 3 if (mDrawable != null) { 4 mDrawable.setCallback(null); 5 } 6 mDrawable = d; 7 if (d != null) { 8 d.setCallback(this); 9 updateDrawableBounds(); 10 } 11 } 12 } 13 14 @Override 15 protected boolean verifyDrawable(Drawable who) { 16 return super.verifyDrawable(who) || who == mDrawable; 17 } D’t fget  ca  sup meod
  83. Fg VIEW HIERARCHY

  84. disclaimer: this is an imaginary screen

  85. 1 <activity 2 android:name="LoginActivity" 3 android:label="@string/app_name" 4 android:theme="@android:style/Theme.Holo.Light.NoActionBar"> 5 <intent-filter>

    6 <action android:name="android.intent.action.MAIN" /> 7 <category android:name="android.intent.category.LAUNCHER" /> 8 </intent-filter> 9 </activity> AndroidManifest.xml In
  86. 1 <?xml version="1.0" encoding="utf-8"?> 2 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4

    android:layout_height="match_parent" 5 android:background="@color/app_background" 6 android:padding="8dp"> 7 8 <ImageView 9 android:layout_width="wrap_content" 10 android:layout_height="wrap_content" 11 android:layout_marginBottom="24dp" 12 android:layout_gravity="center" 13 android:src="@drawable/logo" /> 14 15 <LinearLayout 16 android:layout_width="match_parent" 17 android:layout_height="48dp" 18 android:layout_gravity="bottom" 19 android:orientation="horizontal"> 20 21 <Button 22 android:layout_width="0dp" 23 android:layout_height="fill_parent" 24 android:layout_weight="1" 25 android:text="@string/sign_up" /> 26 27 <Button 28 android:layout_width="0dp" 29 android:layout_height="fill_parent" 30 android:layout_weight="1" 31 android:text="@string/sign_in" /> 32 33 </LinearLayout> 34 35 </FrameLayout>
  87. 1 <?xml version="1.0" encoding="utf-8"?> 2 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4

    android:layout_height="match_parent" 5 android:background="@color/app_background" 6 android:padding="8dp"> 7 8 <ImageView 9 android:layout_width="wrap_content" 10 android:layout_height="wrap_content" 11 android:layout_marginBottom="24dp" 12 android:layout_gravity="center" 13 android:src="@drawable/logo" /> 14 15 <LinearLayout 16 android:layout_width="match_parent" 17 android:layout_height="48dp" 18 android:layout_gravity="bottom" 19 android:orientation="horizontal"> 20 21 <Button 22 android:layout_width="0dp" 23 android:layout_height="fill_parent" 24 android:layout_weight="1" 25 android:text="@string/sign_up" /> 26 27 <Button 28 android:layout_width="0dp" 29 android:layout_height="fill_parent" 30 android:layout_weight="1" 31 android:text="@string/sign_in" /> 32 33 </LinearLayout> 34 35 </FrameLayout> Use  almt uss rt FrameLayt  hold  backgr
  88. 1 <?xml version="1.0" encoding="utf-8"?> 2 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4

    android:layout_height="match_parent" 5 android:background="@color/app_background" 6 android:padding="8dp"> 7 8 <ImageView 9 android:layout_width="wrap_content" 10 android:layout_height="wrap_content" 11 android:layout_marginBottom="24dp" 12 android:layout_gravity="center" 13 android:src="@drawable/logo" /> 14 15 <LinearLayout 16 android:layout_width="match_parent" 17 android:layout_height="48dp" 18 android:layout_gravity="bottom" 19 android:orientation="horizontal"> 20 21 <Button 22 android:layout_width="0dp" 23 android:layout_height="fill_parent" 24 android:layout_weight="1" 25 android:text="@string/sign_up" /> 26 27 <Button 28 android:layout_width="0dp" 29 android:layout_height="fill_parent" 30 android:layout_weight="1" 31 android:text="@string/sign_in" /> 32 33 </LinearLayout> 34 35 </FrameLayout> T logo c be csid   backgr
  89. 1 <?xml version="1.0" encoding="utf-8"?> 2 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4

    android:layout_height="match_parent" 5 android:background="@color/app_background" 6 android:padding="8dp"> 7 8 <ImageView 9 android:layout_width="wrap_content" 10 android:layout_height="wrap_content" 11 android:layout_marginBottom="24dp" 12 android:layout_gravity="center" 13 android:src="@drawable/logo" /> 14 15 <LinearLayout 16 android:layout_width="match_parent" 17 android:layout_height="48dp" 18 android:layout_gravity="bottom" 19 android:orientation="horizontal"> 20 21 <Button 22 android:layout_width="0dp" 23 android:layout_height="fill_parent" 24 android:layout_weight="1" 25 android:text="@string/sign_up" /> 26 27 <Button 28 android:layout_width="0dp" 29 android:layout_height="fill_parent" 30 android:layout_weight="1" 31 android:text="@string/sign_in" /> 32 33 </LinearLayout> 34 35 </FrameLayout> «Sign Up/In» bus e  ly actual ctt
  90. disclaimer: this is an imaginary screen

  91. disclaimer: this is an imaginary screen

  92. Drawable A -b be solu ...

  93. 1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout 3 xmlns:android="http://schemas.android.com/apk/res/android" 4 android:layout_width="match_parent"

    5 android:layout_height="48dp" 6 android:layout_gravity="bottom" 7 android:layout_margin="8dp" 8 android:orientation="horizontal"> 9 10 <Button 11 android:layout_width="0dp" 12 android:layout_height="fill_parent" 13 android:layout_weight="1" 14 android:text="@string/sign_up" /> 15 16 <Button 17 android:layout_width="0dp" 18 android:layout_height="fill_parent" 19 android:layout_weight="1" 20 android:text="@string/sign_in" /> 21 22 </LinearLayout>
  94. 1 <?xml version="1.0" encoding="utf-8"?> 2 <layer-list 3 xmlns:android="http://schemas.android.com/apk/res/android"> 4 5

    <item> 6 <shape android:shape="rectangle"> 7 <solid 8 android:color="@color/app_background" /> 9 </shape> 10 </item> 11 12 <item 13 android:bottom="48dp"> 14 <bitmap 15 android:src="@drawable/logo" 16 android:gravity="center" /> 17 </item> 18 19 </layer-list> Backgr col wh ct logo  p
  95. 1 <activity 2 android:name="LoginActivity" 3 android:label="@string/app_name" 4 android:theme="@style/Theme.Default.NoActionBar"> 5 <intent-filter>

    6 <action android:name="android.intent.action.MAIN" /> 7 <category android:name="android.intent.category.LAUNCHER" /> 8 </intent-filter> 9 </activity> AndroidManifest.xml In
  96. 1 <?xml version="1.0" encoding="utf-8"?> 2 <resources> 3 4 <style 5

    name="Theme.Default.NoActionBar" 6 parent="@android:style/Theme.Holo.Light.NoActionBar"> 7 <item name="android:windowBackground">@drawable/login</item> 8 </style> 9 10 </resources> res/values/themes.xml In
  97. disclaimer: this is an imaginary screen

  98. disclaimer: this is an imaginary screen

  99. + + + + + + + + + +

    + + + + + + + + + + + + + + + Mimiz yt & be uhg im
  100. T CONCLUSION

  101. Sp kg   View v ly

  102. DRAWABLES ARE light-weig

  103. DRAWABLES ARE system-cacd

  104. DRAWABLES ARE cfig-pdt

  105. DRAWABLES ARE sy  use

  106. DRAWABLES ARE sy awome :)

  107. CYRIL MOTTIER @cyrilmoi cyrilmoi.com