Android AppBarLayout吸顶位置错误问题
我们通常会用AppBarLayout来做一些复杂滚动和吸顶的效果,比如如下代码:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="btn1"
app:layout_scrollFlags="scroll" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="btn2" />
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
...
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>
正常展示的效果是这样的:
会在BTN2处吸顶,然后下面的NestedScrollView再进行滚动。
然而当我们将AppBarLayout里面的子View进行变动,比如在点击BTN1时,将BTN1隐藏掉(GONE),效果却变成了这样:
我们发现吸顶效果消失了,这是因为AppBarLayout计算scrollRange,即吸顶前可滚动的距离的方法,是不考虑View的可见性,而是直接加上View的measureHeight,而GONE掉的View的height是不会变的,只是不绘制而已,所以AppBarLayout实际可滚动的距离,就比我们预料的大了
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();
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());
}
}
结论:
-
如果需要GONE掉某个View,我们可以通过设置其LayoutParams的height或者通过addView/removeView操作实现,这样就不影响AppBarLayout的scrollRange计算
-
其他的View变动,比如addView,removeView等,由于都会自动重新测量而影响height计算,所以没问题