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

Nested scrolling: living life on the edge (DevFest Tokyo 2016)

Chris Banes
October 09, 2016

Nested scrolling: living life on the edge (DevFest Tokyo 2016)

Chris Banes

October 09, 2016
Tweet

More Decks by Chris Banes

Other Decks in Technology

Transcript

  1. NESTED SCROLLING
    LIVING LIFE ON THE EDGE
    +ChrisBanes

    @chrisbanes

    View Slide

  2. User swipes up, collapsing the
    AppBarLayout and scrolling the list

    View Slide

  3. User swipes down, scrolling the list

    View Slide

  4. User needs to swipe down again,
    to expand the AppBarLayout

    View Slide

  5. The problem:
    Flinging does not transfer
    left over velocity

    View Slide

  6. The goal:
    Flinging transfers

    velocity when it reaches

    the end

    View Slide

  7. >
    NESTED SCROLLING: LIVING LIFE ON THE EDGE
    Nested Scrolling
    NESTED SCROLLING

    View Slide

  8. Nested scrolling views was difficult

    View Slide

  9. Nested scrolling views was difficult








    View Slide

  10. View Slide






  11. View Slide






  12. View Slide







  13. View Slide

  14. Nested scrolling was created

    to solve this

    View Slide

  15. An event system where scroll events are dispatched

    up the view hierarchy

    View Slide

  16. ScrollView
    LinearLayout
    ImageView ListView

    View Slide

  17. 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

    View Slide

  18. 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

    View Slide

  19. 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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  23. Pre vs Non-Pre

    View Slide

  24. Pre vs Non-Pre
    Pre-scroll events allow a parent to intercept and consume
    the event

    View Slide

  25. Pre vs Non-Pre
    The view then scrolls the remainder
    Pre-scroll events allow a parent to intercept and consume
    the event

    View Slide

  26. 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

    View Slide

  27. Let’s see how it happens…

    View Slide

  28. ACTION_DOWN

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  33. ACTION_MOVE

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  37. 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, ...)
    // -3

    View Slide

  38. 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

    View Slide

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

    View Slide

  40. 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
    }

    View Slide

  41. ACTION_UP

    View Slide

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

    View Slide

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

    View Slide

  44. 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
    }

    View Slide

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

    View Slide

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

    View Slide

  47. The problem:
    Flinging does not transfer
    left over velocity

    View Slide

  48. The problem:
    Nested flinging does not allow
    interception like scrolling

    View Slide

  49. >
    NESTED SCROLLING: LIVING LIFE ON THE EDGE
    Indirect Nested Scrolling
    INDIRECT NESTED SCROLLING
    Work in progress

    View Slide

  50. Nested scrolling

    View Slide

  51. Direct nested scrolling

    View Slide

  52. Direct nested scrolling
    User directly touching screen

    View Slide

  53. User not directly touching screen
    Indirect nested scrolling

    View Slide

  54. Settling from fling
    Setting scroll position programmatically
    Scrolling from KeyEvents
    ?

    View Slide

  55. API is mostly the same as nested scrolling

    View Slide

  56. 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

    View Slide

  57. 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

    View Slide

  58. Fling
    API is mostly the same as nested scrolling

    View Slide

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

    nested scrolling

    View Slide

  60. View Slide

  61. startNestedScroll(...)
    ACTION_DOWN

    View Slide

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

    View Slide

  63. ACTION_MOVE

    View Slide

  64. stopNestedScroll()
    ACTION_UP
    fling()
    startNestedIndirectScroll(...)

    View Slide

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

    View Slide

  66. stopNestedIndirectScroll()

    View Slide

  67. Full demo

    View Slide

  68. Over and out…
    +ChrisBanes

    @chrisbanes

    View Slide