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

Tools of the Trade (Droidcon NYC 2015)

Tools of the Trade (Droidcon NYC 2015)

This talk will tour one of the most understated resources in the Android developer arsenal: the support tools.

Learn how to enrich your methods and classes using annotations, so that Lint and Android Studio will be able to spot errors for you. Or use IntelliJ annotations to define contracts, and let the IDE warn you of potential issues before you even run your code once. Take advantage of the tools namespace to bend even the most cryptic XML resource to your will.
----
Since this deck is quite heavy on the animations side, I've exported a video version of it as well, that you can find here: http://youtu.be/z9zyQwyO8l0
----
The talk video is now available here: https://www.youtube.com/watch?v=AeoeD7K8vKI

Sebastiano Poggi

August 28, 2015
Tweet

More Decks by Sebastiano Poggi

Other Decks in Programming

Transcript

  1. “How can this be a NPE?” “I wish things were

    strongly typed, all resource IDs look like any other integer!”
  2. “How can this be a NPE?” “I wish things were

    strongly typed, all resource IDs look like any other integer!” “Why isn’t this text R.color.red? It’s showing as black!”
  3. “How can this be a NPE?” “I wish things were

    strongly typed, all resource IDs look like any other integer!” “Why isn’t this text R.color.red? It’s showing as black!” “I love Eclipse!”
  4. “How can this be a NPE?” “I wish things were

    strongly typed, all resource IDs look like any other integer!” “Why isn’t this text R.color.red? It’s showing as black!” “I love Eclipse!” “What? I thought this was on the
  5. “How can this be a NPE?” “I wish things were

    strongly typed, all resource IDs look like any other integer!” “Why isn’t this text R.color.red? It’s showing as black!” “I love Eclipse!” “What? I thought this was on the UI thread, but it’s crashing”
  6. Design-time overrides Override any android attribute /> tools:text="Hello world!" <TextView


    android:layout_width="wrap_content"
 android:layout_height="wrap_content" ...
  7. <style name="Widget.FAB"> ... Lint attribute: targetApi Equivalent to @TargetApi />

    <item android:elevation="8dp" </style> tools:targetApi="HONEYCOMB" android:elevation requires API level 21(current min is 19)
  8. <style name="Widget.FAB"> ... Lint attribute: targetApi Equivalent to @TargetApi />

    <item android:elevation="8dp" </style> tools:targetApi="LOLLIPOP" /> android:elevation requires API level 21(current min is 19)
  9. <style name="Widget.FAB"> ... Lint attribute: targetApi Equivalent to @TargetApi <item

    android:elevation="8dp" </style> tools:targetApi="LOLLIPOP" />
  10. <resources Lint attribute: locale Helps spell checking <string name="typo">Helo, I

    am a typo.</string> </resources> > tools:locale="en">
  11. <merge xmlns:android="..." Design attribute: context Helps AS tying a layout

    to an Activity android:layout_width="match_parent"
 android:layout_height="match_parent" ... </merge> > tools:context=".map.MapActivity"
  12. <merge xmlns:android="..." Design attribute: context Helps AS tying a layout

    to an Activity android:layout_width="match_parent"
 android:layout_height="match_parent" ... </merge> > tools:context=".map.MapActivity"
  13. <merge xmlns:android="..." Design attribute: context Helps AS tying a layout

    to an Activity android:layout_width="match_parent"
 android:layout_height="match_parent" tools:context=".map.MapActivity" ... </merge> >
  14. <merge xmlns:android="..." Design attribute: showIn Shows a merge in its

    parent layout’s include android:layout_width="match_parent"
 android:layout_height="match_parent" ... </merge> > tools:showIn="@layout/activity_main"
  15. <merge xmlns:android="..." Design attribute: menu Shows a menu in the

    layout preview android:layout_width="match_parent"
 android:layout_height="match_parent" ... </merge> > tools:menu="map"
  16. <merge xmlns:android="..." Design attribute: actionBarNavMode Shows navigation in the action

    bar android:layout_width="match_parent"
 android:layout_height="match_parent" ... </merge> > tools:actionBarNavMode="tabs"
  17. Design attributes: list* Previews a ListView’s items, header and footer

    <ListView
 android:id="@+id/list"
 ... tools:listheader="@layout/list_header"
 tools:listitem="@layout/list_item"
 tools:listfooter="@layout/list_footer" />
  18. <ListView
 android:id="@+id/list"
 ... Design attributes: list* Previews a ListView’s items,

    header and footer tools:listheader="@layout/list_header"
 tools:listitem="@layout/list_item"
 tools:listfooter="@layout/list_footer" />
  19. Nullability annotations @Nullable and @NonNull String what) { @Nullable public

    static String trim( return what.trim(); Method invocation may produce NullPointerException }
  20. public static String trim( if (what == null) {
 return

    "";
 
 return what.trim();
 Nullability annotations @Nullable and @NonNull String what) { } } @NonNull
  21. Nullability annotations @Nullable and @NonNull String what) { @NonNull }

    } public static String trim( if (what == null) {
 return "";
 
 return what.trim();
 Condition 'what == null' is always 'false'
  22. setMainTextColor(R.color.text_color);
 
 void setMainTextColor(
 textView.setTextColor(getColor(colorId)); Resource ID annotations A lot

    of @{resType}Res that enforce typing @Nullable Should pass resolved color instead of resource ID } int colorId) {
  23. Resource ID annotations @AnimatorRes @AnimRes @AnyRes @ArrayRes @AttrRes @BoolRes @ColorRes

    @DimenRes @DrawableRes @FractionRes @IdRes @IntegerRes @InterpolatorRes @LayoutRes @MenuRes @PluralsRes @RawRes @StringRes @StyleableRes @StyleRes @XmlRes
  24. setTextColor(R.color.text_color);
 
 private void setTextColor(
 ... RGB Color annotation @ColorInt

    requires color value, not ID } @ColorInt int color) { Should pass resolved color instead of resource id here
  25. setValue(20); 
 void setValue(
 // Value must be in [0,

    10] Range annotations @FloatRange and @IntRange } int value) { @IntRange(from=0, to=10)
  26. setValue(20); 
 void setValue(
 // Value must be in [0,

    10] Range annotations @FloatRange and @IntRange } int value) { @IntRange(from=0, to=10) Value must be ≥ 0 and ≤ 10 (was 20)
  27. Range annotations setCoords(new int[]{42}); 
 void setCoords(
 // Value must

    be int[2] @Size for arrays, collections and strings } int[] value) { @Size(2) Value must be ≥ 0 and ≤ 10 (was 20)
  28. Range annotations setCoords(new int[]{42}); 
 void setCoords(
 // Value must

    be int[2] @Size for arrays, collections and strings } int[] value) { @Size(2) Value must be ≥ 0 and ≤ 10 (was 20) Size must be exactly 2
  29. int style; TypeDef annotations The issue with avoiding enums @IntRange(from=0,

    to=10) static final int STYLE_MATERIAL = 0;
 static final int STYLE_HOLOYOLO = 1; style = 21;
  30. @IntDef(value={STYLE_MATERIAL, STYLE_HOLOYOLO})
 @Retention(RetentionPolicy.SOURCE)
 @interface Style { } TypeDef annotations @interface

    definition: @IntDef and @StringDef @IntRange(from=0, to=10) static final int STYLE_MATERIAL = 0;
 static final int STYLE_HOLOYOLO = 1;
  31. style = 21; TypeDef annotations @interface usage @IntRange(from=0, to=10) Must

    be one of: STYLE_MATERIAL, STYLE_HOLOYOLO static final int STYLE_MATERIAL = 0;
 static final int STYLE_HOLOYOLO = 1; int style; @Style
  32. static final int STYLE_MATERIAL = 0;
 static final int STYLE_HOLOYOLO

    = 1; TypeDef annotations @interface usage @IntRange(from=0, to=10) @Style int style; style = 21; Must be one of: STYLE_MATERIAL, STYLE_HOLOYOLO
  33. Threading annotations Specify thread “affinity” for methods private void test()

    {
 textView.setText("Oops!"); @WorkerThread Method setText must be called from the UI thread, currently inferred thread is worker }
  34. Threading annotations Specify thread “affinity” for methods Method setText must

    be called from the UI thread, currently inferred thread is worker @MainThread @BinderThread @UiThread @WorkerThread
  35. Permissions annotations Specifies calling a method requires a permission startBluetoothScan();

    @RequiresPermission(Manifest.permission.BLUETOOTH) private void startBluetoothScan() { ... }
  36. Permissions annotations Specifies calling a method requires a permission @RequiresPermission(Manifest.permission.BLUETOOTH)

    startBluetoothScan(); private void startBluetoothScan() { ... } Missing permissions required by startBluetoothScan: android.permission.BLUETOOTH
  37. public class MyCustomView extends View {
 private int fancyColor; Exported

    view properties Use @ExportedProperty @Nullable public int getFancyColor() {
 return fancyColor; } @ViewDebug.ExportedProperty
  38. public class MyCustomView extends View {
 private int fancyColor; Exported

    view properties Use @ExportedProperty @Nullable @ViewDebug.ExportedProperty public int getFancyColor() {
 return fancyColor; }
  39. Exported view properties Use @ExportedProperty @Nullable (
 category = "My

    category"
 ) public int getFancyColor() {
 return fancyColor; } @ViewDebug.ExportedProperty