Share
Embed
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
Transcript
104 slides
-
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 final class Configuration { // Lots of other configuration
fields public int uiMode; } -
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; -
public static final int UI_MODE_NIGHT_NO; public static final int UI_MODE_NIGHT_YES;
AppCompatDelegate.MODE_NIGHT_NO AppCompatDelegate.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_DOWN // ev = MotionEvent parameter // Initialise VelocityTracker and
add event mVelocityTracker = VelocityTracker.obtain(); mVelocityTracker.addMovement(ev); int y = ev.getY(); // 1020 mLastMotionY = y; -
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; -
ACTION_UP // Calculate velocity over 1000ms mVelocityTracker.computeCurrentVelocity(1000); float velY =
mVelocityTracker.getYVelocity(); // TODO Implement some flinging fling(velY); -
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); } -
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); } -
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); } -
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); } -
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 -
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 -
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 -
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 -
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 -
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); } -
@Override public void computeScroll() { if (mScroller.computeScrollOffset()) { int x
= mScroller.getCurrX(); int y = mScroller.getCurrY(); // Update the scroll position scrollTo(x, y); } } -
Nested scrolling views was difficult <ScrollView> <LinearLayout> <!-- Some content
--> <ListView> <!-- More content which scrolls --> </ListView> </LinearLayout> </ScrollView> -
An event system where scroll events are dispatched up the
view hierarchy -
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 -
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 -
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 -
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); -
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); -
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); -
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); -
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_DOWN // Initialise VelocityTracker and add event mVelocityTracker.addMovement(ev); int y
= ev.getY(); // 1020 mInitialMotionY = y; mLastMotionY = y; startNestedScroll(SCROLL_AXIS_VERTICAL); -
ACTION_DOWN startNestedScroll(SCROLL_AXIS_VERTICAL); // Initialise VelocityTracker and add event mVelocityTracker.addMovement(ev); int
y = ev.getY(); // 1020 mInitialMotionY = y; mLastMotionY = y; -
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; } -
ACTION_DOWN startNestedScroll(SCROLL_AXIS_VERTICAL); @Override void onNestedScrollAccepted(View child, View target, int axes)
{ // child == LinearLayout // target == ListView // axes == SCROLL_AXIS_VERTICAL } -
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 @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, ...) -
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 // 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 // Calculate velocity over 1000ms mVelocityTracker.computeCurrentVelocity(1000); float velY =
mVelocityTracker.getYVelocity(); fling(velY); -
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(); -
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(); -
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 } -
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(); -
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(); -
> SECRETS OF THE SUPPORT LIBRARY Indirect Nested Scrolling APPBARLAYOUT
FLINGING Work in progress -
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 -
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 -
Fling API is mostly the same as nested scrolling Works
with nested scrolling