美文网首页
Android 为RecyclerView 添加头部刷新功能

Android 为RecyclerView 添加头部刷新功能

作者: 飞鸟和鱼_89ab | 来源:发表于2018-10-09 21:29 被阅读0次

简介:

突发奇想好多APP的列表视图都有下拉刷新的功能,可能大家会联想到ListView或RecyclerView,今天就针对RecyclerView说说下拉刷新是怎样实现的?
如果有不了解RecyclerView的原理请看

快速了解传送门:https://www.jianshu.com/p/4f9591291365
本文代码附在文章的末尾

基本实现原理:

首先了解一下当触发下拉手势的几种状态

1:当手指触摸屏幕后并向下滑动,此下拉过程中有两种状态第一个为下拉状态,第二个为到了一个零界点该改变状态为释放刷新的状态

2:当手指离开屏幕后会有三种状态,第一种刷新中,第二种刷新成功或者失败

3:刷新成功或者失败时候刷新状态回归为默认未刷新的状态

public static final int HEAD_STATE_NORMAL = 0;//正常
public static final int HEAD_STATE_RELEASE_TO_REFRESH = 1;//释放
public static final int HEAD_STATE_REFRESHING = 2;//刷新
public static final int HEAD_STATE_SUCCESS = 3;//刷新成功
public static final int HEAD_STATE_FAIL = 4;//刷新失败
public static final int HEAD_STATE_RETREAT_NORMAL = 5;//后退到正常 

滑动函数:

   final void onMove(float move) {
        //动画没有执行则为true
        if (!isAnimation) {
            //System.out.println("刷新高度: " + getVisibleHeight());
            if (mHeadRefreshState == HEAD_STATE_NORMAL
                    || mHeadRefreshState == HEAD_STATE_RETREAT_NORMAL
                    || mHeadRefreshState == HEAD_STATE_RELEASE_TO_REFRESH) {
                int newVisibleHeight = (int) (getVisibleHeight() + Math.floor(move / 2.5));
                setVisibleHeight(newVisibleHeight);
                //如果当前高度等于0
                if (getVisibleHeight() == 0) {
                    mHeadRefreshState = HEAD_STATE_NORMAL;
                }
                //如果当前高度小于刷新高度
                else if (getVisibleHeight() < refreshHeight) {
                    mHeadRefreshState = HEAD_STATE_RETREAT_NORMAL;
                    onPullingDown();
                }
                //如果当前高度大于刷新高度
                else if (getVisibleHeight() > refreshHeight) {
                    mHeadRefreshState = HEAD_STATE_RELEASE_TO_REFRESH;
                    onReleaseState();
                }
            }
        }
    }

手指离开

//手指离开
 final void onUp() {
     //动画没有执行则为true
     if (!isAnimation) {
         //状态正常和动画不在运行的时则为true
         if (mHeadRefreshState == HEAD_STATE_RETREAT_NORMAL) {
             smoothScrollTo(0);
         } else if (mHeadRefreshState == HEAD_STATE_RELEASE_TO_REFRESH) {
             mHeadRefreshState = HEAD_STATE_REFRESHING;
             onRefreshing();
             smoothScrollTo(refreshHeight);//回退到刷新的临界点高度
         }
     }
 }

刷新类是继承LinearLayout的并且设置成一个抽象的类,方便扩展,然后包装在RecyclerView Adapter中

public abstract class AbsRefreshHeaderView extends LinearLayout

刷新动画

    private void smoothScrollTo(int height) {
        ValueAnimator animator = ValueAnimator.ofInt(getVisibleHeight(), height);
        animator.setDuration(260);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                setVisibleHeight((int) animation.getAnimatedValue());
            }
        });
        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                isAnimation = animation.isRunning();//true
            }
        });
        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                isAnimation = animation.isRunning();//false
                if (mHeadRefreshState == HEAD_STATE_REFRESHING) {
                    refreshListener.refresh();
                }
                //如果状态成功或者失败后恢复状态为默认的状态
                if (mHeadRefreshState == HEAD_STATE_SUCCESS || mHeadRefreshState == HEAD_STATE_FAIL) {
                    mHeadRefreshState = HEAD_STATE_NORMAL;
                }
            }
        });
        animator.start();
    }

以下是实现该功能的全部代码

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.widget.LinearLayout;

public abstract class AbsRefreshHeaderView extends LinearLayout {

    //刷新监听
    private RefreshListener refreshListener;

    public static final int HEAD_STATE_NORMAL = 0;//正常
    public static final int HEAD_STATE_RELEASE_TO_REFRESH = 1;//释放
    public static final int HEAD_STATE_REFRESHING = 2;//刷新
    public static final int HEAD_STATE_SUCCESS = 3;//刷新成功
    public static final int HEAD_STATE_FAIL = 4;//刷新失败
    public static final int HEAD_STATE_RETREAT_NORMAL = 5;//后退到正常

    private int mHeadRefreshState;//刷新状态
    private int refreshHeight;//触发刷新的高度

    private boolean isAnimation = false;//动画是否完成

    public AbsRefreshHeaderView(Context context) {
        this(context, null);
    }

    public AbsRefreshHeaderView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public AbsRefreshHeaderView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setRefreshHeight();
        init();
    }

    //初始化
    protected abstract void init();

    //正在下拉
    protected abstract void onPullingDown();

    //已经达到可以刷新的状态
    protected abstract void onReleaseState();

    //执行刷新
    protected abstract void onRefreshing();

    //刷新成功
    protected abstract void onResultSuccess();

    //刷新失败
    protected abstract void onResultFail();

    protected void setRefreshHeight() {
        refreshHeight = (int) (Resources.getSystem().getDisplayMetrics().density * 90 + 0.5F);
    }

    //获取head高度
    protected int getVisibleHeight() {
        return getLayoutParams().height;
    }

    protected boolean getIsNormal() {
        return getVisibleHeight() == 0 && mHeadRefreshState == HEAD_STATE_NORMAL;
    }

    //是否在刷新中(刷新中包含刷新成功或失败)
    protected boolean getIsRefreshing() {
        return mHeadRefreshState == HEAD_STATE_REFRESHING
                || mHeadRefreshState == HEAD_STATE_FAIL
                || mHeadRefreshState == HEAD_STATE_SUCCESS;
    }

    //设置head高度
    public void setVisibleHeight(int height) {
        height = height < 0 ? 0 : height;
        ViewGroup.LayoutParams lp = getLayoutParams();
        lp.height = height;
        setLayoutParams(lp);
    }

    private void smoothScrollTo(int height) {
        ValueAnimator animator = ValueAnimator.ofInt(getVisibleHeight(), height);
        animator.setDuration(260);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                setVisibleHeight((int) animation.getAnimatedValue());
            }
        });
        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                isAnimation = animation.isRunning();//true
            }
        });
        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                isAnimation = animation.isRunning();//false
                if (mHeadRefreshState == HEAD_STATE_REFRESHING) {
                    refreshListener.refresh();
                }
                //如果状态成功或者失败后恢复状态为默认的状态
                if (mHeadRefreshState == HEAD_STATE_SUCCESS || mHeadRefreshState == HEAD_STATE_FAIL) {
                    mHeadRefreshState = HEAD_STATE_NORMAL;
                }
            }
        });
        animator.start();
    }

    //手指移动 该方法只涉及到头部高度,没有涉及到动画
    final void onMove(float move) {
        //动画没有执行则为true
        if (!isAnimation) {
            //System.out.println("刷新高度: " + getVisibleHeight());
            if (mHeadRefreshState == HEAD_STATE_NORMAL
                    || mHeadRefreshState == HEAD_STATE_RETREAT_NORMAL
                    || mHeadRefreshState == HEAD_STATE_RELEASE_TO_REFRESH) {
                int newVisibleHeight = (int) (getVisibleHeight() + Math.floor(move / 2.5));
                setVisibleHeight(newVisibleHeight);
                //如果当前高度等于0
                if (getVisibleHeight() == 0) {
                    mHeadRefreshState = HEAD_STATE_NORMAL;
                }
                //如果当前高度小于刷新高度
                else if (getVisibleHeight() < refreshHeight) {
                    mHeadRefreshState = HEAD_STATE_RETREAT_NORMAL;
                    onPullingDown();
                }
                //如果当前高度大于刷新高度
                else if (getVisibleHeight() > refreshHeight) {
                    mHeadRefreshState = HEAD_STATE_RELEASE_TO_REFRESH;
                    onReleaseState();
                }
            }
        }
    }

    //手指离开
    final void onUp() {
        //动画没有执行则为true
        if (!isAnimation) {
            //状态正常和动画不在运行的时则为true
            if (mHeadRefreshState == HEAD_STATE_RETREAT_NORMAL) {
                smoothScrollTo(0);
            } else if (mHeadRefreshState == HEAD_STATE_RELEASE_TO_REFRESH) {
                mHeadRefreshState = HEAD_STATE_REFRESHING;
                onRefreshing();
                smoothScrollTo(refreshHeight);//回退到刷新的临界点高度
            }
        }
    }

    //判断刷新是否成功
    final void setRefreshState(boolean isSuccess) {
        if (!isAnimation) {
            if (isSuccess) {
                mHeadRefreshState = HEAD_STATE_SUCCESS;
                onResultSuccess();//刷新成功
                smoothScrollTo(0);
            } else {
                mHeadRefreshState = HEAD_STATE_FAIL;
                onResultFail();//刷新失败
                smoothScrollTo(0);
            }
        }
    }

    //刷新成功
    public void onRefreshSuccess() {
        setRefreshState(true);
    }

    //刷新失败
    public void onRefreshFail() {
        setRefreshState(false);
    }

    //定义刷新的监听
    public interface RefreshListener {
        void refresh();
    }

    final void setRefreshListener(RefreshListener listener) {
        refreshListener = listener;
    }
}

定义一个刷新类的具体实现类

public class SimpleRefreshHeaderView extends AbsRefreshHeaderView {

    private TextView mTvTip;

    public SimpleRefreshHeaderView(Context context) {
        super(context);
    }

    public SimpleRefreshHeaderView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public SimpleRefreshHeaderView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void init() {
        LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0);
        setLayoutParams(params);
        setGravity(Gravity.BOTTOM);
        View view = LayoutInflater.from(getContext()).inflate(R.layout.header_refresh_view, this, false);
        mTvTip = view.findViewById(R.id.tv_tip);
        addView(view);
    }

    @Override
    protected void onPullingDown() {
        mTvTip.setText(R.string.pull_to_refresh);
    }

    @Override
    protected void onReleaseState() {
        mTvTip.setText(R.string.release_refresh);
    }

    @Override
    protected void onRefreshing() {
        mTvTip.setText(R.string.refreshing);
    }

    @Override
    protected void onResultSuccess() {
        mTvTip.setText(R.string.refresh_success);
    }

    @Override
    protected void onResultFail() {
        mTvTip.setText(R.string.refresh_fail);
    }
}

然后把该刷新视图包装到RecyclerView中

public class RecyclerPlusView extends RecyclerView {

    private RecyclerPlusAdapter mRecyclerPlusAdapter;
    private AbsRefreshHeaderView absRefreshHeaderView;
    private PullListener pullListener;
    private float lastY;

    public RecyclerPlusView(Context context) {
        super(context);
    }

    public RecyclerPlusView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public RecyclerPlusView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public void setAdapter(Adapter adapter) {
        if (adapter != null) {
            mRecyclerPlusAdapter = new RecyclerPlusAdapter(adapter);
            super.setAdapter(mRecyclerPlusAdapter);
        }
    }

    private class RecyclerPlusAdapter extends Adapter<ViewHolder> {

        private static final int TYPE_REFRESH_HEADER = 10000;

        private Adapter mAdapter;

        public RecyclerPlusAdapter(Adapter adapter) {
            mAdapter = adapter;
        }

        @Override
        public int getItemCount() {
            int count = 0;
            if (absRefreshHeaderView != null) {
                count++;
            }
            if (mAdapter != null) {
                count += mAdapter.getItemCount();
            }
            return count;
        }

        @Override
        public int getItemViewType(int position) {
            if (absRefreshHeaderView != null && position == 0) {
                return TYPE_REFRESH_HEADER;
            }
            return 0;
        }

        @NonNull
        @Override
        public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            if (viewType == TYPE_REFRESH_HEADER) {
                return new PlusHolder(absRefreshHeaderView);
            }
            return mAdapter.onCreateViewHolder(parent, viewType);
        }

        @Override
        public void onBindViewHolder(@NonNull ViewHolder holder, int position) {

        }

        private class PlusHolder extends ViewHolder {

            public PlusHolder(View itemView) {
                super(itemView);
            }
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent e) {
        if (absRefreshHeaderView != null) {
            switch (e.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    lastY = e.getRawY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    float moveY = e.getRawY() - lastY;
                    lastY = e.getRawY();
                    if (isTop()) {
                        absRefreshHeaderView.onMove(moveY);
                        //Log.d("headView: ", String.valueOf(refreshHeadView.getVisibleHeight()));
                        if (absRefreshHeaderView.getIsNormal()) {
                            //Log.d("isNormal: ", String.valueOf(refreshHeadView.getIsNormal()));
                            return super.onTouchEvent(e);
                        }
                        return false;
                    }
                case MotionEvent.ACTION_UP:
                    absRefreshHeaderView.onUp();
                    break;
            }
        }
        return super.onTouchEvent(e);
    }

    private boolean isTop() {
        return absRefreshHeaderView.getParent() != null;
    }

    //定义下拉上拉监听
    public interface PullListener {
        void refresh();
    }

    //设置下拉刷新上拉加载监听
    public void setPullListener(PullListener listener) {
        this.pullListener = listener;
        if (getAdapter() != null) {
            initRefresh();
        }
    }

    //初始化刷新
    private void initRefresh() {
        if (absRefreshHeaderView == null) {
            absRefreshHeaderView = new SimpleRefreshHeaderView(getContext());
        }
        //mPlushAdapter.setRefreshHeaderView(absRefreshHeaderView);
        absRefreshHeaderView.setRefreshListener(new AbsRefreshHeaderView.RefreshListener() {
            @Override
            public void refresh() {
                pullListener.refresh();
            }
        });
    }

    //判断刷新是否成功
    public void onRefreshComplete(boolean success) {
        if (success) {
            absRefreshHeaderView.onRefreshSuccess();
        } else {
            absRefreshHeaderView.onRefreshFail();
        }
    }
}

https://github.com/931005451/RecyclerPlusView

相关文章

网友评论

      本文标题:Android 为RecyclerView 添加头部刷新功能

      本文链接:https://www.haomeiwen.com/subject/ywnyaftx.html