Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

Secrets of the Support Library (Droidcon NYC 2016)

Chris Banes
September 30, 2016

Secrets of the Support Library (Droidcon NYC 2016)

The support library has become a necessity for the majority of Android developers. This talk will delve into some of its secrets, from bugs we’ve fixed, things we haven’t (yet), and how some of the features you’re using actually work back to API 9.

Chris Banes

September 30, 2016
Tweet

More Decks by Chris Banes

Other Decks in Technology

Transcript

  1. MODE_NIGHT_FOLLOW_SYSTEM Follows the system night mode DEFAULT MODE_NIGHT_YES Always night

    mode MODE_NIGHT_NO Never night mode (aka day mode) MODE_NIGHT_AUTO Switches based on time of day
  2. MODE_NIGHT_FOLLOW_SYSTEM Follows the system night mode DEFAULT MODE_NIGHT_YES Always night

    mode MODE_NIGHT_NO Never night mode (aka day mode) MODE_NIGHT_AUTO Switches based on time of day
  3. MODE_NIGHT_FOLLOW_SYSTEM Follows the system night mode DEFAULT MODE_NIGHT_YES Always night

    mode MODE_NIGHT_NO Never night mode (aka day mode) MODE_NIGHT_AUTO Switches based on time of day
  4. public static final int UI_MODE_NIGHT_MASK;
 public static final int UI_MODE_NIGHT_UNDEFINED;

    public static final int UI_MODE_NIGHT_NO; public static final int UI_MODE_NIGHT_YES;
  5. public static final int UI_MODE_NIGHT_MASK;
 public static final int UI_MODE_NIGHT_UNDEFINED;

    public static final int UI_MODE_NIGHT_NO; public static final int UI_MODE_NIGHT_YES;
  6. public static final int UI_MODE_NIGHT_MASK;
 public static final int UI_MODE_NIGHT_UNDEFINED;

    public static final int UI_MODE_NIGHT_NO; public static final int UI_MODE_NIGHT_YES;
  7. public static final int UI_MODE_NIGHT_NO; public static final int UI_MODE_NIGHT_YES;

    AppCompatDelegate.MODE_NIGHT_NO AppCompatDelegate.MODE_NIGHT_YES
  8. Resources res = getResources(); Configuration config = res.getConfiguration(); // Update

    the UI Mode to reflect the new night mode config.uiMode = newNightMode | (config.uiMode & ~Configuration.UI_MODE_NIGHT_MASK); // Now update the configuration res.updateConfiguration(config, ...);
  9. Resources res = getResources(); Configuration config = res.getConfiguration(); // Update

    the UI Mode to reflect the new night mode config.uiMode = newNightMode | (config.uiMode & ~Configuration.UI_MODE_NIGHT_MASK); // Now update the configuration res.updateConfiguration(config, ...); Configuration.UI_MODE_NIGHT_YES
  10. Resources res = getResources(); Configuration config = res.getConfiguration(); // Update

    the UI Mode to reflect the new night mode config.uiMode = newNightMode | (config.uiMode & ~Configuration.UI_MODE_NIGHT_MASK); // Now update the configuration res.updateConfiguration(config, ...);
  11. ACTION_DOWN // ev = MotionEvent parameter // Initialise VelocityTracker and

    add event mVelocityTracker = VelocityTracker.obtain(); mVelocityTracker.addMovement(ev); int y = ev.getY(); // 1020 mLastMotionY = y;
  12. ACTION_MOVE // Add event to velocity tracker mVelocityTracker.addMovement(ev); int y

    = ev.getY(); // 1010 // Calculate difference from last event int dy = mLastMotionY - y; // -10 for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); child.offsetTopAndBottom(dy); }A

  13. ACTION_MOVE // Add event to velocity tracker mVelocityTracker.addMovement(ev); int y

    = ev.getY(); // 1010 // Calculate difference from last event int dy = mLastMotionY - y; // -10 for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); child.offsetTopAndBottom(dy); }A
 // Finally update our recorded Y mLastMotionY = y;
  14. ACTION_UP // Calculate velocity over 1000ms mVelocityTracker.computeCurrentVelocity(1000); float velY =

    mVelocityTracker.getYVelocity(); // TODO Implement some flinging fling(velY);
  15. OverScroller mScroller = new OverScroller(); void fling(int velocityY) { //

    How much we can scroll int scrollRange = ...; mScroller.fling(mScrollX, mScrollY, 0, velocityY, 0, 0, 0, scrollRange, 0, height / 2); ViewCompat.postInvalidateOnAnimation(this); }
  16. OverScroller mScroller = new OverScroller(); void fling(int velocityY) { //

    How much we can scroll int scrollRange = ...; mScroller.fling(mScrollX, mScrollY, 0, velocityY, 0, 0, 0, scrollRange, 0, height / 2); ViewCompat.postInvalidateOnAnimation(this); }
  17. OverScroller mScroller = new OverScroller(); void fling(int velocityY) { //

    How much we can scroll int scrollRange = ...; mScroller.fling(mScrollX, mScrollY, 0, velocityY, 0, 0, 0, scrollRange, 0, height / 2); ViewCompat.postInvalidateOnAnimation(this); } // Passed in from touch handling
  18. OverScroller mScroller = new OverScroller(); void fling(int velocityY) { //

    How much we can scroll int scrollRange = ...; mScroller.fling(mScrollX, mScrollY, 0, velocityY, 0, 0, 0, scrollRange, 0, height / 2); ViewCompat.postInvalidateOnAnimation(this); }
  19. OverScroller mScroller = new OverScroller(); void fling(int velocityY) { //

    How much we can scroll int scrollRange = ...; mScroller.fling(mScrollX, mScrollY, 0, velocityY, 0, 0, 0, scrollRange, 0, height / 2); ViewCompat.postInvalidateOnAnimation(this); }
  20. OverScroller mScroller = new OverScroller(); void fling(int velocityY) { //

    How much we can scroll int scrollRange = ...; mScroller.fling(mScrollX, mScrollY, 0, velocityY, 0, 0, 0, scrollRange, 0, height / 2); ViewCompat.postInvalidateOnAnimation(this); } // Current scroll position
  21. OverScroller mScroller = new OverScroller(); void fling(int velocityY) { //

    How much we can scroll int scrollRange = ...; mScroller.fling(mScrollX, mScrollY, 0, velocityY, 0, 0, 0, scrollRange, 0, height / 2); ViewCompat.postInvalidateOnAnimation(this); } // Fling velocity
  22. OverScroller mScroller = new OverScroller(); void fling(int velocityY) { //

    How much we can scroll int scrollRange = ...; mScroller.fling(mScrollX, mScrollY, 0, velocityY, 0, 0, 0, scrollRange, 0, height / 2); ViewCompat.postInvalidateOnAnimation(this); } // minX, maxX
  23. OverScroller mScroller = new OverScroller(); void fling(int velocityY) { //

    How much we can scroll int scrollRange = ...; mScroller.fling(mScrollX, mScrollY, 0, velocityY, 0, 0, 0, scrollRange, 0, height / 2); ViewCompat.postInvalidateOnAnimation(this); } // minY, maxY
  24. OverScroller mScroller = new OverScroller(); void fling(int velocityY) { //

    How much we can scroll int scrollRange = ...; mScroller.fling(mScrollX, mScrollY, 0, velocityY, 0, 0, 0, scrollRange, 0, height / 2); ViewCompat.postInvalidateOnAnimation(this); } // overscroll X, Y
  25. OverScroller mScroller = new OverScroller(); void fling(int velocityY) { //

    How much we can scroll int scrollRange = ...; mScroller.fling(mScrollX, mScrollY, 0, velocityY, 0, 0, 0, scrollRange, 0, height / 2); ViewCompat.postInvalidateOnAnimation(this); }
  26. @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { int x

    = mScroller.getCurrX(); int y = mScroller.getCurrY(); // Update the scroll position scrollTo(x, y); } }
  27. Nested scrolling views was difficult <ScrollView> <LinearLayout> <!-- Some content

    --> <ListView> <!-- More content which scrolls --> </ListView> </LinearLayout> </ScrollView>
  28. void setNestedScrollingEnabled(boolean enabled); boolean isNestedScrollingEnabled(); boolean startNestedScroll(int axes); boolean hasNestedScrollingParent();

    boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed,
 int[] offsetInWindow); boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,
 int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow); boolean dispatchNestedPreFling(float velocityX, float velocityY); boolean dispatchNestedFling(float velocityX, float velocityY,
 boolean consumed); void stopNestedScroll(); Sending
  29. void setNestedScrollingEnabled(boolean enabled); boolean isNestedScrollingEnabled(); boolean startNestedScroll(int axes); boolean hasNestedScrollingParent();

    boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed,
 int[] offsetInWindow); boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,
 int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow); boolean dispatchNestedPreFling(float velocityX, float velocityY); boolean dispatchNestedFling(float velocityX, float velocityY,
 boolean consumed); void stopNestedScroll(); Sending
  30. boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed,
 int[] offsetInWindow); boolean

    dispatchNestedScroll(int dxConsumed, int dyConsumed,
 int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow); boolean dispatchNestedPreFling(float velocityX, float velocityY); boolean dispatchNestedFling(float velocityX, float velocityY,
 boolean consumed); Sending
  31. boolean onStartNestedScroll(View child, View target,
 int nestedScrollAxes); void onNestedScrollAccepted(View child,

    View target,
 int nestedScrollAxes); void onStopNestedScroll(View target); int getNestedScrollAxes(); Receiving void onNestedPreScroll(View target, int dx, int dy, int[] consumed); void onNestedScroll(View target, int dxConsumed, int dyConsumed,
 int dxUnconsumed, int dyUnconsumed); boolean onNestedPreFling(View target, float velocityX,
 float velocityY); boolean onNestedFling(View target, float velocityX,
 float velocityY, boolean consumed);
  32. boolean onStartNestedScroll(View child, View target,
 int nestedScrollAxes); void onNestedScrollAccepted(View child,

    View target,
 int nestedScrollAxes); void onStopNestedScroll(View target); int getNestedScrollAxes(); Receiving void onNestedPreScroll(View target, int dx, int dy, int[] consumed); void onNestedScroll(View target, int dxConsumed, int dyConsumed,
 int dxUnconsumed, int dyUnconsumed); boolean onNestedPreFling(View target, float velocityX,
 float velocityY); boolean onNestedFling(View target, float velocityX,
 float velocityY, boolean consumed);
  33. Receiving void onNestedPreScroll(View target, int dx, int dy, int[] consumed);

    void onNestedScroll(View target, int dxConsumed, int dyConsumed,
 int dxUnconsumed, int dyUnconsumed); boolean onNestedPreFling(View target, float velocityX,
 float velocityY); boolean onNestedFling(View target, float velocityX,
 float velocityY, boolean consumed);
  34. Sending (scrolling view) boolean dispatchNestedPreScroll(int dx, int dy,
 int[] consumed,

    int[] offsetInWindow); Receiving (parent) void onNestedPreScroll(View target, int dx, int dy,
 int[] consumed);
  35. Pre vs Non-Pre Scroll events allow the parent to observe

    what the view scrolled The view then scrolls the remainder Pre-scroll events allow a parent to intercept and consume the event
  36. ACTION_DOWN // Initialise VelocityTracker and add event mVelocityTracker.addMovement(ev); int y

    = ev.getY(); // 1020 mInitialMotionY = y; mLastMotionY = y; startNestedScroll(SCROLL_AXIS_VERTICAL);
  37. ACTION_DOWN startNestedScroll(SCROLL_AXIS_VERTICAL); @Override boolean onStartNestedScroll(View child, View target, int axes)

    { // child == LinearLayout // target == ListView // axes == SCROLL_AXIS_VERTICAL return true; }
  38. ACTION_DOWN startNestedScroll(SCROLL_AXIS_VERTICAL); @Override void onNestedScrollAccepted(View child, View target, int axes)

    { // child == LinearLayout // target == ListView // axes == SCROLL_AXIS_VERTICAL }
  39. ACTION_MOVE int[] mScrollConsumed = new int[2]; int y = ev.getY();

    // 1010 int dy = mLastMotionY - y; // -10 if (dispatchNestedPreScroll(0, dy, mScrollConsumed, ...)) { dy -= mScrollConsumed[1]; } // INSERT Move children by remaining dy // TODO call dispatchNestedScroll()
  40. ACTION_MOVE @Override void onNestedPreScroll(View target, int dx, int dy, int[]

    consumed) { // dx = 0, dy = -10, consumed = int[2] int amountWeConsume = doSomethingWithPreScroll(dy); // consumed[0] = y, consumed[1] = x consumed[1] = amountWeConsume; } dispatchNestedPreScroll(0, dy, mScrollConsumed, ...)
  41. ACTION_MOVE int[] mScrollConsumed = new int[2]; int y = ev.getY();

    // 1010 int dy = mLastMotionY - y; // -10 if (dispatchNestedPreScroll(0, dy, mScrollConsumed, ...)) { dy -= mScrollConsumed[1]; } // INSERT Move children by remaining dy // TODO call dispatchNestedScroll() // -3 remember // dy = -7
  42. ACTION_MOVE int[] mScrollConsumed = new int[2]; int y = ev.getY();

    // 1010 int dy = mLastMotionY - y; // -10 if (dispatchNestedPreScroll(0, dy, mScrollConsumed, ...)) { dy -= mScrollConsumed[1]; } // INSERT Move children by remaining dy // TODO call dispatchNestedScroll()
  43. ACTION_MOVE // INSERT Move children by remaining dy dispatchNestedScroll(0, dy,

    0, unconsumedY, ...) @Override void onNestedScroll(View target,
 int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) { // Do something if you wish }
  44. ACTION_UP float velY = ...; if (!dispatchNestedPreFling(0, velY)) { //

    Parent hasn't consumed fling, lets fling // and let the parent observe dispatchNestedFling(0, velY, ...); fling(velY); } stopNestedScroll();
  45. ACTION_UP float velY = ...; if (!dispatchNestedPreFling(0, velY)) { //

    Parent hasn't consumed fling, lets fling // and let the parent observe dispatchNestedFling(0, velY, ...); fling(velY); } stopNestedScroll();
  46. ACTION_UP dispatchNestedPreFling(0, velY); @Override boolean onNestedPreFling(View target, float velX, float

    velY) { // return true to consume the whole fling, // false to let the view handle it }
  47. ACTION_UP float velY = ...; if (!dispatchNestedPreFling(0, velY)) { //

    Parent hasn't consumed fling, lets fling // and let the parent observe dispatchNestedFling(0, velY, ...); fling(velY); } stopNestedScroll();
  48. ACTION_UP float velY = ...; if (!dispatchNestedPreFling(0, velY)) { //

    Parent hasn't consumed fling, lets fling // and let the parent observe dispatchNestedFling(0, velY, ...); fling(velY); } stopNestedScroll();
  49. void setNestedIndirectScrollingEnabled(boolean enabled); boolean isNestedIndirectScrollingEnabled(); boolean startNestedIndirectScroll(int axes); boolean dispatchNestedIndirectPreScroll(int

    dx, int dy,
 int[] consumed); boolean dispatchNestedIndirectScroll(int dxConsumed,
 int dyConsumed, int dxUnconsumed, int dyUnconsumed); boolean hasNestedIndirectScrollingParent(); void stopNestedIndirectScroll(); Sending
  50. boolean onStartNestedIndirectScroll(View child, View target,
 int axes); void onNestedIndirectScrollAccepted(View child,

    View target,
 int axes); void onNestedIndirectPreScroll(View target, int dx, int dy,
 int[] consumed); void onNestedIndirectScroll(View target, int dxConsumed,
 int dyConsumed, int dxUnconsumed, int dyUnconsumed); void onStopNestedIndirectScroll(View child); Receiving