下拉刷新框架TwinklingRefreshLayout的使用

TwinklingRefreshLayout

TwinklingRefreshLayout介绍

TwinklingRefreshLayout延伸了Google的SwipeRefreshLayout的思想,不在列表控件上动刀,而是使用一个ViewGroup来包含列表控件,以保持其较低的耦合性和较高的通用性。其主要特性有:

  1. 支持RecyclerView、ScrollView、AbsListView系列(ListView、GridView)、WebView以及其它可以获取到scrollY的控件
  2. 支持加载更多
  3. 默认支持 越界回弹,随手势速度有不同的效果
  4. 可开启没有刷新控件的纯净越界回弹模式
  5. setOnRefreshListener中拥有大量可以回调的方法
  6. 将Header和Footer抽象成了接口,并回调了滑动过程中的系数,方便实现个性化的Header和Footer
  7. 支持NestedScroll,嵌套CoordinatorLayout

目前已经支持了所有的View,比如是一个FrameLayout,LinearLayout,AnyView。

image.png

依赖

  implementation 'com.lcodecorex:tkrefreshlayout:1.0.7'

基本使用

在xml中添加TwinklingRefreshLayout




    

    

    

        

            

        


    

Android系统为了跟iOS不一样,当界面OverScroll的时候会显示一个阴影。为了达到更好的显示效果,最好禁用系统的overScroll,如上给RecyclerView添加android:overScrollMode="never"。

在Activity或者Fragment中配置

TwinklingRefreshLayout不会自动结束刷新或者加载更多,需要手动控制

public class MainActivity2 extends AppCompatActivity {

    private ActivityMain2Binding mBinding;

    private List mDatas = new ArrayList<>();

    private RvAdapter mRvAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main2);


        //是否需要下拉刷新,默认需要true
        mBinding.refreshLayout.setEnableRefresh(true);

        //是否需要加载更多,默认需要true
        mBinding.refreshLayout.setEnableLoadmore(true);

        //是否自动加载更多, 滑到底部默认加载更多,默认false
        mBinding.refreshLayout.setAutoLoadMore(true);

        //如果你想进入到界面的时候主动调用下刷新,可以调用startRefresh()/startLoadmore()方法。
        mBinding.refreshLayout.startRefresh();
        // mBinding.refreshLayout.startLoadMore();

        //是否允许进入越界回弹模式,默认true
        mBinding.refreshLayout.setEnableOverScroll(true);

        //是否开启悬浮刷新模式(支持切换到像SwipeRefreshLayout一样的悬浮刷新模式),默认false
        mBinding.refreshLayout.setFloatRefresh(false);


        mRvAdapter = new RvAdapter(this, mDatas);
        mBinding.recyclerView.setAdapter(mRvAdapter);

        loadData();


        //TwinklingRefreshLayout不会自动结束刷新或者加载更多,需要手动控制
        mBinding.refreshLayout.setOnRefreshListener(new RefreshListenerAdapter() {
            @Override
            public void onRefresh(TwinklingRefreshLayout refreshLayout) {
                super.onRefresh(refreshLayout);

                new Handler().postDelayed(() -> {
                    loadData();
                    Toast.makeText(MainActivity2.this, "下拉刷新", Toast.LENGTH_SHORT).show();
                    refreshLayout.finishRefreshing();
                }, 1000);
            }

            @Override
            public void onLoadMore(TwinklingRefreshLayout refreshLayout) {
                super.onLoadMore(refreshLayout);

                new Handler().postDelayed(() -> {
                    loadMore();
                    Toast.makeText(MainActivity2.this, "上拉加载", Toast.LENGTH_SHORT).show();
                    refreshLayout.finishLoadmore();
                }, 1000);
            }
        });
    }

    private void loadData() {
        mDatas.clear();
        for (int i = 1; i <= 30; i++) {
            mDatas.add("赵丽颖" + i);
        }
        mRvAdapter.notifyDataSetChanged();
    }

    private void loadMore() {
        for (int i = 1; i <= 10; i++) {
            mDatas.add("赵丽颖更多" + i);
        }
        mRvAdapter.notifyDataSetChanged();
    }
}

使用finishRefreshing()方法结束刷新,finishLoadmore()方法结束加载更多。此处OnRefreshListener还有其它方法,可以选择需要的来重写。

扩展属性

image.png

动态设置相关属性

如果你想进入到界面的时候主动调用下刷新

如果你想进入到界面的时候主动调用下刷新,可以调用startRefresh()/startLoadmore()方法。

        mBinding.refreshLayout.startRefresh();
        //mBinding.refreshLayout.startLoadMore();

开启纯净的越界回弹模式,也就是所有刷新相关的View都不显示,只显示越界回弹效果

setPureScrollModeOn()

灵活的设置是否禁用上下拉

setEnableRefresh、setEnableLoadmore

是否在底部越界的时候自动切换到加载更多模式

setAutoLoadMore

是否允许越界回弹,默认支持越界回弹

这一点很多类似SwipeRefreshLayout的刷新控件都没有做到(包括SwipeRefreshLayout),因为没有拦截下来的时间会传递给列表控件,而列表控件的滚动状态很难获取。解决方案就是给列表控件设置了OnTouchListener并把事件交给GestureDetector处理,然后在列表控件的OnScrollListener中监听View是否滚动到了顶部(没有OnScrollListener的则采用延时监听策略)。

setEnableOverScroll

支持切换到像SwipeRefreshLayout一样的悬浮刷新模式了

setFloatRefresh(boolean)

设置头部/底部个性化刷新效果,头部需要实现IHeaderView,底部需要实现IBottomView

setHeaderView(IHeaderView headerView)、setBottomView(IBottomView bottomView)

setDefaultHeader、setDefaultFooter
现在已经提供了设置默认的Header、Footer的static方法,可在Application或者一个Activity中这样设置:

TwinklingRefreshLayout.setDefaultHeader(SinaRefreshView.class.getName());
TwinklingRefreshLayout.setDefaultFooter(BallPulseView.class.getName());

添加一个固定在顶部的Header(效果还需要优化)

addFixedExHeader

设置滚动事件的作用对象。

setTargetView(View view)

是否允许在越界的时候显示刷新控件,默认是允许的,也就是Fling越界的时候Header或Footer照常显示,反之就是不显示;可能有特殊的情况,刷新控件会影响显示体验才设立了这个状态。

setOverScrollTopShow、setOverScrollBottomShow、setOverScrollRefreshShow

setWaveHeight、setHeaderHeight、setBottomHeight、setOverScrollHeight

setMaxHeadHeight 设置头部可拉伸的最大高度。
setHeaderHeight 头部固定高度(在此高度上显示刷新状态)
setMaxBottomHeight
setBottomHeight 底部高度
setOverScrollHeight 设置最大的越界高度

setOnRefreshListener大量可以回调的方法

onPullingDown(TwinklingRefreshLayout refreshLayout, float fraction) 正在下拉的过程
onPullingUp(TwinklingRefreshLayout refreshLayout, float fraction) 正在上拉的过程
onPullDownReleasing(TwinklingRefreshLayout refreshLayout, float fraction) 下拉释放过程
onPullUpReleasing(TwinklingRefreshLayout refreshLayout, float fraction) 上拉释放过程
onRefresh(TwinklingRefreshLayout refreshLayout) 正在刷新
onLoadMore(TwinklingRefreshLayout refreshLayout) 正在加载更多
其中fraction表示当前下拉的距离与Header高度的比值(或者当前上拉距离与Footer高度的比值)。

纯净的越界回弹模式

开启纯净的越界回弹模式,也就是所有刷新相关的View都不显示,只显示越界回弹效果。

mBinding.refreshLayout.setPureScrollModeOn();



    

    


    

        

            

                


                


                


                


            
        

    



public class MainActivity6 extends AppCompatActivity {
    private ActivityMain6Binding mBinding;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main6);

        mBinding.refreshLayout.setPureScrollModeOn();
    }
}

使用库中已有的header和footer

现在已经提供了设置默认的Header、Footer的static方法,可在Application或者一个Activity中这样设置:

//设置全局  可在Application或者一个Activity中这样设置
TwinklingRefreshLayout.setDefaultHeader(SinaRefreshView.class.getName());
TwinklingRefreshLayout.setDefaultFooter(BallPulseView.class.getName());
//使用库中已有的header和footer
mBinding.refreshLayout.setHeaderView(new SinaRefreshView(this));
mBinding.refreshLayout.setBottomView(new BallPulseView(this));
//仿android原生系统下拉刷新 SwipeRefreshLayout
ProgressLayout headerView = new ProgressLayout(this);
mBinding.refreshLayout.setHeaderView(headerView);

自定义Header

相关接口分别为IHeaderView,代码如下:

public interface IHeaderView {
    View getView();

    void onPullingDown(float fraction,float maxHeadHeight,float headHeight);

    void onPullReleasing(float fraction,float maxHeadHeight,float headHeight);

    void startAnim(float maxHeadHeight,float headHeight);

    void reset();
}

CustomHeader

继承自FrameLayout并实现IHeaderView方法。

public class CustomHeader extends FrameLayout implements IHeaderView {

    //圆形进度条
    private int rotationSrc = R.drawable.ypc_footer_progress_small;

    //箭头
    private int arrowSrc = R.mipmap.arrow;

    private boolean mIsBeingDragged = false;

    private long freshTime;

    private final int ROTATE_ANIM_DURATION = 180;
    //private RotateAnimation mRotateUpAnim;
    //private RotateAnimation mRotateDownAnim;

    private TextView headerTitle;
    private TextView headerTime;
    private ImageView headerArrow;
    private ProgressBar headerProgressbar;

    public CustomHeader(@NonNull Context context) {
        this(context, null);
    }

    public CustomHeader(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CustomHeader(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();

       /* mRotateUpAnim = new RotateAnimation(0.0f, -180.0f,
                Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
                0.5f);
        mRotateUpAnim.setDuration(ROTATE_ANIM_DURATION);
        mRotateUpAnim.setFillAfter(true);
        mRotateDownAnim = new RotateAnimation(-180.0f, 0.0f,
                Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
                0.5f);
        mRotateDownAnim.setDuration(ROTATE_ANIM_DURATION);
        mRotateDownAnim.setFillAfter(true);*/
    }

    private void init() {
        View view = View.inflate(getContext(), R.layout.pull_down_refresh_header, null);
        headerTitle = view.findViewById(R.id.default_header_title);
        headerTime = view.findViewById(R.id.default_header_time);
        headerArrow = view.findViewById(R.id.default_header_arrow);
        headerProgressbar = view.findViewById(R.id.default_header_progressbar);
        headerProgressbar.setIndeterminateDrawable(ContextCompat.getDrawable(getContext(), rotationSrc));
        headerArrow.setImageResource(arrowSrc);
        addView(view);
    }


    /**
     * 用于在TwinklingRefreshLayout中获取到实际的Header,因此不能返回null。
     *
     * @return
     */
    @Override
    public View getView() {
        return this;
    }

    /**
     * 正在下拉的过程
     *
     * @param fraction      其中fraction表示当前下拉的距离与Header高度的比值(或者当前上拉距离与Footer高度的比值)。
     * @param maxHeadHeight
     * @param headHeight
     */
    @Override
    public void onPullingDown(float fraction, float maxHeadHeight, float headHeight) {
        if (!mIsBeingDragged) {
            mIsBeingDragged = true;
            if (freshTime == 0) {
                freshTime = System.currentTimeMillis();
            } else {
                int m = (int) ((System.currentTimeMillis() - freshTime) / 1000 / 60);
                if (m >= 1 && m < 60) {
                    headerTime.setText(String.format("%s分钟前", m));
                } else if (m > 60 * 24) {
                    int d = m / (60 * 24);
                    headerTime.setText(String.format("%s天前", d));
                } else if (m >= 60) {
                    int h = m / 60;
                    headerTime.setText(String.format("%s小时前", h));
                } else if (m == 0) {
                    headerTime.setText("刚刚");
                }
            }
        }
        if (fraction > 1f) {
            headerTitle.setText("松开刷新");
        } else {
            headerTitle.setText("下拉刷新");
        }

        //表示当前头部滑动的距离,然后算出它和最大高度的比例,然后乘以180,可以使得在滑动到最大距离时Arrow恰好能旋转180度。
        headerArrow.setRotation(fraction * headHeight / maxHeadHeight * 180);
    }

    /**
     * 上拉/下拉释放时回调的状态
     *
     * @param fraction
     * @param maxHeadHeight
     * @param headHeight
     */
    @Override
    public void onPullReleasing(float fraction, float maxHeadHeight, float headHeight) {
        mIsBeingDragged = false;
    }

    /**
     * 在onRefresh/onLoadMore之后才会回调的过程(此处是显示了加载中的小菊花)
     *
     * @param maxHeadHeight
     * @param headHeight
     */
    @Override
    public void startAnim(float maxHeadHeight, float headHeight) {
        freshTime = System.currentTimeMillis();
        headerTitle.setText("正在刷新");
        headerArrow.setVisibility(View.INVISIBLE);
        headerArrow.clearAnimation();
        headerProgressbar.setVisibility(View.VISIBLE);
    }

    @Override
    public void onFinish(OnAnimEndListener animEndListener) {
        animEndListener.onAnimEnd();
        headerTitle.setVisibility(VISIBLE);
        headerArrow.setVisibility(View.VISIBLE);
        headerProgressbar.setVisibility(View.INVISIBLE);
    }

    @Override
    public void reset() {
        headerTitle.setVisibility(VISIBLE);
        headerArrow.setVisibility(View.VISIBLE);
        headerProgressbar.setVisibility(View.INVISIBLE);
    }
}

pull_down_refresh_header.xml



    

        

            

            

                

                
            
        

        

        
    


设置头部/底部个性化刷新效果,头部需要实现IHeaderView,底部需要实现IBottomView

setHeaderView(IHeaderView headerView)、setBottomView(IBottomView bottomView)

  mBinding.refreshLayout.setHeaderView(new CustomHeader(this));

gif图片作为header

public class GifHeader extends FrameLayout implements IHeaderView {

    private GifDrawable mGifDrawable;

    private ViewGroup.LayoutParams mLp;

    private int mLoadingW = 0, mLoadingH = 0;

    private GifImageView mGifImageView;

    private Context mContext;

    public GifHeader(@NonNull Context context) {
        this(context, null);
    }

    public GifHeader(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public GifHeader(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext = context;
        init();
    }

    private void init() {
        View rootView = View.inflate(getContext(), R.layout.ypc_home_refresh_header3, null);
        mGifImageView = rootView.findViewById(R.id.gif_second);
        mGifDrawable = (GifDrawable) mGifImageView.getDrawable();
        mLoadingW = DensityUtils.dip2px(238f);
        mLoadingH = DensityUtils.dip2px(120f);
        mLp = mGifImageView.getLayoutParams();

        addView(rootView);
    }


    /**
     * 用于在TwinklingRefreshLayout中获取到实际的Header,因此不能返回null。
     *
     * @return
     */
    @Override
    public View getView() {
        return this;
    }

    /**
     * 正在下拉的过程
     *
     * @param fraction      其中fraction表示当前下拉的距离与Header高度的比值(或者当前上拉距离与Footer高度的比值)。
     * @param maxHeadHeight
     * @param headHeight
     */
    @Override
    public void onPullingDown(float fraction, float maxHeadHeight, float headHeight) {
        //下拉的时候不执行动画
        // mGifDrawable.reset();

        if (fraction > 1f) {
            changeLoadingWH(1);
        } else if (fraction <= 1f) {
            changeLoadingWH(fraction);
        }

    }

    /**
     * 上拉/下拉释放时回调的状态
     *
     * @param fraction
     * @param maxHeadHeight
     * @param headHeight
     */
    @Override
    public void onPullReleasing(float fraction, float maxHeadHeight, float headHeight) {

    }

    /**
     * 在onRefresh/onLoadMore之后才会回调的过程(此处是显示了加载中的小菊花)
     *
     * @param maxHeadHeight
     * @param headHeight
     */
    @Override
    public void startAnim(float maxHeadHeight, float headHeight) {
        mGifDrawable.start();
    }

    @Override
    public void onFinish(OnAnimEndListener animEndListener) {
        animEndListener.onAnimEnd();
        mGifDrawable.stop();
    }

    @Override
    public void reset() {
        mGifDrawable.reset();
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
    }

    private void changeLoadingWH(float fraction) {
        mLp.width = (int) (mLoadingW * fraction);
        mLp.height = (int) (mLoadingH * fraction);
        mGifImageView.setLayoutParams(mLp);
    }
}



    


自定义Footer

相关接口分别为IBottomView,代码如下:

CustomFooter

public class CustomFooter  extends FrameLayout implements IBottomView {

    private int rotationSrc = R.drawable.ypc_footer_progress_small;
    private TextView footerTitle;
    private ProgressBar footerProgressbar;
    private boolean isLoading;

    public CustomFooter(Context context) {
        this(context,null,0);
    }

    public CustomFooter(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        View view = View.inflate(getContext(),R.layout.common_footer,  null);
        footerTitle = view.findViewById(R.id.default_footer_title);
        footerProgressbar = view.findViewById(R.id.default_footer_progressbar);
        footerProgressbar.setIndeterminateDrawable(ContextCompat.getDrawable(getContext(), rotationSrc));
        addView(view);
    }


    @Override
    public View getView() {
        return this;
    }

    @Override
    public void onPullingUp(float fraction, float maxBottomHeight, float bottomHeight) {
        if(Math.abs(fraction) < 1f){
            footerTitle.setText("查看更多");
        }else{
            footerTitle.setText("松开载入更多");
        }
        if(footerTitle.getVisibility() != VISIBLE){
            footerTitle.setVisibility(VISIBLE);
            footerProgressbar.setVisibility(View.INVISIBLE);
        }
    }

    @Override
    public void startAnim(float maxBottomHeight, float bottomHeight) {
        footerTitle.setVisibility(View.INVISIBLE);
        footerProgressbar.setVisibility(View.VISIBLE);
        isLoading = true;
    }

    @Override
    public void onPullReleasing(float fraction, float maxBottomHeight, float bottomHeight) {

    }

    @Override
    public void onFinish() {
        footerTitle.setText("查看更多");
        footerTitle.setVisibility(View.VISIBLE);
        footerProgressbar.setVisibility(View.INVISIBLE);
        isLoading =false;
    }

    @Override
    public void reset() {
        footerTitle.setText("查看更多");
        footerTitle.setVisibility(View.VISIBLE);
        footerProgressbar.setVisibility(View.INVISIBLE);
        isLoading =false;
    }
}



    

    


  mBinding.refreshLayout.setBottomView(new CustomFooter(this));

封装Header和Footer到基类

public abstract class BaseActivity extends AppCompatActivity {

    public T mBinding;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mBinding= DataBindingUtil.setContentView(this,getLayoutId());

        initData();
    }

    public abstract int getLayoutId();

    public void initData() {

    }
}
public abstract class RefreshActivity extends BaseActivity {

    private IHeaderView mHeaderView;

    @Override
    public void initData() {
        super.initData();

        getRefreshView().setEnableRefresh(true);
        getRefreshView().setEnableLoadmore(false);
        mHeaderView = getHeaderView();
        getRefreshView().setHeaderView(mHeaderView);
    }

    /***
     * 获取头部布局,子类可重写该方法设置头布局
     */
    protected IHeaderView getHeaderView() {
        if (mHeaderView == null) {
            return new CustomHeader(this);
        }
        return mHeaderView;
    }


    protected abstract TwinklingRefreshLayout getRefreshView();

}
public abstract class LoadMoreActivity extends RefreshActivity {

    private IBottomView mBottomView;

    @Override
    public void initData() {
        super.initData();
        getRefreshView().setEnableLoadmore(true);
        mBottomView = getBottomView();
        getRefreshView().setBottomView(mBottomView);
    }

    protected IBottomView getBottomView() {
        if (mBottomView == null) {
            return new CustomFooter(this);
        }
        return mBottomView;
    }
}
public class MainActivity extends LoadMoreActivity {

    private List mDatas = new ArrayList<>();

    private RvAdapter mRvAdapter;

    @Override
    public int getLayoutId() {
        return R.layout.activity_main;
    }

    @Override
    protected TwinklingRefreshLayout getRefreshView() {
        return mBinding.refreshLayout;
    }

    /**
     * 重写该方法设置和基类不同的头布局
     *
     * @return
     */
    @Override
    protected IHeaderView getHeaderView() {
        return super.getHeaderView();
        //return new CustomHeader2(this);
    }

    /**
     * 重写该方法设置和基类不同的底布局
     *
     * @return
     */
    @Override
    protected IBottomView getBottomView() {
        return super.getBottomView();
        //return new CustomFooter2(this);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);


        mRvAdapter = new RvAdapter(mDatas);
        mBinding.recyclerView.setAdapter(mRvAdapter);

        loadData();

        //TwinklingRefreshLayout不会自动结束刷新或者加载更多,需要手动控制
        mBinding.refreshLayout.setOnRefreshListener(new RefreshListenerAdapter() {
            @Override
            public void onRefresh(TwinklingRefreshLayout refreshLayout) {
                super.onRefresh(refreshLayout);

                new Handler().postDelayed(() -> {
                    loadData();
                    Toast.makeText(MainActivity.this, "下拉刷新", Toast.LENGTH_SHORT).show();
                    refreshLayout.finishRefreshing();
                }, 2000);
            }

            @Override
            public void onLoadMore(TwinklingRefreshLayout refreshLayout) {
                super.onLoadMore(refreshLayout);

                new Handler().postDelayed(() -> {
                    loadMore();
                    Toast.makeText(MainActivity.this, "上拉加载", Toast.LENGTH_SHORT).show();
                    refreshLayout.finishLoadmore();
                }, 2000);
            }
        });
    }


    private void loadData() {
        mDatas.clear();
        for (int i = 1; i <= 30; i++) {
            mDatas.add("赵丽颖" + i);
        }
        mRvAdapter.notifyDataSetChanged();
    }

    private void loadMore() {
        for (int i = 1; i <= 10; i++) {
            mDatas.add("赵丽颖更多" + i);
        }
        mRvAdapter.notifyDataSetChanged();
    }
}

TwinklingRefreshLayout嵌套CoordinatorLayout




    

        

            

        

        

    

让refreshLayout能够找到RecyclerView/ListView

refreshLayout.setTargetView(rv);

设置AppBarLayout的移动监听器,需要下拉显示AppBarLayout时需设置setEnableRefresh(false),setEnableOverScroll(false);AppBarLayout隐藏后还原为原来设置的值即可:

AppBarLayout appBarLayout = (AppBarLayout) findViewById(R.id.appbar_layout);
appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
    @Override
    public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
        if (verticalOffset >= 0) {
            refreshLayout.setEnableRefresh(true);
            refreshLayout.setEnableOverScroll(false);
        } else {
            refreshLayout.setEnableRefresh(false);
            refreshLayout.setEnableOverScroll(false);
        }
    }
});

CoordinatorLayout嵌套TwinklingRefreshLayout




    

        

    


注意给TwinklingRefreshLayout设置一个layout_behavior="@string/appbar_scrolling_view_behavior"。

你可能感兴趣的:(下拉刷新框架TwinklingRefreshLayout的使用)