要研究的几个问题

一、Behavior是什么?为什么要用Behavior?

二、怎么使用Behavior?

三、从源码角度看为什么要这么使用Behavior?

一、Behavior是什么?为什么要用Behavior?

CoordinatorLayout是android support design推出的新布局,主要用于作为视图根布局以及协调子控件的行为,而Behavior就是用于直接子控件来协调自身CoordinatorLayout以及和其他子控件的关系,使用Behavior的控件必须是直接从属于CoordinatorLayout。

在传统的事件分发流程中,在子控件处理事件过程中,父控件是可以进行拦截的,但一旦父控件进行拦截,那么这次事件只能由父控件处理,而不能再由子控件处理了。

在android5.0之后新的嵌套滑动机制中,引入了:NestScrollChildNestedScrollingParent两个接口,用于协调子父控件滑动状态,而CoordinatorLayout实现了NestedScrollingParent接口,在实现了NestScrollChild这个接口的子控件在滑动时会调用NestedScrollingParent接口的相关方法,将事件发给父控件,由父控件决定是否消费当前事件,在CoordinatorLayout实现的NestedScrollingParent相关方法中会调用Behavior内部的方法。

我们实现Behavior的方法,就可以嵌入整个CoordinatorLayout所构造的嵌套滑动机制中,可以获取到两个方面的内容:

1、某个view监听另一个view的状态变化,例如大小、位置、显示状态等

需要重写layoutDependsOnonDependentViewChanged方法

2、某个view监听CoordinatorLayout内NestedScrollingChild的接口实现类的滑动状态

重写onStartNestedScrollonNestedPreScroll方法。注意:是监听实现了NestedScrollingChild的接口实现类的滑动状态,这就可以解释为什么不能用ScrollView而用NestScrollView来滑动了。

二、怎么使用Behavior?

我们先看下Behavior最常见的几个方法,Behavior还有其他比如onMeasureChild、onLayoutChild等一些方法,列举的这几个方法平时还是比较常见的,知道常见方法的使用后,在研究下其他方法,思路还是相通的。

  1. public static abstract class Behavior<V extends View> {
  2. //指定Behavior关注的滑动方向
  3. public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout,
  4. V child, View directTargetChild, View target, int nestedScrollAxes) {
  5. return false;
  6. }
  7. //用来监听滑动状态,对象消费滚动距离前回调
  8. public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target,
  9. int dx, int dy, int[] consumed) {
  10. // TODO
  11. }
  12. //确定子视图与同级视图的依赖
  13. @Override
  14. public boolean layoutDependsOn(CoordinatorLayout parent, View
  15. child, View dependency) {
  16. return Build.VERSION.SDK_INT >= 11 && dependency instanceof Snackbar.SnackbarLayout;
  17. }
  18. //依赖布局变化时调用
  19. //If the Behavior changes the child view's size or position,
  20. //it should return true. The default implementation returns false
  21. public boolean onDependentViewChanged(CoordinatorLayout parent, V child, View dependency) {
  22. return false;
  23. }
  24. @Override
  25. public boolean onNestedFling(CoordinatorLayout coordinatorLayout, View child, View target, float velocityX, float
  26. velocityY, boolean consumed) {
  27. //快速滑动
  28. return super.onNestedFling(coordinatorLayout, child,target,velocityX, velocityY, consumed);
  29. }
  30. //所有Behavior能在子View之前收到CoordinatorLayout的所有触摸事件
  31. @Override
  32. public boolean onInterceptTouchEvent(CoordinatorLayout parent,View child, MotionEvent ev) {
  33. return super.onInterceptTouchEvent(parent, child, ev);
  34. }
  35. @Override
  36. public boolean onTouchEvent(CoordinatorLayout parent, View child, MotionEvent ev) {
  37. return super.onTouchEvent(parent, child, ev);
  38. }
  39. }

1、某个view监听另一个view的状态变化

这样的效果最常见的如之后导航栏那样:

前面已经说了,如果要监听另一个view的状态变化,需要重写layoutDependsOnonDependentViewChanged方法,看下具体实现:

layout:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <android.support.design.widget.CoordinatorLayout
  3. xmlns:android="http://schemas.android.com/apk/res/android"
  4. xmlns:app="http://schemas.android.com/apk/res-auto"
  5. android:id="@+id/behavior_demo_coordinatorLayout"
  6. android:layout_width="match_parent"
  7. android:layout_height="match_parent">
  8. <android.support.design.widget.AppBarLayout
  9. android:id="@+id/appbar"
  10. android:layout_width="match_parent"
  11. android:layout_height="wrap_content"
  12. android:theme="@style/AppTheme.AppBarOverlay">
  13. <android.support.v7.widget.Toolbar
  14. android:id="@+id/toolbar"
  15. android:layout_width="match_parent"
  16. android:layout_height="?attr/actionBarSize"
  17. app:layout_scrollFlags="scroll|enterAlways|snap"
  18. android:background="?attr/colorPrimary" />
  19. </android.support.design.widget.AppBarLayout>
  20. <android.support.v4.widget.NestedScrollView
  21. android:layout_width="match_parent"
  22. android:layout_height="match_parent"
  23. >
  24. <LinearLayout
  25. android:layout_width="match_parent"
  26. android:layout_height="match_parent"
  27. android:orientation="vertical">
  28. <TextView
  29. android:layout_width="match_parent"
  30. android:layout_height="400dp"
  31. android:text="哈哈哈"
  32. android:gravity="center"/>
  33. <TextView
  34. android:layout_width="match_parent"
  35. android:layout_height="400dp"
  36. android:text="哈哈哈"
  37. android:gravity="center"/>
  38. <TextView
  39. android:layout_width="match_parent"
  40. android:layout_height="400dp"
  41. android:text="哈哈哈"
  42. android:gravity="center"/>
  43. <TextView
  44. android:layout_width="match_parent"
  45. android:layout_height="400dp"
  46. android:text="哈哈哈"
  47. android:gravity="center"/>
  48. <TextView
  49. android:layout_width="match_parent"
  50. android:layout_height="400dp"
  51. android:text="哈哈哈"
  52. android:gravity="center"/>
  53. <TextView
  54. android:layout_width="match_parent"
  55. android:layout_height="400dp"
  56. android:text="哈哈哈"
  57. android:gravity="center"/>
  58. <TextView
  59. android:layout_width="match_parent"
  60. android:layout_height="400dp"
  61. android:text="哈哈哈"
  62. android:gravity="center"/>
  63. <TextView
  64. android:layout_width="match_parent"
  65. android:layout_height="400dp"
  66. android:text="哈哈哈"
  67. android:gravity="center"/>
  68. <TextView
  69. android:layout_width="match_parent"
  70. android:layout_height="400dp"
  71. android:text="哈哈哈"
  72. android:gravity="center"/>
  73. </LinearLayout>
  74. </android.support.v4.widget.NestedScrollView>
  75. <LinearLayout
  76. android:layout_width="match_parent"
  77. android:layout_height="?attr/actionBarSize"
  78. android:layout_gravity="bottom"
  79. android:background="@color/colorPrimary"
  80. android:gravity="center"
  81. app:aucher_id="@id/appbar"
  82. app:layout_behavior="com.mrzk.newstudy.behavior.MyCustomBehavior">
  83. <TextView
  84. android:layout_width="wrap_content"
  85. android:layout_height="wrap_content"
  86. android:layout_gravity="center"
  87. android:textColor="#ffffff"
  88. android:text="底部导航栏"/>
  89. </LinearLayout>
  90. </android.support.design.widget.CoordinatorLayout>

attrs:

  1. <declare-styleable name="MyCustomStyle">
  2. <attr name="anchor_id" format="integer|reference"/>
  3. </declare-styleable>

MyCustomBehavior.java:

  1. public class MyCustomBehavior extends CoordinatorLayout.Behavior<View>{
  2. private int id;
  3. public MyCustomBehavior(Context context, AttributeSet attrs) {
  4. super(context,attrs);
  5. TypedArray typedArray = context.getResources().obtainAttributes(attrs, R.styleable.MyCustomStyle);
  6. id = typedArray.getResourceId(R.styleable.MyCustomStyle_anchor_id, -1);
  7. typedArray.recycle();
  8. }
  9. @Override
  10. public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
  11. // return dependency instanceof AppBarLayout;
  12. return dependency.getId() == id;
  13. }
  14. @Override
  15. public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
  16. child.setTranslationY(-dependency.getTop());
  17. return true;
  18. }
  19. }

重点关注几点:

首先,我们必须重写两个参数的构造方法,因为通过反射实例化的时候就是用的这个构造方法,在这个构造方法中我们也可以获取一些东西,比如我们的依赖控件ID。

之后layoutDependsOn方法我们来决定要依赖哪个view,如果我们知道要依赖的控件,可以直接写:

return dependency instanceof AppBarLayout

而如果我们不知道,也可以由外部传入,在构造方法中获取资源ID来进行判断,这样具有更高的灵活性:

return dependency.getId() == id

我们看下在CoordinatorLayout中两个方法的调用过程:

  1. class OnPreDrawListener implements ViewTreeObserver.OnPreDrawListener {
  2. @Override
  3. public boolean onPreDraw() {
  4. dispatchOnDependentViewChanged(false);
  5. return true;
  6. }
  7. }
  8. void dispatchOnDependentViewChanged(final boolean fromNestedScroll) {
  9. final int layoutDirection = ViewCompat.getLayoutDirection(this);
  10. ...
  11. // Update any behavior-dependent views for the change
  12. for (int j = i + 1; j < childCount; j++) {
  13. final View checkChild = mDependencySortedChildren.get(j);
  14. final LayoutParams checkLp = (LayoutParams) checkChild.getLayoutParams();
  15. final Behavior b = checkLp.getBehavior();
  16. //如果Behavior不为null,layoutDependsOn方法返回true
  17. if (b != null && b.layoutDependsOn(this, checkChild, child)) {
  18. if (!fromNestedScroll && checkLp.getChangedAfterNestedScroll()) {
  19. // If this is not from a nested scroll and we have already been changed
  20. // from a nested scroll, skip the dispatch and reset the flag
  21. checkLp.resetChangedAfterNestedScroll();
  22. continue;
  23. }
  24. //调用onDependentViewChanged方法
  25. final boolean handled = b.onDependentViewChanged(this, checkChild, child);
  26. ...
  27. }
  28. }
  29. }

从调用上来看,在CoordinatorLayout内部的任何子view均可产生依赖关系。

2、某个view监听CoordinatorLayout内NestedScrollingChild的接口实现类的滑动状态

如前所说,重写onStartNestedScrollonNestedPreScroll方法。它可以监听实现了NestedScrollingChild的接口实现类的滑动状态

如果用WebView来滚动的,结果预期要隐藏和显示的appbar没有反应,在外层加上NestScrollView就解决了问题,这是因为WebView没有实现NestedScrollingChild接口造成的,因为滑动控件的滑动状态是通过NestedScrollingChild接口方法处理中来调用NestedScrollingParent接口方法来实现。

实现上面的效果我们还可以用重写onStartNestedScrollonNestedPreScroll来实现。来看看吧:

  1. public class MyCustomBehavior extends CoordinatorLayout.Behavior<View>{
  2. private boolean isAnimate;
  3. public MyCustomBehavior(Context context, AttributeSet attrs) {
  4. super(context,attrs);
  5. }
  6. @Override
  7. public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {
  8. return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL)!=-1;//判断是否为垂直滚动
  9. }
  10. @Override
  11. public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx, int dy, int[] consumed) {
  12. //super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
  13. if (dy>0 &&!isAnimate && child.getTranslationY()<child.getHeight()){
  14. child.setTranslationY(child.getTranslationY() + dy);
  15. }else if (dy<0 &&!isAnimate && child.getTranslationY()>0){
  16. child.setVisibility(View.VISIBLE);
  17. if (child.getTranslationY()+dy<0){
  18. child.setTranslationY(0);
  19. }else {
  20. child.setTranslationY(child.getTranslationY()+dy);
  21. }
  22. }
  23. }
  24. @Override
  25. public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target) {
  26. //super.onStopNestedScroll(coordinatorLayout, child, target);
  27. if (child.getTranslationY()<child.getHeight()/2){
  28. changeState(child,0);
  29. }else{
  30. changeState(child,child.getHeight());
  31. }
  32. }
  33. private void changeState(final View view, final int scrollY) {
  34. ViewPropertyAnimator animator = view.animate().translationY(scrollY).setInterpolator(new FastOutSlowInInterpolator()).setDuration(200*scrollY/view.getHeight());
  35. animator.setListener(new Animator.AnimatorListener() {
  36. @Override
  37. public void onAnimationStart(Animator animator) {
  38. isAnimate=true;
  39. }
  40. @Override
  41. public void onAnimationEnd(Animator animator) {
  42. if (view.getTranslationY() == view.getHeight()){
  43. view.setVisibility(View.GONE);
  44. }
  45. isAnimate=false;
  46. }
  47. @Override
  48. public void onAnimationCancel(Animator animator) {
  49. view.setTranslationY(scrollY);
  50. }
  51. @Override
  52. public void onAnimationRepeat(Animator animator) {
  53. }
  54. });
  55. animator.start();
  56. }
  57. }

用这个来实现的话,需要注意的是滚动控件必须实现NestedScrollingChild接口,而没有实现该接口且不调用 dispatchNestedScroll相关接口的滚动控件如ScrollView、WebView、ListView是没有作用的。

三、从源码角度看为什么要这么使用Behavior

我们从Behavior获取实例化开始看,看CoordinatorLayout.LayoutParams源码:

  1. LayoutParams(Context context, AttributeSet attrs) {
  2. super(context, attrs);
  3. final TypedArray a = context.obtainStyledAttributes(attrs,
  4. R.styleable.CoordinatorLayout_LayoutParams);
  5. this.gravity = a.getInteger(
  6. R.styleable.CoordinatorLayout_LayoutParams_android_layout_gravity,
  7. Gravity.NO_GRAVITY);
  8. mAnchorId = a.getResourceId(R.styleable.CoordinatorLayout_LayoutParams_layout_anchor,
  9. View.NO_ID);
  10. this.anchorGravity = a.getInteger(
  11. R.styleable.CoordinatorLayout_LayoutParams_layout_anchorGravity,
  12. Gravity.NO_GRAVITY);
  13. this.keyline = a.getInteger(R.styleable.CoordinatorLayout_LayoutParams_layout_keyline,
  14. -1);
  15. mBehaviorResolved = a.hasValue(
  16. R.styleable.CoordinatorLayout_LayoutParams_layout_behavior);
  17. if (mBehaviorResolved) {
  18. //在这里解析获取Behavior
  19. mBehavior = parseBehavior(context, attrs, a.getString(
  20. R.styleable.CoordinatorLayout_LayoutParams_layout_behavior));
  21. }
  22. a.recycle();
  23. }

接着来看看具体是怎么获取到Behavior的:

  1. static final Class<?>[] CONSTRUCTOR_PARAMS = new Class<?>[] {
  2. Context.class,
  3. AttributeSet.class
  4. };
  5. static Behavior parseBehavior(Context context, AttributeSet attrs, String name) {
  6. if (TextUtils.isEmpty(name)) {
  7. return null;
  8. }
  9. final String fullName;
  10. if (name.startsWith(".")) {
  11. // Relative to the app package. Prepend the app package name.
  12. fullName = context.getPackageName() + name;
  13. } else if (name.indexOf('.') >= 0) {
  14. // Fully qualified package name.
  15. fullName = name;
  16. } else {
  17. // Assume stock behavior in this package (if we have one)
  18. fullName = !TextUtils.isEmpty(WIDGET_PACKAGE_NAME)
  19. ? (WIDGET_PACKAGE_NAME + '.' + name)
  20. : name;
  21. }
  22. try {
  23. Map<String, Constructor<Behavior>> constructors = sConstructors.get();
  24. if (constructors == null) {
  25. constructors = new HashMap<>();
  26. sConstructors.set(constructors);
  27. }
  28. Constructor<Behavior> c = constructors.get(fullName);
  29. if (c == null) {
  30. //这里通过反射获取到Behavior
  31. final Class<Behavior> clazz = (Class<Behavior>) Class.forName(fullName, true,
  32. context.getClassLoader());
  33. //获取两个参数的构造方法
  34. c = clazz.getConstructor(CONSTRUCTOR_PARAMS);
  35. c.setAccessible(true);
  36. constructors.put(fullName, c);
  37. }
  38. //在这里实例化
  39. return c.newInstance(context, attrs);
  40. } catch (Exception e) {
  41. throw new RuntimeException("Could not inflate Behavior subclass " + fullName, e);
  42. }
  43. }

这里就解释了为什么我们每次继承都要写两个参数的构造方法了,如果没有,则会报Caused by: java.lang.NoSuchMethodException: [class android.content.Context, interface android.util.AttributeSet]错误。

然后我们看看主要关注的onStartNestedScroll和onNestedPreScroll的调用时机,当实现了NestScrollChild接口的子控件滑动时,会回调CoordinatorLayout中的onStartNestedScroll方法:

  1. public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
  2. boolean handled = false;
  3. final int childCount = getChildCount();
  4. for (int i = 0; i < childCount; i++) {
  5. final View view = getChildAt(i);
  6. final LayoutParams lp = (LayoutParams) view.getLayoutParams();
  7. //获取Behavior
  8. final Behavior viewBehavior = lp.getBehavior();
  9. if (viewBehavior != null) {
  10. //true if the Behavior wishes to accept this nested scroll
  11. //调用viewBehavior.onStartNestedScroll方法,如果返回true表示希望接受滚动事件
  12. final boolean accepted = viewBehavior.onStartNestedScroll(this, view, child, target,
  13. nestedScrollAxes);
  14. handled |= accepted;
  15. lp.acceptNestedScroll(accepted);
  16. } else {
  17. lp.acceptNestedScroll(false);
  18. }
  19. }
  20. return handled;
  21. }

当实现了NestScrollChild接口的子控件滚动时,在消费滚动距离之前把总的滑动距离传给父布局,即CoordinatorLayout。然后回调onNestedPreScroll方法:

  1. public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
  2. int xConsumed = 0;
  3. int yConsumed = 0;
  4. boolean accepted = false;
  5. final int childCount = getChildCount();
  6. for (int i = 0; i < childCount; i++) {
  7. final View view = getChildAt(i);
  8. final LayoutParams lp = (LayoutParams) view.getLayoutParams();
  9. //遍历所有子控件 如果不希望接受处理事件 跳出本次循环
  10. if (!lp.isNestedScrollAccepted()) {
  11. continue;
  12. }
  13. //获得child view的Behavior
  14. final Behavior viewBehavior = lp.getBehavior();
  15. if (viewBehavior != null) {
  16. mTempIntPair[0] = mTempIntPair[1] = 0;
  17. //调用viewBehavior.onNestedPreScroll方法
  18. viewBehavior.onNestedPreScroll(this, view, target, dx, dy, mTempIntPair);
  19. //dy大于0是向上滚动 小于0是向下滚动
  20. xConsumed = dx > 0 ? Math.max(xConsumed, mTempIntPair[0])
  21. : Math.min(xConsumed, mTempIntPair[0]);
  22. yConsumed = dy > 0 ? Math.max(yConsumed, mTempIntPair[1])
  23. : Math.min(yConsumed, mTempIntPair[1]);
  24. accepted = true;
  25. }
  26. }
  27. //consumed:表示父布局要消费的滚动距离,consumed[0]和consumed[1]分别表示父布局在x和y方向上消费的距离
  28. consumed[0] = xConsumed;
  29. consumed[1] = yConsumed;
  30. if (accepted) {
  31. dispatchOnDependentViewChanged(true);
  32. }
  33. }

然后我们来研究layoutDependsOn和onDependentViewChanged的调用时机,看CoordinatorLayout的dispatchOnDependentViewChanged方法:

  1. void dispatchOnDependentViewChanged(final boolean fromNestedScroll) {
  2. final int layoutDirection = ViewCompat.getLayoutDirection(this);
  3. final int childCount = mDependencySortedChildren.size();
  4. for (int i = 0; i < childCount; i++) {
  5. final View child = mDependencySortedChildren.get(i);
  6. final LayoutParams lp = (LayoutParams) child.getLayoutParams();
  7. // Check child views before for anchor
  8. for (int j = 0; j < i; j++) {
  9. final View checkChild = mDependencySortedChildren.get(j);
  10. if (lp.mAnchorDirectChild == checkChild) {
  11. offsetChildToAnchor(child, layoutDirection);
  12. }
  13. }
  14. // Did it change? if not continue
  15. final Rect oldRect = mTempRect1;
  16. final Rect newRect = mTempRect2;
  17. getLastChildRect(child, oldRect);
  18. getChildRect(child, true, newRect);
  19. if (oldRect.equals(newRect)) {
  20. continue;
  21. }
  22. recordLastChildRect(child, newRect);
  23. // Update any behavior-dependent views for the change
  24. for (int j = i + 1; j < childCount; j++) {
  25. final View checkChild = mDependencySortedChildren.get(j);
  26. final LayoutParams checkLp = (LayoutParams) checkChild.getLayoutParams();
  27. final Behavior b = checkLp.getBehavior();
  28. //behavior不为null同时layoutDependsOn返回了true
  29. if (b != null && b.layoutDependsOn(this, checkChild, child)) {
  30. if (!fromNestedScroll && checkLp.getChangedAfterNestedScroll()) {
  31. // If this is not from a nested scroll and we have already been changed
  32. // from a nested scroll, skip the dispatch and reset the flag
  33. checkLp.resetChangedAfterNestedScroll();
  34. continue;
  35. }
  36. //this:CoordinatorLayout
  37. //checkChild:behavior所属的view
  38. //child:依赖的view
  39. //true if the Behavior changed the child view's size or position, false otherwise
  40. final boolean handled = b.onDependentViewChanged(this, checkChild, child);
  41. if (fromNestedScroll) {
  42. // If this is from a nested scroll, set the flag so that we may skip
  43. // any resulting onPreDraw dispatch (if needed)
  44. checkLp.setChangedAfterNestedScroll(handled);
  45. }
  46. }
  47. }
  48. }
  49. }

这段代码在onNestedScroll、onNestedPreScroll、onNestedFling和OnPreDrawListener.onPreDraw方法中都有调用,判断依赖控件大小或者位置变化时及时通知behavior,子控件作出相应调整。

这里把我们主要关心的控件的调用时机大体走读了一遍,对于为什么在behavior中调用相关方法可以依赖和监听其他控件的滑动事件应该有了一定认识,如果关注CoordinatorLayout的实现细节,务必要搞明白NestScrollChild和NestedScrollingParent机制的调用关系,建议查看NestScrollView源码,这里给出NestScrollChild和NestedScrollingParent的一些主要方法说明,对其具体了解还可以看Android 嵌套滑动机制(NestedScrolling)这篇文章。

NestScrollChild

public void setNestedScrollingEnabled(boolean enabled)

enabled:true表示view使用嵌套滚动,false表示禁用

public boolean startNestedScroll(int axes)

axes:表示滚动的方向如:ViewCompat.SCROLL_AXIS_VERTICAL(垂直方向滚动)和 ViewCompat.SCROLL_AXIS_HORIZONTAL(水平方向滚动)

return:true表示本次滚动支持嵌套滚动,false不支持

startNestedScroll表示view开始滚动了,一般是在ACTION_DOWN中调用,如果返回true则表示父布局支持嵌套滚动

public void stopNestedScroll()

在事件结束比如ACTION_UP或者ACTION_CANCLE中调用stopNestedScroll,告诉父布局滚动结束

public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow)

dxConsumed: 表示view消费了x方向的距离长度

dyConsumed: 表示view消费了y方向的距离长度

dxUnconsumed: 表示滚动产生的x滚动距离还剩下多少没有消费>dyUnconsumed: 表示滚动产生的y滚动距离还剩下多少没有消费

offsetInWindow: 表示剩下的距离dxUnconsumed和dyUnconsumed使得view在父布局中的位置偏移了多少

在view消费滚动距离之后,把剩下的滑动距离再次传给父布局

public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow)

dx: 表示view本次x方向的滚动的总距离长度

dy: 表示view本次y方向的滚动的总距离长度

consumed: 表示父布局消费的距离,consumed[0]表示x方向,consumed[1]表示y方向

参数offsetInWindow: 表示剩下的距离dxUnconsumed和dyUnconsumed使得view在父布局中的位置偏移了多少

view消费滚动距离之前把总的滑动距离传给父布局

* public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed)*

velocityX:X方向滚动的距离

velocityY:Y方向滚动的距离

consumed:父布局是否消费

public boolean dispatchNestedPreFling(float velocityX, float velocityY)

velocityX:X方向滚动的距离

velocityY:Y方向滚动的距离

NestedScrollingParent

public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes)

child:ViewParent包含触发嵌套滚动的view的对象

target:触发嵌套滚动的view (在这里如果不涉及多层嵌套的话,child和target)是相同的

nestedScrollAxes:就是嵌套滚动的滚动方向了.

当子view的调用NestedScrollingChild的方法startNestedScroll时,会调用该方法

该方法决定了当前控件是否能接收到其内部View(并非是直接子View)滑动时的参数

public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes);

如果onStartNestedScroll方法返回true,之后就会调用该方法.它是让嵌套滚动在开始滚动之前,让布局容器(viewGroup)或者它的父类执行一些配置的初始化(React to the successful claiming of a nested scroll operation)

public void onStopNestedScroll(View target)

当子view调用stopNestedScroll时会调用该方法,停止滚动

public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed)

target:同上

dxConsumed:表示target已经消费的x方向的距离

dyConsumed:表示target已经消费的x方向的距离

dxUnconsumed:表示x方向剩下的滑动距离

dyUnconsumed:表示y方向剩下的滑动距离

当子view调用dispatchNestedScroll方法时,会调用该方法

public void onNestedPreScroll(View target, int dx, int dy, int[] consumed)

target:同上

dx:表示target本次滚动产生的x方向的滚动总距离

dy:表示target本次滚动产生的y方向的滚动总距离

consumed:表示父布局要消费的滚动距离,consumed[0]和consumed[1]分别表示父布局在x和y方向上消费的距离.

当子view调用dispatchNestedPreScroll方法是,会调用该方法

调用时机:

子view 父view
startNestedScroll onStartNestedScroll、onNestedScrollAccepted
dispatchNestedPreScroll onNestedPreScroll
dispatchNestedScroll onNestedScroll
stopNestedScroll onStopNestedScroll

深入理解CoordinatorLayout.Behavior的更多相关文章

  1. 自定义CoordinatorLayout Behavior 隐藏Footer View

    在用新的控件中,我们可以用Toolbar与CoordinatorLayout实现 向上滚动隐藏的效果,可是官方并没有找到向上隐藏底部导航的功能,有一些第三方的框架实现了. 在Android M,Coo ...

  2. 深入理解Android开发中的CoordinatorLayout Behavior

    在使用Android设计支持库(Android Design Support Library)时,很难避开CoordinatorLayout:设计库中有很多视图都需要CoordinatorLayout ...

  3. <Android 基础(二十)> CoordinatorLayout Behavior

    介绍 Interaction behavior plugin for child views of {@link CoordinatorLayout}. A Behavior implements o ...

  4. 源代码看CoordinatorLayout.Behavior原理

    在上一篇博客CoordinatorLayout高级使用方法-自己定义Behavior中,我们介绍了怎样去自己定义一个CoordinatorLayout的Behavior.通过文章也能够看出Behavi ...

  5. [置顶] 针对 CoordinatorLayout 及 Behavior 的一次细节较真

    我认真不是为了输赢,我就是认真.– 罗永浩 我一直对 Material Design 很感兴趣,每次在官网上阅读它的相关文档时,我总会有更进一步的体会.当然,Material Design 并不是仅仅 ...

  6. (转载)自定义CoordinatorLayout的Behavior(2):实现淘宝和QQ ToolBar透明渐变效果

    自定义CoordinatorLayout的Behavior(2):实现淘宝和QQ ToolBar透明渐变效果 作者 小武站台 关注 2016.02.19 11:34 字数 1244 阅读 3885评论 ...

  7. 关于CoordinatorLayout与Behavior的一点分析

    Behavior是Android新出的Design库里新增的布局概念.Behavior只有是CoordinatorLayout的直接子View才有意义.可以为任何View添加一个Behavior.Be ...

  8. CoordinatorLayout 自定义Behavior并不难,由简到难手把手带你飞

    先来看看最终的效果~~ 本文同步至博主的私人博客wing的地方酒馆 嗯..一个是头像上移的 另一个是模仿UC浏览器的. (PД`q.)你不是说!有三款的吗,怎么只有两款!!!! 不要急嘛... 说了从 ...

  9. CoordinatorLayout 自定义Behavior并不难,由简到难手把手带你撸三款!

    先来看看最终的效果~~ 本文同步至博主的私人博客wing的地方酒馆 嗯..一个是头像上移的 另一个是模仿UC浏览器的. (PД`q.)你不是说!有三款的吗,怎么只有两款!!!! 不要急嘛... 说了从 ...

随机推荐

  1. [UOJ] #217. 【UNR #1】奇怪的线段树

    题解见大佬博客 我的丑陋代码: #include<cstdio> #include<cstring> #include<cstdlib> inline int re ...

  2. Codeforces 671 D. Roads in Yusland

    题目描述 Mayor of Yusland just won the lottery and decided to spent money on something good for town. Fo ...

  3. SPOJ DQUERY树状数组离线or主席树

    D-query Time Limit: 227MS   Memory Limit: 1572864KB   64bit IO Format: %lld & %llu Submit Status ...

  4. python 2week

    本节内容 列表.元组操作 字符串操作 字典操作 集合操作 文件操作 字符编码与转码 列表是我们最以后最常用的数据类型之一,通过列表可以对数据实现最方便的存储.修改等操作 定义列表 1 names =  ...

  5. js中json字符串与json对象的相互转换

    web前端开发过程中,数据传输json是以字符串的形式传递,而js操作的是JSON对象. 一.JSON字符串转换为JSON对象 var obj = JSON.parse(str[, reviver]) ...

  6. lgp20151222 java中如何将Object类型转换为int类型

    if (object instanceof Integer) {    Integer.parseInt(object.toString()); } 很简单是不是?我就想提醒下自己,java有个特殊词 ...

  7. 首届.NET Core开源峰会

    首届.NET Core开源峰会 代号:dnc 2018 亮点:去中心化.社区驱动 开源峰会 时间:2018年5月20日 周日 地点:在线峰会.远程参与 形式:每个主题5分钟-15分钟闪电演讲 演讲方式 ...

  8. jquery插件存档

    1.选择插件selectMenu github地址:https://github.com/josiaho/selectMenu 2.选择插件bootstrap_multiselect 官方地址:htt ...

  9. spark on yarn 运行问题记录

    问题一: 18/03/15 07:59:23 INFO yarn.Client: client token: N/A diagnostics: Application application_1521 ...

  10. 解决linux中使用git,ssh每次都要输入密码

    在linux中使用git,去提交或者下载代码都是很方便的,但是最近新配置了一套系统,发现每次git pull或者其他动作都需要输入密码. 想一想不对劲啊,我使用的是ssh的方式clone的代码,而且在 ...