English Version
TwinklingRefreshLayout 延伸了 Google 的 SwipeRefreshLayout 的思想,不在列表控件上动刀,而是使用一个 ViewGroup 来包含列表控件,以保持其较低的耦合性和较高的通用性。其主要特性有:
下载 Demo
将 libray 模块复制到项目中,或者直接在 build.gradle 中依赖:
compile 'com.lcodecorex:tkrefreshlayout:1.0.3'
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 还有其它方法,可以选择需要的来重写。
灵活的设置是否禁用上下拉。
设置头部/底部个性化刷新效果,头部需要实现 IHeaderView,底部需要实现 IBottomView。
是否允许在越界的时候显示刷新控件,默认是允许的,也就是 Fling 越界的时候 Header 或 Footer 照常显示,反之就是不显示;可能有特殊的情况,刷新控件会影响显示体验才设立了这个状态。
开启纯净的越界回弹模式,也就是所有刷新相关的 View 都不显示,只显示越界回弹效果
ViewPager 默认最大加载 3 个 Fragment,Fragment 被切换到 3 个之外时,Fragment 会调用 onDestroyView()方法,当再次切换回来时,View 会被重新创建。上一版的 Demo 有点小 bug,目前都已修复。
这一点很多类似 SwipeRefreshLayout 的刷新控件都没有做到(包括 SwipeRefreshLayout),因为没有拦截下来的时间会传递给列表控件,而列表控件的滚动状态很难获取。解决方案就是给列表控件设置了 OnTouchListener 并把事件交给 GestureDetector 处理,然后在列表控件的 OnScrollListener 中监听 View 是否滚动到了顶部(没有 OnScrollListener 的则采用延时监听策略)。
其中 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.布局文件
注意 fraction 的使用,比如上面的代码refreshArrow.setRotation(fraction * headHeight / maxHeadHeight * 180)
,fraction * headHeight
表示当前头部滑动的距离,然后算出它和最大高度的比例,然后乘以 180,可以使得在滑动到最大距离时 Arrow 恰好能旋转 180 度。
onPullingDown/onPullingUp 表示正在下拉/正在上拉的过程。 onPullReleasing 表示向上拉/下拉释放时回调的状态。 startAnim 则是在 onRefresh/onLoadMore 之后才会回调的过程(此处是显示了加载中的小菊花)
如上所示,轻而易举就可以实现一个个性化的 Header 或者 Footer。(更简单的实现请参考 Demo 中的TextHeaderView(图四))。