引言
前段时间在网上又看到小伙伴提出SwipeRefreshLayout的指示器不能显示的问题,该问题的出现情景是在Activity或者Fragmen的onCreate方法中直接调用SwipeRefreshLayout.setRefreshing(true)。
其实这个问题我在14年底的时候就遇到过了,后来在android issues上找到解决方案。本来不想写,后来想想时间过去两年了,Google还没对这个bug进行fix,那倒不如根据那个android issues做一个整理帮助更多未找到原因和解决方案的小伙伴们。
原因
在SwipeRefreshLayout.onMeasure()之前,调用SwipeRefreshLayout.setRefreshing(true)。
解决方案
- 很简单,那就是在SwipeRefreshLayout.onMeasure()之后,调用SwipeRefreshLayout.setRefreshing(true)。
- 我把解决方案按照调用方式简单的分为两类:主调和回调。
主调
Handler延迟时间执行,这个方案不好,因为延迟时间很难有一个准确数据,确保在任何机器上都是可行和完美的。
handler.postDelayed(new Runnable() {
@Override
public void run() {
initiateRefresh();
}
}, 1000); // 时间可以按自己需要设,不一定是1秒
感谢无奈的冻鱼提供的意见,使用Handler.post(runnable)。 那么为什么Handler.post(runnable)比较好呢?
原因非常简单,因为activity的onCreate()生命周期方法在调用setContentVIew()后,UI消息队列会包含绘制view的消息(view的绘制流程——measure,layout,draw),而UI消息队列又是按顺序执行的,所以这时候handler.post发出的消息会后执行,因此就相当于在view绘制完成后执行
通过设置指示器的偏移间接地调用onMeasure():
TypedValue typed_value = new TypedValue();
getTheme().resolveAttribute(android.support.v7.appcompat.R.attr.actionBarSize, typed_value, true);
swipeRefreshLayout.setProgressViewOffset(false, 0, getResources().getDimensionPixelSize(typed_value.resourceId));
SwipeRefreshLayout.setProgressViewOffset()源码:
public void setProgressViewOffset(boolean scale, int start, int end) {
mScale = scale;
mCircleView.setVisibility(View.GONE);
mOriginalOffsetTop = mCurrentTargetOffsetTop = start;
mSpinnerFinalOffset = end;
mUsingCustomStart = true;
mCircleView.invalidate();// onMeasure()就是由此触发
}
回调
重写SwipeRefreshLayout的onMeasure()和setRefreshing():
private boolean mMeasured = false;
private boolean mPreMeasureRefreshing = false;
@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (!mMeasured) {
mMeasured = true;
setRefreshing(mPreMeasureRefreshing);
}
}
@Override
public void setRefreshing(boolean refreshing) {
if (mMeasured) {
super.setRefreshing(refreshing);
} else {
mPreMeasureRefreshing = refreshing;
}
}
监听OnGlobalLayoutListener:
private class OnLayoutReadyListener implements OnGlobalLayoutListener {
@SuppressWarnings("deprecation")
@Override
public void onGlobalLayout() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
getViewTreeObserver().removeOnGlobalLayoutListener(this);
} else {
getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
if (!mLayoutReady) {
mLayoutReady = true;
setRefreshing(mPreLayoutReadyRefreshing);
}
}
}
好消息
经过两年的时间后,该issues终于反馈到Android开发团队: