给子View设置Behavior的原理
如果我们在给CoordinatorLayout设置app:layout_behavior属性
app:layout_behavior="@string/hide_bottom_view_on_scroll_behavior"
CoordinatorLayout会去获取layout_behavior属性
this.mBehaviorResolved = a.hasValue(styleable.CoordinatorLayout_Layout_layout_behavior);
if (this.mBehaviorResolved) {
this.mBehavior = CoordinatorLayout.parseBehavior(context, attrs, a.getString(styleable.CoordinatorLayout_Layout_layout_behavior));
}
如果layout_behavior属性存在,则会进行解析
static CoordinatorLayout.Behavior parseBehavior(Context context, AttributeSet attrs, String name) {
String fullName;
if (name.startsWith(".")) { //如果没有加报名,自动添加报名
fullName = context.getPackageName() + name;
} else if (name.indexOf(46) >= 0) {
fullName = name;
} else {
fullName = !TextUtils.isEmpty(WIDGET_PACKAGE_NAME) ? WIDGET_PACKAGE_NAME + '.' + name : name;
}try {
Map> constructors = (Map)sConstructors.get();
if (constructors == null) {
constructors = new HashMap();
sConstructors.set(constructors);
}Constructor c = (Constructor)((Map)constructors).get(fullName);
if (c == null) {
//反射加载Behavior
Class clazz = context.getClassLoader().loadClass(fullName);
c = clazz.getConstructor(CONSTRUCTOR_PARAMS);
c.setAccessible(true);
((Map)constructors).put(fullName, c);
}return (CoordinatorLayout.Behavior)c.newInstance(context, attrs);
} catch (Exception var7) {
throw new RuntimeException("Could not inflate Behavior subclass " + fullName, var7);
}
}
可以看到,会通过反射来加载layout_behavior,并存储到constructors这个Map中
CoordinatorLayout如何结合Behavior实现对子View的事件响应
父View(CoordinatorLayout)实现NestedScrollingParent2接口,子View(Recyclerview)实现NestedScrollingChild2接口
然后这两个接口的实现会交由NestedScrollingChildHelper来实现
来看下Recyclerview,就有如下代码
private NestedScrollingChildHelper getScrollingChildHelper() {
if (this.mScrollingChildHelper == null) {
this.mScrollingChildHelper = new NestedScrollingChildHelper(this);
}return this.mScrollingChildHelper;
}public boolean startNestedScroll(int axes) {
return this.getScrollingChildHelper().startNestedScroll(axes);
}
再来看NestedScrollingChildHelper
public boolean startNestedScroll(int axes, int type) {
if (this.hasNestedScrollingParent(type)) {
return true;
} else {
if (this.isNestedScrollingEnabled()) {
ViewParent p = this.mView.getParent();
for(View child = this.mView;
p != null;
p = p.getParent()) {
//调用父类的onStartNestedScroll -> 比如CoordinatorLayout 会遍历子View,获得子View的Behavior,调用Behavior的onStartNestedScroll方法
if (ViewParentCompat.onStartNestedScroll(p, child, this.mView, axes, type)) {
this.setNestedScrollingParentForType(type, p);
//调用父类onNestedScrollAccepted
ViewParentCompat.onNestedScrollAccepted(p, child, this.mView, axes, type);
return true;
}if (p instanceof View) {
child = (View)p;
}
}
}return false;
}
}
可以看到,最终会遍历子View,获得子View的Behavior,调用Behavior的onStartNestedScroll和onNestedScrollAccepted方法
同理,Recyclerview的dispatchNestedScroll通过NestedScrollingChildHelper也会调用onNestedScroll方法
来看NestedScrollingChildHelper#dispatchNestedScroll
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, @Nullable int[] offsetInWindow, int type) {
if (this.isNestedScrollingEnabled()) {
ViewParent parent = this.getNestedScrollingParentForType(type);
if (parent == null) {
return false;
}if (dxConsumed != 0 || dyConsumed != 0 || dxUnconsumed != 0 || dyUnconsumed != 0) {
int startX = 0;
int startY = 0;
if (offsetInWindow != null) {
this.mView.getLocationInWindow(offsetInWindow);
startX = offsetInWindow[0];
startY = offsetInWindow[1];
}ViewParentCompat.onNestedScroll(parent, this.mView, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type);
if (offsetInWindow != null) {
this.mView.getLocationInWindow(offsetInWindow);
offsetInWindow[0] -= startX;
offsetInWindow[1] -= startY;
}return true;
}if (offsetInWindow != null) {
offsetInWindow[0] = 0;
offsetInWindow[1] = 0;
}
}return false;
}
【3.1.3_CoordinatorLayout原理】以一个自定义的Behavior为例,可以看到调用的对应方法
public class ScaleBehavior extends CoordinatorLayout.Behavior {private FastOutLinearInInterpolator mFastOutLinearInInterpolator = new FastOutLinearInInterpolator();
private LinearOutSlowInInterpolator mLinearOutSlowInInterpolator = new LinearOutSlowInInterpolator();
public ScaleBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}@Override
public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View directTargetChild, @NonNull View target, int axes, int type) {
return axes == ViewCompat.SCROLL_AXIS_VERTICAL;
//垂直滚动
}@Override
public void onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type);
if (dyConsumed > 0 && !isRunning && child.getVisibility() == View.VISIBLE){//向下滑动,缩放隐藏控件
scaleHide(child);
}else if (dyConsumed < 0 && !isRunning && child.getVisibility() == View.INVISIBLE){ //向上滑动,缩放显示控件
scaleShow(child);
}
}private boolean isRunning;
private void scaleShow(V child) {
child.setVisibility(View.VISIBLE);
ViewCompat.animate(child)
.scaleX(1)
.scaleY(1)
.setDuration(500)
.setInterpolator(mLinearOutSlowInInterpolator)
.setListener(new ViewPropertyAnimatorListenerAdapter(){
@Override
public void onAnimationStart(View view) {
super.onAnimationStart(view);
isRunning = true;
}@Override
public void onAnimationEnd(View view) {
super.onAnimationEnd(view);
isRunning = false;
}@Override
public void onAnimationCancel(View view) {
super.onAnimationCancel(view);
isRunning = false;
}
});
}private void scaleHide(final V child) {
ViewCompat.animate(child)
.scaleX(0)
.scaleY(0)
.setDuration(500)
.setInterpolator(mFastOutLinearInInterpolator)
.setListener(new ViewPropertyAnimatorListenerAdapter(){
@Override
public void onAnimationStart(View view) {
super.onAnimationStart(view);
isRunning = true;
}@Override
public void onAnimationEnd(View view) {
super.onAnimationEnd(view);
isRunning = false;
child.setVisibility(View.INVISIBLE);
}@Override
public void onAnimationCancel(View view) {
super.onAnimationCancel(view);
isRunning = false;
}
});
} @Override
public void onNestedScrollAccepted(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View directTargetChild, @NonNull View target, int axes, int type) {
super.onNestedScrollAccepted(coordinatorLayout, child, directTargetChild, target, axes, type);
}
}