前言

嵌套布局是事件分发的衍生内容,理解了嵌套布局的实现方式,Md的很多效果都是毛毛雨了

​​原文地址进入​​

Android 嵌套布局简析_嵌套

Uml 时序图形

Android 嵌套布局简析_android_02

 嵌套布局实现方式,以及 5.0 中与嵌套布局有关联的组件

提起嵌套布局,MD中的 CoordinatorLayout,SwipeRefreshLayout,RecyclerView 当然还有V4包下的NestedScrollView,都有直接关系,因为他们是 NestedScrollingParent(NP) 或者 NestedScrollingChild(NC) 直接或者间接子类。

那么如何才能进行嵌套布局自定义?
通过成为NP,NC子类进行嵌套布局开发妥妥的。
下面是NP,NC 具体方法,调用时序查看blog头部的时序图结合源码进行理解
NestedScrollingChildHelper // Child辅助类
NestedScrollingParentHelper // Parent辅助类

核心类 NestedScrollingParent

package android.support.v4.view;

import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;

public interface NestedScrollingParent {

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

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


public void onStopNestedScroll(View target);


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

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


public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed);


public boolean onNestedPreFling(View target, float velocityX, float velocityY);


public int getNestedScrollAxes();
}

核心类 NestedScrollingChild

package android.support.v4.view;

import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewParent;


public interface NestedScrollingChild {

public void setNestedScrollingEnabled(boolean enabled);


public boolean isNestedScrollingEnabled();


public boolean startNestedScroll(int axes);


public void stopNestedScroll();


public boolean hasNestedScrollingParent();


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


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


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


public boolean dispatchNestedPreFling(float velocityX, float velocityY);
}

简单一下回调方法的介含义,以及需要在那里处理事件:

onStartNestedScroll该方法,一定要按照自己的需求返回true,该方法决定了当前控件是否能接收到其内部View(非并非是直接子View)滑动时的参数;假设你只涉及到纵向滑动,这里可以根据nestedScrollAxes这个参数,进行纵向判断。

onNestedPreScroll该方法的会传入内部View移动的dx,dy,如果你需要消耗一定的dx,dy,就通过最后一个参数consumed进行指定,例如我要消耗一半的dy,就可以写consumed[1]=dy/2

onNestedFling你可以捕获对内部View的fling事件,如果return true则表示拦截掉内部View的事件。

主要关注的就是这三个方法~

这里内部View表示不一定非要是直接子View,只要是内部View即可。

下面看一下我们具体的实现:

public class StickyNavLayout extends LinearLayout implements NestedScrollingParent
{
@Override
public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes)
{
return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
}
@Override
public void onNestedPreScroll(View target, int dx, int dy, int[] consumed)
{
boolean hiddenTop = dy > 0 && getScrollY() < mTopViewHeight;
boolean showTop = dy < 0 && getScrollY() > 0 && !ViewCompat.canScrollVertically(target, -1);

if (hiddenTop || showTop)
{
scrollBy(0, dy);
consumed[1] = dy;
}
}
@Override
public boolean onNestedPreFling(View target, float velocityX, float velocityY)
{
if (getScrollY() >= mTopViewHeight) return false;
fling((int) velocityY);
return true;
}
}

onStartNestedScroll中,我们判断了如果是纵向返回true,这个一般是需要内部的View去传入的,你要是不确定,或者担心内部View编写的不规范,你可以直接return true;

onNestedPreScroll中,我们判断,如果是上滑且顶部控件未完全隐藏,则消耗掉dy,即consumed[1]=dy;如果是下滑且内部View已经无法继续下拉,则消耗掉dy,即consumed[1]=dy,消耗掉的意思,就是自己去执行scrollBy,实际上就是我们的StickNavLayout滑动。

此外,这里还处理了fling,通过onNestedPreFling方法,这个可以根据自己需求定了,当顶部控件显示时,fling可以让顶部控件隐藏或者显示。

Eg:当然有人会说用手势 + 事件分发拦截同样可以实现这个效果,的确可以实现,只是效果会打折扣

事件分发是这样的:子View首先得到事件处理权,处理过程中,父View可以对其拦截,但是拦截了以后就无法再还给子View(本次手势内)。
NestedScrolling机制是这样的:内部View在滚动的时候,首先将dx,dy交给NestedScrollingParent,NestedScrollingParent可对其进行部分消耗,剩余的部分还给内部View。

这里针对实现了NetstedScrollingParent 的类继续log输出,直观显示滚动事件调用链。

不进行滑动

vlayout E/StickyNavLayout: onStartNestedScroll
05-22 14 E/StickyNavLayout: onNestedScrollAccepted
05-22 14 E/StickyNavLayout: onStopNestedScroll

进行滑动

05-22 14:46:11.655 27721-27721/: onStartNestedScroll
05-22 14:46:11.655 27721-27721/: onNestedScrollAccepted
05-22 14:46:11.875 27721-27721/: onNestedPreScroll
05-22 14:46:11.895 27721-27721/: onNestedPreScroll
05-22 14:46:11.915 27721-27721/: onNestedPreScroll
05-22 14:46:11.925 27721-27721/: onNestedPreScroll
05-22 14:46:11.945 27721-27721/: onNestedPreScroll
05-22 14:46:11.965 27721-27721/: onNestedPreScroll
05-22 14:46:11.975 27721-27721/: onNestedPreScroll
05-22 14:46:11.995 27721-27721/: onNestedPreScroll
05-22 14:46:12.015 27721-27721/: onNestedPreScroll
05-22 14:46:12.025 27721-27721/: onNestedPreScroll
05-22 14:46:12.045 27721-27721/: onStopNestedScroll

快速滑动

05-22 14:48:18.245 27721-27721/: onStartNestedScroll
05-22 14:48:18.245 27721-27721/: onNestedScrollAccepted
05-22 14:48:18.315 27721-27721/: onNestedPreScroll
05-22 14:48:18.315 27721-27721/: onNestedScroll

05-22 14:48:18.315 27721-27721/: onNestedPreFling
05-22 14:48:18.315 27721-27721/: onNestedFling
05-22 14:48:18.315 27721-27721/: onStopNestedScroll

抛,快速 滑动触发Fling

05-22 14:49:51.875 27721-27721/: onNestedPreFling
05-22 14:49:51.875 27721-27721/: onNestedFling

1.嵌套布局模式。依附于onTouchEvent() 进行处理。在down,move,up 中进行回调。Helper 类中实现

  1. 固定大小的 int [2],集合。 作为形参,进行传递,在函数中可以通过形参进行赋值

3.scroller 和 后面 api 19 新添加对于over 边界处理的 overscroller ,两者80% api重复,后者多了几个处理Over bound的 api

总结:

嵌套布局滑动和事件分发之间的区别,进行自定义扩展嵌套布局需要实现那些方法,实现那几个类。嵌套布局的延伸兼容性等有个大体的了解。细节部分还请自行体验


引用:
Android NestedScrolling 实战
​​​https://race604.com/android-nested-scrolling/​​​
关于View的ScrollTo, getScrollX 和 getScrollY
​​​http://www.xuebuyuan.com/2013505.html​