Slide 1

Slide 1 text

SECRETS OF THE SUPPORT LIBRARY +ChrisBanes @chrisbanes

Slide 2

Slide 2 text

SECRETS OF THE SUPPORT LIBRARY banes.me/dcnyc16 http://

Slide 3

Slide 3 text

SECRETS OF THE SUPPORT LIBRARY DayNight

Slide 4

Slide 4 text

Allows your app to be automatically themed based on time of day

Slide 5

Slide 5 text

Allows your app to be automatically themed based on time of day

Slide 6

Slide 6 text

AppCompatDelegate.setDefaultNightMode(...) Change night mode Change theme Theme.AppCompat.DayNight

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

Night mode has existed since API 8 -night -notnight

Slide 11

Slide 11 text

Car mode Docked ⚓ © Philip Greenspun

Slide 12

Slide 12 text

public final class Configuration { // Lots of other configuration fields public int uiMode; }

Slide 13

Slide 13 text

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;

Slide 14

Slide 14 text

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;

Slide 15

Slide 15 text

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;

Slide 16

Slide 16 text

public static final int UI_MODE_NIGHT_NO; public static final int UI_MODE_NIGHT_YES; AppCompatDelegate.MODE_NIGHT_NO AppCompatDelegate.MODE_NIGHT_YES

Slide 17

Slide 17 text

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, ...);

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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, ...);

Slide 20

Slide 20 text

values-night drawable-night raw-night

Slide 21

Slide 21 text

<style name="Theme.AppCompat.DayNight" parent="Theme.AppCompat" /> res/values-night res/values

Slide 22

Slide 22 text

SECRETS OF THE SUPPORT LIBRARY AppBarLayout & flinging http://b.android.com/179501

Slide 23

Slide 23 text

User swipes up, collapsing the AppBarLayout and scrolling the list

Slide 24

Slide 24 text

User swipes down, scrolling the list

Slide 25

Slide 25 text

User needs to swipe down again, to expand the AppBarLayout

Slide 26

Slide 26 text

The problem: Flinging does not transfer left over velocity

Slide 27

Slide 27 text

The goal: Flinging transfers velocity when it reaches the end

Slide 28

Slide 28 text

> SECRETS OF THE SUPPORT LIBRARY Basic Scrolling APPBARLAYOUT FLINGING

Slide 29

Slide 29 text

onTouchEvent(MotionEvent) onInterceptTouchEvent(MotionEvent) There’s also but we’ll skip that

Slide 30

Slide 30 text

ACTION_DOWN

Slide 31

Slide 31 text

ACTION_DOWN // ev = MotionEvent parameter // Initialise VelocityTracker and add event mVelocityTracker = VelocityTracker.obtain(); mVelocityTracker.addMovement(ev); int y = ev.getY(); // 1020 mLastMotionY = y;

Slide 32

Slide 32 text

ACTION_MOVE A scroll is usually made up with 100s of events

Slide 33

Slide 33 text

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


Slide 34

Slide 34 text

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;

Slide 35

Slide 35 text

ACTION_UP

Slide 36

Slide 36 text

ACTION_UP // Calculate velocity over 1000ms mVelocityTracker.computeCurrentVelocity(1000); float velY = mVelocityTracker.getYVelocity(); // TODO Implement some flinging fling(velY);

Slide 37

Slide 37 text

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); }

Slide 38

Slide 38 text

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); }

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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); }

Slide 41

Slide 41 text

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); }

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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); }

Slide 48

Slide 48 text

@Override public void computeScroll() { if (mScroller.computeScrollOffset()) { int x = mScroller.getCurrX(); int y = mScroller.getCurrY(); // Update the scroll position scrollTo(x, y); } }

Slide 49

Slide 49 text

> SECRETS OF THE SUPPORT LIBRARY Nested Scrolling APPBARLAYOUT FLINGING

Slide 50

Slide 50 text

Nested scrolling views was difficult

Slide 51

Slide 51 text

Nested scrolling views was difficult

Slide 52

Slide 52 text

No content

Slide 53

Slide 53 text

Slide 54

Slide 54 text

Slide 55

Slide 55 text

Slide 56

Slide 56 text

Nested scrolling was created to solve this

Slide 57

Slide 57 text

An event system where scroll events are dispatched up the view hierarchy

Slide 58

Slide 58 text

ScrollView LinearLayout ImageView ListView

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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);

Slide 63

Slide 63 text

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);

Slide 64

Slide 64 text

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);

Slide 65

Slide 65 text

Two things to know about the API…

Slide 66

Slide 66 text

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);

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

Let’s see how it happens…

Slide 69

Slide 69 text

ACTION_DOWN

Slide 70

Slide 70 text

ACTION_DOWN // Initialise VelocityTracker and add event mVelocityTracker.addMovement(ev); int y = ev.getY(); // 1020 mInitialMotionY = y; mLastMotionY = y; startNestedScroll(SCROLL_AXIS_VERTICAL);

Slide 71

Slide 71 text

ACTION_DOWN startNestedScroll(SCROLL_AXIS_VERTICAL); // Initialise VelocityTracker and add event mVelocityTracker.addMovement(ev); int y = ev.getY(); // 1020 mInitialMotionY = y; mLastMotionY = y;

Slide 72

Slide 72 text

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; }

Slide 73

Slide 73 text

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

Slide 74

Slide 74 text

ACTION_MOVE

Slide 75

Slide 75 text

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()

Slide 76

Slide 76 text

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, ...)

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

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()

Slide 79

Slide 79 text

ACTION_MOVE // INSERT Move children by remaining dy // TODO call dispatchNestedScroll()

Slide 80

Slide 80 text

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 }

Slide 81

Slide 81 text

ACTION_UP // Calculate velocity over 1000ms mVelocityTracker.computeCurrentVelocity(1000); float velY = mVelocityTracker.getYVelocity(); fling(velY);

Slide 82

Slide 82 text

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();

Slide 83

Slide 83 text

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();

Slide 84

Slide 84 text

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 }

Slide 85

Slide 85 text

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();

Slide 86

Slide 86 text

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();

Slide 87

Slide 87 text

The problem: Flinging does not transfer left over velocity

Slide 88

Slide 88 text

The problem: Nested flinging does not allow interception like scrolling

Slide 89

Slide 89 text

> SECRETS OF THE SUPPORT LIBRARY Indirect Nested Scrolling APPBARLAYOUT FLINGING Work in progress

Slide 90

Slide 90 text

Nested scrolling

Slide 91

Slide 91 text

Direct nested scrolling User directly touching screen

Slide 92

Slide 92 text

User not directly touching screen Indirect nested scrolling

Slide 93

Slide 93 text

Settling from fling Setting scroll position programmatically Scrolling from KeyEvents ?

Slide 94

Slide 94 text

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

Slide 95

Slide 95 text

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

Slide 96

Slide 96 text

API is mostly the same as nested scrolling

Slide 97

Slide 97 text

Fling API is mostly the same as nested scrolling Works with nested scrolling

Slide 98

Slide 98 text

startNestedScroll(...) ACTION_DOWN

Slide 99

Slide 99 text

dispatchNestedPreScroll(...) dispatchNestedScroll(...) ACTION_MOVE

Slide 100

Slide 100 text

ACTION_MOVE

Slide 101

Slide 101 text

stopNestedScroll() ACTION_UP fling() startNestedIndirectScroll(...) ↳

Slide 102

Slide 102 text

dispatchNestedIndirectPreScroll(...) dispatchNestedIndirectScroll(...)

Slide 103

Slide 103 text

stopNestedIndirectScroll()

Slide 104

Slide 104 text

Over and out… +ChrisBanes @chrisbanes