代码路径位于:com.google.android.material.appbar.AppBarLayout
首先看下构造方法:分为无参和有参两种类型。
有参方法主要是设置了behavior_overlapTop这个值。暂时不看这个值。
public ScrollingViewBehavior() {
}
public ScrollingViewBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, styleable.ScrollingViewBehavior_Layout);
this.setOverlayTop(a.getDimensionPixelSize(styleable.ScrollingViewBehavior_Layout_behavior_overlapTop, 0));
a.recycle();
}
来看看依赖,解释一下,第一个不用说,就是当前的CoordinatorLayout,第二个参数是我们设置这个Behavior的View,第三个是我们关心的那个View。这里意思是我要依赖AppBarLayout。依赖完了如何监听依赖变化呢,那是另外一个方法。
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
return dependency instanceof AppBarLayout;
}
这里调用了offsetChildAsNeeded。接着看。
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
this.offsetChildAsNeeded(child, dependency);
this.updateLiftedStateIfNeeded(child, dependency);
return false;
}
private void offsetChildAsNeeded(View child, View dependency) {
//获取到Depency的设置的Behavior,这里就是AppBarLayout 上设置的Behavior。
androidx.coordinatorlayout.widget.CoordinatorLayout.Behavior behavior = ((androidx.coordinatorlayout.widget.CoordinatorLayout.LayoutParams)dependency.getLayoutParams()).getBehavior();
//强转
if (behavior instanceof AppBarLayout.BaseBehavior) {
AppBarLayout.BaseBehavior ablBehavior = (AppBarLayout.BaseBehavior)behavior;
//代表如果AppBarLayout移动了多少,那么被依赖的View也要移动多少。
ViewCompat.offsetTopAndBottom(child, dependency.getBottom() - child.getTop() + ablBehavior.offsetDelta + this.getVerticalLayoutGap() - this.getOverlapPixelsForOffset(dependency));
}
}
现在来看下AppBarLayout经常用到的Behavior。BaseBehavior 继承了HeaderBehvior。
现在来看下HeaderBehavior。
HeaderBehavior重写了onInterceptTouchEvent,onTouchEvent,并且创建了FlingRunnable类,这里不具体分析细节了,作为AppBarLayout的默认Behavior,主要实现的是Header的一些行为,注意这里的Header实际是AppBarLayout包裹的第一个View。
BaseBehavior 实现了NestScrollingParent的一些默认方法。这里来重点分析下。
canScrollChildren,因为此方法就判定了是否能够滚动子View。
private boolean canScrollChildren(CoordinatorLayout parent, T child, View directTargetChild) {
return child.hasScrollableChildren() && parent.getHeight() - directTargetChild.getHeight() <= child.getHeight();
}
关键是判断getTotalScrollRange的滚动距离。默认totalScrollRange为-1.走else方法。
public final int getTotalScrollRange() {
if (this.totalScrollRange != -1) {
return this.totalScrollRange;
} else {
int range = 0;
int i = 0;
for(int z = this.getChildCount(); i < z; ++i) {
View child = this.getChildAt(i);
AppBarLayout.LayoutParams lp = (AppBarLayout.LayoutParams)child.getLayoutParams();
int childHeight = child.getMeasuredHeight();
//这里获取子类的scrollFlags。如果不具备scroll 标识,那么滚动距离为0,不能滚动。直接brak,然后return 0.
int flags = lp.scrollFlags;
if ((flags & 1) == 0) {
break;
}
range += childHeight + lp.topMargin + lp.bottomMargin;
if ((flags & 2) != 0) {
range -= ViewCompat.getMinimumHeight(child);
break;
}
}
return this.totalScrollRange = Math.max(0, range - this.getTopInset());
}
}
好了,上文从ScrollingViewBahavior出发,来分析AppBarLayout的行为,Behavior作为一个比较新的类,主要配合CoordinatorLayout 来使用。CoordinatorLayout 通过dependence来协调各个设置Behavior View的行为。具体细节后续有机会再深入一下。