自定义Recyclerview实现下拉刷新

Android已经有很多开源的下拉刷新的库可以使用,比如官方的SwipeRefreshLayout,但是出于练习的目的,还是自己写了一下下拉刷新的逻辑和实现,有些简陋:
首先自定义MyRecyclerview继承自Recyclerview,继承的目的是获取recyclerview的滑动过程的数据,通过位置来判断是否需要刷新,主要是重写onTouchEvent方法:

private MyLinearLayoutManager layoutManager;
private int firstVisibleItem;
private OnRefreshLayout refreshLayout;
float oldX = 0,oldY = 0;
float currentX = 0,currentY = 0;
private int deltaDp;
private float overY,maxY;
private Context context;

......

@Override
public boolean onTouchEvent(MotionEvent e){
	layoutManager = (MyLinearLayoutManager)getLayoutManager();
	switch (e.getAction()){
		case MotionEvent.ACTION_DOWN:
			Log.i("dingyl","ACTION_DOWN");
			oldX = e.getRawX();
			oldY = e.getRawY();
			break;
		case MotionEvent.ACTION_MOVE:
			firstVisibleItem = layoutManager.findFirstVisibleItemPosition();
			currentX = e.getRawX();
			currentY = e.getRawY();
			if (currentY > maxY){
				maxY = currentY;
			}
			overY = 0;
			deltaDp = DensityUtil.px2dp(context,currentY - oldY);
			if (deltaDp > 230){
				oldY = maxY - DensityUtil.dp2px(context,230); //相当于oldY相应下拉
			}
			if (currentY > oldY && firstVisibleItem == 0){
				refreshLayout.refreshLayout(currentY - oldY);
			}
		break;
		case MotionEvent.ACTION_UP:
			maxY = 0;
			refreshLayout.refreshStop();
		break;
	}
	return super.onTouchEvent(e);
}

MyRecyclerview的主要代码如上所示,firstVisibleItem是当前界面可见的第一个item的位置,是通过LayoutManager获取的,这里要注意的是,LayoutManager是构造器之后才创建的,所以在构造器里面直接获取LayoutManager会报错。oldX,oldY,currentX,currentY是触摸点的位置,getRawX(Y)是获取整个屏幕坐标系下的坐标,maxY是下拉最远的位置坐标,deltaDp是滑动的距离,这里是如果大于230dp(设置的头部最大距离),则需要判断额外下拉的位置,因为如果大于230dp还在继续下拉,想要上拉恢复状态就要减去这段距离。如果当前item是第一个,而且手指是在下拉状态,则需要调整头部的高度,这部分的代码在Adapter中完成,是通过实现接口来调用adapter的方法:

public interface OnRefreshLayout{
	void refreshLayout(float delta);
	void refreshStop();
}

Recyclerview的适配器中实现这个接口:

public class RefreshAdapter extends RecyclerView.Adapter implements MyRecyclerView.OnRefreshLayout

@Override
public void refreshLayout(float delta) {
	headViewHolder1.refreshIcon.setVisibility(View.VISIBLE);
	headViewHolder1.refreshText.setVisibility(View.VISIBLE);
	headViewHolder1.loadingImage.setVisibility(View.GONE);
	int deltaY = DensityUtil.px2dp(context,delta);
	if (deltaY > 200){
		headViewHolder1.refreshIcon.setImageResource(R.drawable.load_more);
		headViewHolder1.refreshText.setText("松开刷新");
		canRefresh = true;
	}else {
		headViewHolder1.refreshIcon.setImageResource(R.drawable.refresh);
		headViewHolder1.refreshText.setText("上拉刷新");
		canRefresh = false;
	}
	headViewHolder1.setVisibleHeight(deltaY);
}
    
@Override
public void refreshStop() {
	if (canRefresh){
		headViewHolder1.refreshIcon.setVisibility(View.GONE);
		headViewHolder1.refreshText.setVisibility(View.GONE);
		headViewHolder1.loadingImage.setVisibility(View.VISIBLE);
		ObjectAnimator rotate = ObjectAnimator.ofFloat(headViewHolder1.loadingImage,"rotation",360f,0f);
		rotate.setDuration(1000);
		rotate.setRepeatCount(-1);
		rotate.start();
		handler.sendEmptyMessageDelayed(HIDE_HEAD,1000);
	}else {
		handler.sendEmptyMessage(HIDE_HEAD);
	}

}

recyclerview判断下拉状态之后调用到Adapter的方法,deltaY 是触发“松开刷新”的临界点,大于200的话,判断为“松开刷新”,小于200的话,判断为“下拉刷新”,这里改变头部高度是通过LayouParams来实现的:

class HeadViewHolder extends RecyclerView.ViewHolder{
	ImageView loadingImage,refreshIcon;
	TextView refreshText;
	
	public HeadViewHolder(@NonNull View itemView) {
		super(itemView);
		loadingImage = itemView.findViewById(R.id.loading_image);
		refreshIcon = itemView.findViewById(R.id.refresh_icon);
		refreshText = itemView.findViewById(R.id.refresh_text);
	}
	        
	public void setVisibleHeight(int height){
		if (height <= 230){
			RecyclerView.LayoutParams lp  = (RecyclerView.LayoutParams) itemView.getLayoutParams();
			lp.height = height;
			itemView.setLayoutParams(lp);
		}
	}
}

通过调整LayoutPrams设置头部高度,来实现下拉变高 上拉恢复(不过这里代码还有点问题,上拉的同时Recyclerview也在上拉,所以有一部分头部被滚动出屏幕之外)
注意,这里LayoutParams 要使用Recyclerview.LayoutParams,不能使用ViewGroup.LayoutParams,否则就会报错。
至此,一个简单的下拉刷新功能就实现了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值