一般情况下,我们都是把 SwipeRefreshLayout 当做需要有刷新功能的父布局使用,类似这样:
<android.support.v4.widget.SwipeRefreshLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--content-->
</android.support.v4.widget.NestedScrollView>
</android.support.v4.widget.SwipeRefreshLayout>
在这种情况下, SwipeRefreshLayout 会自动找到子布局,进行刷新判断等操作,一切正常。但是,最近一直都在使用 ConstraintLayout,并且是否能触发下拉刷新需要我设置 OnChildScrollUpCallback 自己进行判断。于是乎布局变成了这样:
<android.support.constraint.ConstraintLayout 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.v4.widget.SwipeRefreshLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="@+id/scroll_view"
app:layout_constraintLeft_toLeftOf="@+id/scroll_view"
app:layout_constraintRight_toRightOf="@+id/scroll_view"
app:layout_constraintTop_toTopOf="@+id/scroll_view"/>
<android.support.v4.widget.NestedScrollView
android:id="@+id/scroll_view"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent">
<!--content-->
</android.support.v4.widget.NestedScrollView>
</android.support.constraint.ConstraintLayout>
SwipeRefreshLayout 里面没有了子布局,这样问题就出现了,会发现无论如何设置都不能触发下拉刷新动作了。以前有子布局的时候是正常的,现在没了,难道是这里出了问题?能想到的就是在 SwipeRefreshLayout 里面搜索 getChildAt 方法在什么地方有调用,调用来干嘛来。
首先找到的是在 ensureTarget() 中有调用,源码如下:
private void ensureTarget() {
// Don't bother getting the parent height if the parent hasn't been laid
// out yet.
if (mTarget == null) {
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (!child.equals(mCircleView)) {
mTarget = child;
break;
}
}
}
}
这段代码就是用来确定刷新的子布局,由于我们并没有设置子布局,所以 mTarget 在运行完之后应该还是 null。
下一段代码是在 onMeasure 中:
@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (mTarget == null) {
ensureTarget();
}
if (mTarget == null) {
return;
}
mTarget.measure(MeasureSpec.makeMeasureSpec(
getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(
getMeasuredHeight() - getPaddingTop() - getPaddingBottom(), MeasureSpec.EXACTLY));
mCircleView.measure(MeasureSpec.makeMeasureSpec(mCircleDiameter, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(mCircleDiameter, MeasureSpec.EXACTLY));
mCircleViewIndex = -1;
// Get the index of the circleview.
for (int index = 0; index < getChildCount(); index++) {
if (getChildAt(index) == mCircleView) {
mCircleViewIndex = index;
break;
}
}
}
这段代码使用 getChildAt 是为了确定 mCircleView 的位置,也就是刷新时候的那个圆环。乍看无关,仔细一看,就发现了问题所在。在针对 SwipeRefreshLayout 本身做完测量之后,先是确定了 mTarget,要是 mTarget 为 null 的话,就直接返回了。要是不为空,才会对 mCircleView 进行测量操作。也就是说在没有子布局的情况下,mCircleView 是没有进行测量的操作的,所以 mCircleView 的 getMeasuredHeight() 和 getMeasuredWidth() 都将返回0,这就导致了刷新小圆圈显示不出来。
所以在使用 SwipeRefreshLayout 的时候一定要记得给它添加一个子布局。