Android——调用SwipeRefreshLayout.se

作者: xpleemoon | 来源:发表于2016-03-03 17:22 被阅读3528次

    引言

    前段时间在网上又看到小伙伴提出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开发团队:


    屏幕快照 2016-03-03 17.11.29.png

    相关文章

      网友评论

        本文标题:Android——调用SwipeRefreshLayout.se

        本文链接:https://www.haomeiwen.com/subject/tmtokttx.html