TwinklingRefreshLayout 支持下拉刷新和上拉加载的 RefreshLayout,自带越界回弹效果,支持 RecyclerView,AbsListView,ScrollView等

TwinklingRefreshLayout

简介:支持下拉刷新和上拉加载的 RefreshLayout,自带越界回弹效果,支持 RecyclerView,AbsListView,ScrollView,WebView

English Version

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

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

Demo

下载 Demo

   

使用方法

1.添加 gradle 依赖

将 libray 模块复制到项目中,或者直接在 build.gradle 中依赖:

compile 'com.lcodecorex:tkrefreshlayout:1.0.3'
2.在 xml 中添加 TwinklingRefreshLayout
<?xml version="1.0" encoding="utf-8"?>
<com.lcodecore.library.TwinklingRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/refreshLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:tr_wave_height="180dp"
    app:tr_head_height="100dp">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#fff" />
</com.lcodecore.library.TwinklingRefreshLayout>
3.在 Activity 或者 Fragment 中配置
TwinklingRefreshLayout 不会自动结束刷新或者加载更多,需要手动控制
refreshLayout.setOnRefreshListener(new TwinklingRefreshLayout.OnRefreshListener(){
            @Override
            public void onRefresh(final TwinklingRefreshLayout refreshLayout) {
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        refreshLayout.finishRefreshing();
                    }
                },2000);
            }

            @Override
            public void onLoadMore(final TwinklingRefreshLayout refreshLayout) {
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        refreshLayout.finishLoadmore();
                    }
                },2000);
            }
        });
    }

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

setWaveHeight、setHeaderHeight、setBottomHeight
  • setWaveHeight 设置头部可拉伸的最大高度。
  • setHeaderHeight 头部固定高度(在此高度上显示刷新状态)
  • setBottomHeight 底部高度
setEnableRefresh、setEnableLoadmore

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

setHeaderView(IHeaderView headerView)、setBottomView(IBottomView bottomView)

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

setEnableOverlayRefreshView()

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

setPureScrollModeOn()

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

4.扩展属性
  • tr_wave_height 头部拉伸允许的最大高度
  • tr_head_height 头部高度
  • tr_bottom_height 底部高度
  • tr_overscroll_height 允许越界的最大高度
  • tr_enable_loadmore 是否允许加载更多,默认为 true
  • tr_pureScrollMode_on 是否开启纯净的越界回弹模式
  • tr_show_overlay_refreshview 是否在越界的时候展示刷新控件
5.在 ViewPager+Fragment 中使用的注意事项

ViewPager 默认最大加载 3 个 Fragment,Fragment 被切换到 3 个之外时,Fragment 会调用 onDestroyView()方法,当再次切换回来时,View 会被重新创建。上一版的 Demo 有点小 bug,目前都已修复。

其它说明

1.默认支持越界回弹

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

2.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 高度的比值)。

目前已实现的 Header 有BezierLayout(图一),GoogleDotView(图二),SinaRefreshView(图三);实现的 Footer 有 BottomProgressView(图一),LoadingView(图三),更多动效可以参考AVLoadingIndicatorView库。

其中第一幅图源于BeautifulRefreshForGirl,原来的动效只支持 21 以上版本,且滑动过程中会出现 View 消失的情况,笔者做了一些优化并使其可以正常的在界面中使用。

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

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);
}

其中 getView()方法用于在 TwinklingRefreshLayout 中获取到实际的 Header,因此不能返回 null。

实现像新浪微博那样的刷新效果(有部分修改,具体请看源码),实现代码如下:

1.首先定义 SinaRefreshHeader 继承自 FrameLayout 并实现 IHeaderView 方法

2.getView()方法中返回 this

3.在 onAttachedToWindow()或者构造函数方法中获取一下需要用到的布局

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

        if (rootView == null) {
            rootView = View.inflate(getContext(), R.layout.view_sinaheader, null);
            refreshArrow = (ImageView) rootView.findViewById(R.id.iv_arrow);
            refreshTextView = (TextView) rootView.findViewById(R.id.tv);
            loadingView = (ImageView) rootView.findViewById(R.id.iv_loading);
            addView(rootView);
        }
    }

4.实现其它方法

@Override
    public void onPullingDown(float fraction, float maxHeadHeight, float headHeight) {
        if (fraction < 1f) refreshTextView.setText(pullDownStr);
        if (fraction > 1f) refreshTextView.setText(releaseRefreshStr);
        refreshArrow.setRotation(fraction * headHeight / maxHeadHeight * 180);


    }

    @Override
    public void onPullReleasing(float fraction, float maxHeadHeight, float headHeight) {
        if (fraction < 1f) {
            refreshTextView.setText(pullDownStr);
            refreshArrow.setRotation(fraction * headHeight / maxHeadHeight * 180);
            if (refreshArrow.getVisibility() == GONE) {
                refreshArrow.setVisibility(VISIBLE);
                loadingView.setVisibility(GONE);
            }
        }
    }

    @Override
    public void startAnim(float maxHeadHeight, float headHeight) {
        refreshTextView.setText(refreshingStr);
        refreshArrow.setVisibility(GONE);
        loadingView.setVisibility(VISIBLE);
    }

5.布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal" android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center">
    <ImageView
        android:id="@+id/iv_arrow"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_arrow"/>

    <ImageView
        android:id="@+id/iv_loading"
        android:visibility="gone"
        android:layout_width="34dp"
        android:layout_height="34dp"
        android:src="@drawable/anim_loading_view"/>

    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="16dp"
        android:textSize="16sp"
        android:text="下拉刷新"/>
</LinearLayout>

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

onPullingDown/onPullingUp 表示正在下拉/正在上拉的过程。 onPullReleasing 表示向上拉/下拉释放时回调的状态。 startAnim 则是在 onRefresh/onLoadMore 之后才会回调的过程(此处是显示了加载中的小菊花)

如上所示,轻而易举就可以实现一个个性化的 Header 或者 Footer。(更简单的实现请参考 Demo 中的TextHeaderView(图四))。

剩余问题

  • 制作一个 star 相关的动效。
  • 对回弹效果做一下优化,比如支持使用不同的插值器
  • 带视差效果的 Header
  • SwipeRefreshLayout 风格的刷新 View
  • 允许结束刷新/加载更多前先执行完自定义动画

更新日志

v1.03
  • 扩展了更多的属性
  • 修复 Fragment 回收导致的空指针异常问题
  • 加入 x 方向判断,减小了滑动冲突
  • 优化加载更多列表显示问题
  • 可以灵活的设置是否禁用上下拉
  • 修复 GridView 滑动过程中出现的白条问题
  • Demo 中添加轮播条展示
v1.02
  • 修复加载更多列表控件的显示问题
v1.01
  • 支持了 RecyclerView、ScrollView、AbsListView、WebView
  • 支持越界回弹
  • 支持个性化 Header、Footer
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值