RecyclerView的下拉刷新需要自己封装,其实是RecyclerView添加头部之后的处理逻辑的编辑。所以我们继承自添加头部和底部的RecyclerView开始封装。
一、添加头部
处理下拉刷新,同时考虑刷新列表的不同风格样式,确保每个项目都能用, 所以我们不能直接添加View,需要利用辅助类。
public void addRefreshViewCreator(RefreshViewCreator refreshCreator) {
this.mRefreshCreator = refreshCreator;
addRefreshView();
}
其中辅助类提供给外界的方法
public abstract class RefreshViewCreator {
/**
* 获取下拉刷新的View
*
* @param context 上下文
* @param parent RecyclerView
*/
public abstract View getRefreshView(Context context, ViewGroup parent);
/**
* 正在下拉
* @param currentDragHeight 当前拖动的高度
* @param refreshViewHeight 总的刷新高度
* @param currentRefreshStatus 当前状态
*/
public abstract void onPull(int currentDragHeight, int refreshViewHeight, int currentRefreshStatus);
/**
* 正在刷新中
*/
public abstract void onRefreshing();
/**
* 停止刷新
*/
public abstract void onStopRefresh();
}
添加头部的方法:
/**
* 添加头部的刷新View
*/
private void addRefreshView() {
RecyclerView.Adapter adapter = getAdapter();
if (adapter != null && mRefreshCreator != null) {
// 添加头部的刷新View
View refreshView = mRefreshCreator.getRefreshView(getContext(), this);
if (refreshView != null) {
addHeaderView(refreshView);
this.mRefreshView = refreshView;
}
}
}
二、重写dispatchTouchEvent()方法
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
// 记录手指按下的位置 ,之所以写在dispatchTouchEvent那是因为如果我们处理了条目点击事件,
// 那么就不会进入onTouchEvent里面,所以只能在这里获取
mFingerDownY = (int) ev.getRawY();
break;
case MotionEvent.ACTION_UP:
if (mCurrentDrag) {
restoreRefreshView();
}
break;
}
return super.dispatchTouchEvent(ev);
}
restoreRefreshView()方法
/**
* 重置当前刷新状态状态
*/
private void restoreRefreshView() {
int currentTopMargin = ((MarginLayoutParams) mRefreshView.getLayoutParams()).topMargin;
int finalTopMargin = -mRefreshViewHeight + 1;
if (mCurrentRefreshStatus == REFRESH_STATUS_LOOSEN_REFRESHING) {
finalTopMargin = 0;
mCurrentRefreshStatus = REFRESH_STATUS_REFRESHING;
if (mRefreshCreator != null) {
mRefreshCreator.onRefreshing();
}
if (mListener != null) {
mListener.onRefresh();
}
}
int distance = currentTopMargin - finalTopMargin;
// 回弹到指定位置
ValueAnimator animator = ObjectAnimator.ofFloat(currentTopMargin, finalTopMargin).setDuration(distance);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float currentTopMargin = (float) animation.getAnimatedValue();
setRefreshViewMarginTop((int) currentTopMargin);
}
});
animator.start();
mCurrentDrag = false;
}
三、重写onTouchEvent()方法
@Override
public boolean onTouchEvent(MotionEvent e) {
switch (e.getAction()) {
case MotionEvent.ACTION_MOVE:
// 如果是在最顶部才处理,否则不需要处理
if (canScrollUp() || mCurrentRefreshStatus == REFRESH_STATUS_REFRESHING
|| mRefreshView == null || mRefreshCreator == null) {
// 如果没有到达最顶端,也就是说还可以向上滚动就什么都不处理
return super.onTouchEvent(e);
}
// 解决下拉刷新自动滚动问题
if (mCurrentDrag) {
scrollToPosition(0);
}
// 获取手指触摸拖拽的距离
int distanceY = (int) ((e.getRawY() - mFingerDownY) * mDragIndex);
// 如果是已经到达头部,并且不断的向下拉,那么不断的改变refreshView的marginTop的值
if (distanceY > 0) {
int marginTop = distanceY - mRefreshViewHeight;
setRefreshViewMarginTop(marginTop);
updateRefreshStatus(distanceY);
mCurrentDrag = true;
return false;
}
break;
}
return super.onTouchEvent(e);
}
/**
* 设置刷新View的marginTop
*/
private void setRefreshViewMarginTop(int marginTop) {
MarginLayoutParams params = (MarginLayoutParams) mRefreshView.getLayoutParams();
if (marginTop < -mRefreshViewHeight + 1) {
marginTop = -mRefreshViewHeight + 1;
}
params.topMargin = marginTop;
mRefreshView.setLayoutParams(params);
}
/**
* 更新刷新的状态
*/
private void updateRefreshStatus(int distanceY) {
if (distanceY <= 0) {
mCurrentRefreshStatus = REFRESH_STATUS_NORMAL;
} else if (distanceY < mRefreshViewHeight) {
mCurrentRefreshStatus = REFRESH_STATUS_PULL_DOWN_REFRESH;
} else {
mCurrentRefreshStatus = REFRESH_STATUS_LOOSEN_REFRESHING;
}
if (mRefreshCreator != null) {
mRefreshCreator.onPull(distanceY, mRefreshViewHeight, mCurrentRefreshStatus);
}
}
四、重写onLayout方法
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
//if (changed) {
if (mRefreshView != null && mRefreshViewHeight <= 0) {
// 获取头部刷新View的高度
mRefreshViewHeight = mRefreshView.getMeasuredHeight();
if (mRefreshViewHeight > 0) {
// 隐藏头部刷新的View marginTop 多留出1px防止无法判断是不是滚动到头部问题
setRefreshViewMarginTop(-mRefreshViewHeight + 1);
}
}
// }
}
五、处理刷新回调
/**
* @return Whether it is possible for the child view of this layout to
* scroll up. Override this if the child view is a custom view.
* 判断是不是滚动到了最顶部,这个是从SwipeRefreshLayout里面copy过来的源代码
*/
private boolean canScrollUp() {
if (android.os.Build.VERSION.SDK_INT < 14) {
return ViewCompat.canScrollVertically(this, -1) || this.getScrollY() > 0;
} else {
return ViewCompat.canScrollVertically(this, -1);
}
}
/**
* 停止刷新
*/
public void onStopRefresh() {
mCurrentRefreshStatus = REFRESH_STATUS_NORMAL;
restoreRefreshView();
if (mRefreshCreator != null) {
mRefreshCreator.onStopRefresh();
}
}
// 处理刷新回调监听
private OnRefreshListener mListener;
public void setOnRefreshListener(OnRefreshListener listener) {
this.mListener = listener;
}
public interface OnRefreshListener {
void onRefresh();
}
六、具体使用
实现辅助类
public class DefaultRefreshCreator extends RefreshViewCreator {
// 加载数据的ImageView
private View mRefreshIv;
@Override
public View getRefreshView(Context context, ViewGroup parent) {
View refreshView = LayoutInflater.from(context).inflate(R.layout.layout_refresh_header_view, parent, false);
mRefreshIv = refreshView.findViewById(R.id.refresh_iv);
return refreshView;
}
@Override
public void onPull(int currentDragHeight, int refreshViewHeight, int currentRefreshStatus) {
float rotate = ((float) currentDragHeight) / refreshViewHeight;
// 不断下拉的过程中旋转图片
mRefreshIv.setRotation(rotate * 360);
}
@Override
public void onRefreshing() {
// 刷新的时候不断旋转
RotateAnimation animation = new RotateAnimation(0, 720,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
animation.setRepeatCount(-1);
animation.setDuration(1000);
mRefreshIv.startAnimation(animation);
}
@Override
public void onStopRefresh() {
// 停止加载的时候清除动画
mRefreshIv.setRotation(0);
mRefreshIv.clearAnimation();
}
}
设置
mRecyclerView.addRefreshViewCreator(new DefaultRefreshCreator());