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.
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
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
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
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;
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;
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;
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, ...);
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
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, ...);
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
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;
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
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
@Override public void computeScroll() { if (mScroller.computeScrollOffset()) { int x = mScroller.getCurrX(); int y = mScroller.getCurrY(); // Update the scroll position scrollTo(x, y); } }
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
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()
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
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()
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 }
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 }
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