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,否则就会报错。
至此,一个简单的下拉刷新功能就实现了