$30 off During Our Annual Pro Sale. View Details »

Don't Make Me Repeat Myself

Don't Make Me Repeat Myself

A talk about intelligently re-using resources/code on Android. Given at AnDevCon.

Daniel Lew

May 01, 2012
Tweet

More Decks by Daniel Lew

Other Decks in Programming

Transcript

  1. 2012/05/01
    Don't Make Me Repeat Myself

    View Slide

  2. Problem
    • I'm  lazy
    • Easy  to  figure  out  basics  of  Android
    • Need  solu:ons  to  problems  quickly
    • End  up  fi>ng  square  peg  in  round  hole

    View Slide

  3. View Slide

  4. Goal
    • Save  effort  through  smart  Android  coding  
    prac:ces
    • Decrease  work  by  avoiding  duplica:on
    • Decrease  bugs  through  code  consistency

    View Slide

  5. Resource Qualifiers

    View Slide

  6. Resource Qualifiers
    • All  files  under  /res/  
    are  resources
    • Format:  /res/-­‐
    /
    • Qualifiers  can  be  
    applied  to  any  
    resource

    View Slide

  7. Useful Qualifiers
    • Orienta:on
    • Screen  Size
    • PlaNorm  Version
    • Density
    • Language  and  Region

    View Slide

  8. android:configChanges
    • ORen  abused  to  avoid  Ac:vity  restarts
    • Sidesteps  the  resource  qualifier  system
    • Alterna:ves:
    –onSaveInstanceState()
    –Fragment.setRetainInstance()
    –onRetainNonConfigura:onInstance()  (deprecated  
    solu:on)

    View Slide

  9. onSaveInstanceState()
    protected void onSaveInstanceState(Bundle outState)
    {
    super.onSaveInstanceState(outState);
    outState.putBoolean(KEY, mVar);
    }
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (savedInstanceState != null) {
    mVar = savedInstanceState.getBoolean(KEY);
    }
    }

    View Slide

  10. Retained Fragments
    protected void onCreate(Bundle savedInstanceState)
    {
    ! FragmentManager fm = getFragmentManager();
    ! mInstance = (State)
    fm.findFragmentByTag(State.TAG);
    ! if (mInstance == null) {
    ! ! mInstance = new State();
    ! ! mInstance.setRetainInstance(true);
    ! ! fm.beginTransaction().add(mInstance,
    State.TAG).commit();
    ! }
    }

    View Slide

  11. Leveraging Resource
    Qualifiers

    View Slide


  12. • Most  obvious  usage  award
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:gravity="center"
    android:text="@string/hello" />

    View Slide


  13. • Stores  a  scaling  value
    • Good  for  layout_width,  layout_height,  textSize,  
    etc.
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:gravity="center"
    android:text="@string/hello"
    android:textSize="@dimen/my_text_size" />

    View Slide


  14. • Useful  for  weights
    • Change  a]ributes  with  constants  (e.g.,  
    visibility)
    • Good  for  anima:on  :ming

    View Slide

  15. for weights
    android:orientation="horizontal" >
    android:layout_width="0dp"
    android:layout_height="fill_parent"
    android:layout_weight="@integer/weight1" />
    android:layout_width="0dp"
    android:layout_height="fill_parent"
    android:layout_weight="@integer/weight2" />

    View Slide

  16. for attributes
    • In  /res/values/,  0  ==  "visible"

    0

    • In  /res/values-­‐/,  2  ==  "gone"

    2

    View Slide

  17. for attributes
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:text="@string/visibility_test"
    android:visibility="@integer/
    is_visible" />

    View Slide


  18. • Can  be  used  for  true/false  a]ributes
    • Useful  for  determining  configura:on
    if (res.getBoolean(R.bool.is_landscape)) {
    tv.setText(R.string.landscape_detected);
    }

    View Slide

  19. Drawables
    • Densi:es  -­‐  most  common  use  case
    • Asset  changes  between  layouts

    View Slide

  20. Layouts
    • Simple:  layouts  for  different  orienta:ons
    • Layouts  for  different  screen  sizes

    View Slide


  21. • Inflate  one  layout  file  in  another
    • Allows  one  to  modify  part  of  a  layout  based  
    on  resource  qualifiers
    • Can  be  nested

    View Slide

  22. demo

    View Slide


  23. • Operates  like  ,  but  can  prevent  
    View  duplica:on  in  hierarchy



    View Slide


  24. • Reduces  duplica:on  like  
    • Inflate  Views  on  demand  (lazy  infla:on)
    • Speeds  up  ini:al  infla:on
    android:id="@+id/stub_header"
    android:inflatedId="@+id/header"
    android:layout="@layout/include_header"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content" />

    View Slide

  25. Repeated Inflation
    • Use    for  mul:ple  versions  of  the  same  
    set  of  Views

    layout="@layout/include_repeat" />
    layout="@layout/include_repeat" />
    layout="@layout/include_repeat" />

    View Slide

  26. Repeated Inflation
    • LayoutInflater  to  inflate  a  repeated  set  of  
    Views
    private void addRow(ViewGroup container) {
    View view = getLayoutInflater()
    .inflate(R.layout.include_repeat,
    container, false);
    container.addView(view);
    }

    View Slide

  27. Strings

    View Slide

  28. Plurals
    •  lets  you  define  a  numerical  string  
    forma]er.
    • Logic  sits  in  resources,  not  in  code

    %d item
    %d items

    View Slide

  29. Plurals
    • Get  quan:ty  in  code:
    getResources().getQuantityString(
    R.plurals.items, num, num);

    View Slide

  30. Plurals Localization
    Lets  translators  handle  complex  corner  cases.

    %d items
    %d item
    %d items
    %d items
    %d items
    %d items

    View Slide

  31. String Templating
    • String  forma>ng  built  into  Resources
    • Create  templates  for  easy  modifica:on
    • Don't  be  too  clever  -­‐  localiza:on  can  be  
    tricky.

    View Slide

  32. String Templating
    • Resources:
    %1$s -
    %2$s
    • Code:
    textView.setText(getString(R.string.templ
    ate, "First point", "Second point"));

    View Slide

  33. Strings as Keys
    • Tie  string  iden:fiers  to  keys
    • Works  for  preferences,  fragments,  more
    • Handy  conven:on:  do_not_translate.xml

    View Slide

  34. do_not_translate.xml

    tag_fragment

    View Slide

  35. Fragment Tag
    android:name="com.app.MyPrefFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:tag="@string/tag" />

    View Slide

  36. Identifier in Activity
    protected void onCreate(Bundle state) {
    ! super.onCreate(state);
    ! setContentView(R.layout.my_layout);
    ! FragmentManager fm = getFragmentManager();
    ! mFragment = fm.findFragmentByTag(
    ! ! getString(R.string.tag_fragment));
    }

    View Slide


  37. • Use  referenced  @strings,  not  inline  strings!

    @string/item_1
    @string/item_2
    @string/item_3

    View Slide

  38. Styles

    View Slide

  39. When to Use Styles On Views
    • When  repea:ng  the  same  View  a]ributes  
    along  similar  Views  (e.g.,  label  TextViews)
    • NOT  for  every  single  View
    • NOT  when  two  Views  happen  to  have  some  
    a]ributes  in  common

    View Slide

  40. Themes
    • Styles  applied  to  the  Applica:on  or  Ac:vity
    • Change  a]ribute  for  en:re  app/ac:vity
    • Some  styleables  only  applicable  to  themes

    View Slide

  41. Style Parenting
    • Styles  can  be  parented
    • Implicit  vs.  explicit  parents



    View Slide

  42. Styles and Resource Qualifiers
    • A]ributes  inside  of  styles  fall  under  the  
    resource  qualifier  system
    • Styles  themselves  fall  under  the  resource  
    qualifier  system
    • Use  resource  qualifiers  to  swap  out  the  
    parent  style!

    View Slide

  43. Example: Holo Theme
    • Problem:  Holo  only  introduced  in  v11
    • Solu*on:  Use  resource  qualifiers  +  style  
    paren:ng  to  switch  which  parent  Theme  to  
    use  in  different  contexts

    View Slide

  44. • In  /res/values/themes.xml:
    parent="@android:style/Theme" />
    • In  /res/values-­‐v11/themes.xml:
    parent="@android:style/Theme.Holo" />
    • Bonus  (also  in  /res/values/themes.xml):
    parent="@style/MyTheme" />

    View Slide

  45. Drawables

    View Slide

  46. Nine-patches
    • Allows  intelligent  stretching  of  an  image
    • S:ll  need  one  for  each  density,  as  the  non-­‐
    stretchable  por:ons  are  scaled  otherwise.

    View Slide

  47. SVGs
    • Great  for  cropping  an  image  in  many  ways
    • Bonus:  reduces  APK  size

    View Slide

  48. Shape Drawables
    •  lets  you  draw  simple  objects
    android:shape="rectangle">
    android:startColor="#F00"
    android:endColor="#00F" />

    View Slide

  49. LayerDrawable
    • Layer  drawables  on  
    top  of  each  other
    • Example:  rounded  
    corners
    • Useful  w/  selector

    View Slide

  50. Level-based Drawables
    • All  drawables  have  a  
    "level"  a]ribute
    • ,  ,  

    View Slide

  51. Aliases

    View Slide

  52. Simple Reference
    • You  can  reference  one  value  from  within  
    another:
    @color/
    orig_color
    • Works  for  any  simple  resource  type

    View Slide

  53. resources
    • Reference  any  other  resource  with  :
    type="layout">@layout/orig_resource
    item>
    • Any  resource  type  can  be  aliased

    View Slide

  54. Layout Aliases
    • Problem:  handling  both  old  and  new  screen  
    size  qualifiers
    • Old  style:
    –/layout-­‐small/,
    –/layout-­‐large/
    • New  style:
    –/layout-­‐sw800dp/
    –/layout-­‐h500dp/

    View Slide

  55. Layout Aliases
    • Solu:on:  alias  layouts
    • Define  two  layouts  in  /layouts/:
    –main.xml
    –large.xml

    View Slide

  56. Layout Aliases
    • Define  two  alias  values  files.
    • /values-­‐large/aliases.xml:
    @layout/
    large
    • /values-­‐sw600dp/aliases.xml:
    @layout/
    large

    View Slide


  57. • Create  two  different  Ac:vity  entries  in  
    AndroidManifest.xml  that  point  to  the  same  
    Ac:vity.
    • Uses:
    –Alterna:ve  entrances  into  one  Ac:vity
    –Backwards  compa:bility

    View Slide

  58. Activities and Fragments

    View Slide

  59. Activity Inheritance
    • Suppose  you  want  to  apply  a  behavior  to  all  
    your  Ac:vi:es
    • Problem:  you  have  a  mixture  of  Ac:vity,  
    ListAc:vity,  MapAc:vity,  etc
    • Solu:on:  Composi:on  over  inheritance

    View Slide

  60. Activity Inheritance
    1.Create  an  Ac:vity  Helper  that  implements  
    func:onality.
    public class TrackingHelper {
    public void onResume() {
    // Tracking code
    }
    }

    View Slide

  61. Activity Inheritance
    2.Add  the  helper  to  your  Ac:vity
    public class MyActivity extends Activity {
    private TrackingHelper mTrackingHelper =
    new TrackingHelper();
    public void onResume() {
    super.onResume();
    mTrackingHelper.onResume();
    }
    }

    View Slide

  62. Widgets
    •Group  sets  of  related  Views  into  one  class
    •Easily  reuse  Views  in  mul:ple  places  with  
    one  simple  interface

    View Slide

  63. View Slide

  64. View Slide

  65. Simple Widget
    • Split  Views  into  its  on  layout  file
    • Use    +  find,  or  inflate  in  the  widget  
    itself

    View Slide

  66. View Slide

  67. View Slide

  68. Complex Widget
    • Views  may/may  not  exist,  widget  handles  
    automa:cally.
    • Use    to  create  ids  ahead  of  :me  (no  
    conflicts)

    View Slide

  69. Libraries
    • Leverage  these  to  reuse  common  
    components  between  your  applica:ons
    • Problem:  what  if  you  want  to  reskin  a  
    component  from  a  library?
    • Answer:  A]ributes  and  themes

    View Slide

  70. Styling Library Activities
    1.Define  a]ributes  for  what  XML  values  
    you  want  to  customize  (usually  in  
    a]rs.xml).

    format="reference|color" />

    View Slide

  71. Styling Library Activities
    2.Link  to  those  a]ributes  in  the  library's  XML.
    android:layout_height="fill_parent"
    android:layout_width="fill_parent"
    android:background="?attr/
    myBackground" />

    View Slide

  72. Styling Library Activities
    3.In  the  applica:on  using  the  library,  create  a  
    theme  which  defines  values  for  those  
    a]ributes.
    <br/><item name="myBackground">#000</item><br/>

    View Slide

  73. Styling Library Activities
    4.Apply  that  theme  to  the  Applica:on  (or  
    Ac:vity)
    android:icon="@drawable/icon"
    android:label="My App"
    android:theme="@style/LibraryTheme">

    View Slide

  74. Warning
    • If  you  try  to  access  an  a]ribute  that  is  not  
    defined,  you  get  a  very  ugly  (and  difficult  to  
    debug)  explosion
    • Create  a  default  theme  that  applica:ons  
    using  your  library  can  extend  (to  avoid  
    explosions)

    View Slide

  75. Dynamic Resources
    • Problem:  data-­‐driven  apps  referencing  
    resources  leads  to  duplicate  data  structures
    • Solu:on  #1:  getIden:fier()
    getResources().getIdentifier("drawableName"
    , "drawable", "com.mypackage.myapp");
    • Solu:on  #2:  reflec:on
    Class res = R.drawable.class;
    Field field = res.getField("drawableName");
    int drawableId = field.getInt(null);

    View Slide

  76. Logging
    • Problem:  repeatedly  disabling/tagging  logs
    • Log  wrapper  allows  auto-­‐tagging  and  app-­‐
    wide  enabling/disabling
    • My  open  source  version:  h]p://bit.ly/H7NpRi

    View Slide

  77. Links
    • Samples:  h]p://bit.ly/H9eh5e
    • Log  Wrapper:  h]p://bit.ly/H7NpRi
    • Blog:  h]p://daniel-­‐codes.blogspot.com/
    • Email:  [email protected]

    View Slide