NestedScrollView.java
1.触摸事件
1 @Override 2 public boolean onTouchEvent(MotionEvent ev) { 3 initVelocityTrackerIfNotExists(); 4 5 MotionEvent vtev = MotionEvent.obtain(ev); 6 7 final int actionMasked = MotionEventCompat.getActionMasked(ev); 8 9 if (actionMasked == MotionEvent.ACTION_DOWN) { 10 mNestedYOffset = 0; 11 } 12 vtev.offsetLocation(0, mNestedYOffset); 13 14 switch (actionMasked) { 15 case MotionEvent.ACTION_DOWN: { 16 //通知nested开始滚动 17 startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL); 18 break; 19 } 20 case MotionEvent.ACTION_MOVE: 21 //通知nested滚动 22 if (dispatchNestedPreScroll(0, deltaY, mScrollConsumed, mScrollOffset)) { 23 deltaY -= mScrollConsumed[1]; 24 vtev.offsetLocation(0, mScrollOffset[1]); 25 mNestedYOffset += mScrollOffset[1]; 26 } 27 ... 28 if (mIsBeingDragged) { 29 // Scroll to follow the motion event 30 ... 31 if (dispatchNestedScroll(0, scrolledDeltaY, 0, unconsumedY, mScrollOffset)) { 32 mLastMotionY -= mScrollOffset[1]; 33 vtev.offsetLocation(0, mScrollOffset[1]); 34 mNestedYOffset += mScrollOffset[1]; 35 } else if (canOverscroll) { 36 ... 37 } 38 } 39 break; 40 case MotionEvent.ACTION_CANCEL: 41 case MotionEvent.ACTION_UP: 42 /* Release the drag */ 43 mIsBeingDragged = false; 44 mActivePointerId = INVALID_POINTER; 45 recycleVelocityTracker(); 46 if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0, getScrollRange())) { 47 ViewCompat.postInvalidateOnAnimation(this); 48 } 49 //通知nested滚动 50 stopNestedScroll(); 51 break; 52 53 } 54 55 if (mVelocityTracker != null) { 56 mVelocityTracker.addMovement(vtev); 57 } 58 vtev.recycle(); 59 return true; 60 }
分析dispatchNestedScroll方法
1@Override 2 public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, 3 int dyUnconsumed, int[] offsetInWindow) { 4 return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, 5 offsetInWindow); 6 } 7 8 9NestedScrollViewHelper 10public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, 11 int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) { 12 if (isNestedScrollingEnabled() && mNestedScrollingParent != null) { 13 if (dxConsumed != 0 || dyConsumed != 0 || dxUnconsumed != 0 || dyUnconsumed != 0) { 14 int startX = 0; 15 int startY = 0; 16 if (offsetInWindow != null) { 17 mView.getLocationInWindow(offsetInWindow); 18 startX = offsetInWindow[0]; 19 startY = offsetInWindow[1]; 20 } 21//调用控制器的onNestedScroll 22 ViewParentCompat.onNestedScroll(mNestedScrollingParent, mView, dxConsumed, 23 dyConsumed, dxUnconsumed, dyUnconsumed); 24 25 if (offsetInWindow != null) { 26 mView.getLocationInWindow(offsetInWindow); 27 offsetInWindow[0] -= startX; 28 offsetInWindow[1] -= startY; 29 } 30 return true; 31 } else if (offsetInWindow != null) { 32 // No motion, no dispatch. Keep offsetInWindow up to date. 33 offsetInWindow[0] = 0; 34 offsetInWindow[1] = 0; 35 } 36 } 37 return false; 38 } 39 40ViewParentCompatLollipop.java 41public static void onNestedScroll(ViewParent parent, View target, int dxConsumed, 42 int dyConsumed, int dxUnconsumed, int dyUnconsumed) { 43 try { 44 parent.onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed); 45 } catch (AbstractMethodError e) { 46 Log.e(TAG, "ViewParent " + parent + " does not implement interface " + 47 "method onNestedScroll", e); 48 } 49 }
CoordinatorLayout.java
1 public void onNestedScroll(View target, int dxConsumed, int dyConsumed, 2 int dxUnconsumed, int dyUnconsumed) { 3 final int childCount = getChildCount(); 4 boolean accepted = false; 5 6 for (int i = 0; i < childCount; i++) { 7 final View view = getChildAt(i); 8 final LayoutParams lp = (LayoutParams) view.getLayoutParams(); 9//判断子View是否支持NestedScroll 10 if (!lp.isNestedScrollAccepted()) { 11 continue; 12 } 13 14 final Behavior viewBehavior = lp.getBehavior(); 15 if (viewBehavior != null) { 16//调用Behavior.onNestedScroll,设置子View的偏移量 17 viewBehavior.onNestedScroll(this, view, target, dxConsumed, dyConsumed, 18 dxUnconsumed, dyUnconsumed); 19 accepted = true; 20 } 21 } 22 23 if (accepted) { 24//通知布局变话 25 dispatchOnDependentViewChanged(true); 26 } 27 }
AppbarLayout#Behavior
1@Override 2 public void onNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, 3 View target, int dxConsumed, int dyConsumed, 4 int dxUnconsumed, int dyUnconsumed) { 5 if (dyUnconsumed < 0) { 6 // If the scrolling view is scrolling down but not consuming, it's probably be at 7 // the top of it's content 8//向下滚动时,调整View位置 9 scroll(coordinatorLayout, child, dyUnconsumed, 10 -child.getDownNestedScrollRange(), 0); 11 // Set the expanding flag so that onNestedPreScroll doesn't handle any events 12 mSkipNestedPreScroll = true; 13 } else { 14 // As we're no longer handling nested scrolls, reset the skip flag 15 mSkipNestedPreScroll = false; 16 } 17 } 18 19HeaderBehavior.java 20//设置偏移量 21final int scroll(CoordinatorLayout coordinatorLayout, V header, 22 int dy, int minOffset, int maxOffset) { 23 return setHeaderTopBottomOffset(coordinatorLayout, header, 24 getTopBottomOffsetForScrollingSibling() - dy, minOffset, maxOffset); 25 } 26 27 28 int setHeaderTopBottomOffset(CoordinatorLayout parent, V header, int newOffset, 29 int minOffset, int maxOffset) { 30 final int curOffset = getTopAndBottomOffset(); 31 int consumed = 0; 32 33 if (minOffset != 0 && curOffset >= minOffset && curOffset <= maxOffset) { 34 // If we have some scrolling range, and we're currently within the min and max 35 // offsets, calculate a new offset 36 newOffset = MathUtils.constrain(newOffset, minOffset, maxOffset); 37//计算有效偏移量,如果偏移量有变化则设置新的偏移量 38 if (curOffset != newOffset) { 39 setTopAndBottomOffset(newOffset); 40 // Update how much dy we have consumed 41 consumed = curOffset - newOffset; 42 } 43 } 44 45 return consumed; 46 } 47 48public boolean setTopAndBottomOffset(int offset) { 49 if (mViewOffsetHelper != null) { 50 return mViewOffsetHelper.setTopAndBottomOffset(offset); 51 } else { 52 mTempTopBottomOffset = offset; 53 } 54 return false; 55 } 56//子View在布局时创建ViewOffsetHelper 57@Override 58 public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection) { 59 // First let lay the child out 60 layoutChild(parent, child, layoutDirection); 61 62 if (mViewOffsetHelper == null) { 63 mViewOffsetHelper = new ViewOffsetHelper(child); 64 } 65 mViewOffsetHelper.onViewLayout(); 66 67 if (mTempTopBottomOffset != 0) { 68 mViewOffsetHelper.setTopAndBottomOffset(mTempTopBottomOffset); 69 mTempTopBottomOffset = 0; 70 } 71 if (mTempLeftRightOffset != 0) { 72 mViewOffsetHelper.setLeftAndRightOffset(mTempLeftRightOffset); 73 mTempLeftRightOffset = 0; 74 } 75 76 return true; 77 }