简述
比如说要服务器取新闻数据,界面将会一个列表形式,支持下拉刷新功能。通常做法都会在进入界面时,把下拉刷新的动画显示出来,数据获取成功后隐藏刷新动画,这样的做法很普遍。然后在Google在supperV4包也添加一个支持下拉刷新控件SwipeRefreshLayout后, 只能说小小惊喜,好不好用自己体会咯。那么今天讲手动调用显示刷新动画。
分析源码
下拉刷新数据完成后调用setRefreshing(false)停止刷新动画,那么是不是参数为true就是显示动画呢?可恶的SwipeRefreshLayout在onCreate()中调用setRefreshing(true)不显示刷新动画,拉门奇怪看看源码吧
public void setRefreshing(boolean refreshing) {
if (refreshing && mRefreshing != refreshing) {
// scale and show
mRefreshing = refreshing;
int endTarget = 0;
if (!mUsingCustomStart) {
endTarget = (int) (mSpinnerFinalOffset + mOriginalOffsetTop);
} else {
endTarget = (int) mSpinnerFinalOffset;
}
setTargetOffsetTopAndBottom(endTarget - mCurrentTargetOffsetTop,
true /* requires update */);
mNotify = false;
startScaleUpAnimation(mRefreshListener);
} else {
setRefreshing(refreshing, false /* notify */);
}
}
可以看到上面源码中调用了setTargetOffsetTopAndBottom
方法来偏移下拉动画的位置,我们打个断点瞧瞧,额,setRefreshing
时mCurrentTargetOffsetTop
竟然是0
但继续断点到setTargetOffsetTopAndBottom
中,mCurrentTargetOffsetTop
值为192了,既然有值为什么手动调用显示不了动画呢?继续跟踪!发现在onMeasure
中值为负数了
来看看onMeasure
方法,mCurrentTargetOffsetTop为-140,我去这值肯定是跑到手机屏幕外面显示了,哈哈~都跟到这里了,再跟踪一下吧onLayout
!
onLayout
还调用了动画圆圈控件mCircleView.layout
经过刚才的断点跟踪,源码调用顺序如下:
setRefreshing(boolean refreshing) -> setTargetOffsetTopAndBottom - >onMeasure -> onLayout -> onMeasure -> onLayout
这样看来知道原因了吧,在setTargetOffsetTopAndBottom
中mCurrentTargetOffsetTop
是有值的,可后面又执行了onMeasure
导致mCurrentTargetOffsetTop
为负数
实现方式一
那我们是不是可以这样想,等onLayout
绘制个控件的位置完成后,最后执行我们调用的setRefreshing(true)
呢?这有点思路了,可以用post(Runnable action)
的方法把setRefreshing(true)
加入到UI线程列队中,所以用下面代码来试试,惊喜动画有显示了。成功!
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final SwipeRefreshLayout mSwipeRefreshLayout =
(SwipeRefreshLayout) findViewById(R.id.swipeRefreshLayout);
// mSwipeRefreshLayout.setRefreshing(true);
mSwipeRefreshLayout.post(new Runnable() {
@Override
public void run() {
mSwipeRefreshLayout.setRefreshing(true);
}
});
}
再看断点后执行顺序
onMeasure -> onLayout -> setRefreshing(boolean refreshing) -> setTargetOffsetTopAndBottom -> onMeasure -> onLayout
那么问题来了,后面不还是执行了onMeasure
吗?是,是执行了,但关键代码在mOriginalOffsetCalculated
这个变量,第一次执行onMeasure
后,此变量已经为true,所以第二onMeasure
不会进入if
分支中,源码如下
实现方式二
正如onMeasure
中那段给mCurrentTargetOffsetTop
赋值的代码块,如果能让mUsingCustomStart
为true的话,那么也能不进入if
分支,那么我们就找找mUsingCustomStart
在何时有赋值为true的方法,功夫不负有心人,在setProgressViewOffset
方法中有,并且看方法名字的意思可以知道,设置进度视图偏移,mSpinnerFinalOffset = end
应该是偏移结束位置。
尝试用以下代码试试,果然这样也是成功的
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final SwipeRefreshLayout mSwipeRefreshLayout =
(SwipeRefreshLayout) findViewById(R.id.swipeRefreshLayout);
mSwipeRefreshLayout.setProgressViewOffset(false, 0, 52);
mSwipeRefreshLayout.setRefreshing(true);
// mSwipeRefreshLayout.post(new Runnable() {
// @Override
// public void run() {
// mSwipeRefreshLayout.setRefreshing(true);
// }
// });
}
总结
二种方式手动显示下拉动画
- UI线程列队
mSwipeRefreshLayout.post(new Runnable() {
@Override
public void run() {
mSwipeRefreshLayout.setRefreshing(true);
}
});
- 偏移
mSwipeRefreshLayout.setProgressViewOffset(false, 0, 100);
mSwipeRefreshLayout.setRefreshing(true);
我建了个微信公众号,我们将每天为您推送优质文章、开源库及学习心得,欢迎关注。
微信公众号.png