热门搜索 :
考研考公
您的当前位置:首页正文

SwipeRefreshLayout手动显示刷新动画

来源:东饰资讯网

简述

比如说要服务器取新闻数据,界面将会一个列表形式,支持下拉刷新功能。通常做法都会在进入界面时,把下拉刷新的动画显示出来,数据获取成功后隐藏刷新动画,这样的做法很普遍。然后在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方法来偏移下拉动画的位置,我们打个断点瞧瞧,额,setRefreshingmCurrentTargetOffsetTop竟然是0

非UI线程列队

但继续断点到setTargetOffsetTopAndBottom中,mCurrentTargetOffsetTop值为192了,既然有值为什么手动调用显示不了动画呢?继续跟踪!发现在onMeasure中值为负数了

Paste_Image.png

来看看onMeasure方法,mCurrentTargetOffsetTop为-140,我去这值肯定是跑到手机屏幕外面显示了,哈哈~都跟到这里了,再跟踪一下吧onLayout

Paste_Image.png

onLayout还调用了动画圆圈控件mCircleView.layout

Paste_Image.png

经过刚才的断点跟踪,源码调用顺序如下:
setRefreshing(boolean refreshing) -> setTargetOffsetTopAndBottom - >onMeasure -> onLayout -> onMeasure -> onLayout
这样看来知道原因了吧,在setTargetOffsetTopAndBottommCurrentTargetOffsetTop是有值的,可后面又执行了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分支中,源码如下

Paste_Image.png

实现方式二

正如onMeasure中那段给mCurrentTargetOffsetTop赋值的代码块,如果能让mUsingCustomStart为true的话,那么也能不进入if分支,那么我们就找找mUsingCustomStart在何时有赋值为true的方法,功夫不负有心人,在setProgressViewOffset方法中有,并且看方法名字的意思可以知道,设置进度视图偏移,mSpinnerFinalOffset = end应该是偏移结束位置。

Paste_Image.png

尝试用以下代码试试,果然这样也是成功的

    @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
Top