Android RecyclerView下拉刷新、上拉加载更多、到底自动加载更多

一、废话

搞了这么久的Android,可能是我渠道比较闭塞,一直没找到比较好用的下拉刷新,往上找的第三方比较知名的一些,都说有这样那样的瑕疵,一直用的SwipeRefreshLayout 做下拉加载,然后在RecycleView最后多加一个item,只要加载了这个item,就自动加载更多,要多low有多low,完全没有欲望去搞别人那种很炫酷的效果。

二、效果图

附上原味效果图(可以通过继承本代码来定制,提供有相关方法和接口的,后续博客会说明并举例):

152439_UsOp_1462828.gif

三、使用说明

1、5中状态类别

代码区分了5中状态供不同的应用场景使用,提供了方法可直接设置,也可以各种状态无缝切换:

下拉刷新两种:可下拉、不可下拉(带阻尼回弹)

上拉加载三种:上拉加载、到底自动加载、不加载(带阻尼回弹)

2、导入方法

源码只有两个文件,一个类(RefreshRelativeLayout)和一个布局文件(refresh_relative_layout.xml),所有代码均用的最原始的方式,并无其他比较高深的第三方的东西,可以直接导入使用

3、调用方法

通过设置回调接口监听刷新和加载时间,至于数据,可以通过getRecyclerView方法获取RecyclerView,至于怎么给RecyclerView加上数据操作,这里我就不多废话了,下面是一段调用代码:

//list是数据,adapter是适配器,这两个东西我就不放出来了,大家都懂
RefreshRelativeLayout recyclerView = (RefreshRelativeLayout) findViewById(R.id.recyclerView);
recyclerView.setOnRefreshListener(new RefreshRelativeLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        adapter.setData(list);
                        recyclerView.setRefreshing(false);
                    }
                }, 2000);//延迟两秒操作
            }
        });

        recyclerView.setOnLoadListener(new RefreshRelativeLayout.OnLoadListener() {
            @Override
            public void onLoad() {
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        adapter.addData(list);
                        recyclerView.setLoading(false);
                    }
                }, 2000);//延迟两秒操作
            }
        });

        adapter.setData(list);
        recyclerView.getRecyclerView().setAdapter(adapter);
    }

四、源码说明及源码

1、源码说明

整体构成是由一个父RelativeeLayout容器包裹三大部件(头部RelativeLayout、RecyclerView、尾部RelativeLayout)构成;通过重写父RelativeeLayout的dispatchTouchEvent方法监听手指的触摸事件,然后再根据手指移动的位置做各种状态的切换处理,细节可以看源码,注释都非常清晰了的,至少我自以为是很清晰。

RecyclerView不用多说,头部RelativeLayout和尾部RelativeLayout底下均包含了四个RelativeLayout用于表示四种不同的状态,分别是:(1)下拉或上拉的时候未达到松开刷新加载的状态(2)松开刷新或者加载(3)正在刷新或者加载(4)刷子、加载完成,这个状态可以设置延迟播放收起动画的时间

重写UI的方法(后续会写个博客介绍):

(1)、重写一个refresh_relative_layout.xml,整体结构不能变,三大部件、4*2中状态的RelativeLayout都不能动,但是可以改变里面的内容,以达到不同的状态显示不同的UI的效果。

(2)。继承RefreshRelativeLayout并重写里面的一些方法,具体那些方法重写可以达到相关的功能,可以看源码,我通过分隔线进行了分类,另外提供了一个回调接口,用于回调当前手指拖动的距离,这样方便制作下拉或者上拉时候的某种拖动动效(例如朋友圈的彩色圆圈,拖动的时候就转,按着不动的时候就不转)。

2、源码(RefreshRelativeLayout)


import android.content.Context;
import android.graphics.Rect;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Interpolator;
import android.view.animation.TranslateAnimation;
import android.widget.RelativeLayout;

import com.imxiaoyu.common.R;

/**
 * 下拉刷新以及上啦加载更多
 * Created by 她叫我小渝 on 2016/12/22.
 */

public class RefreshRelativeLayout extends RelativeLayout {

    /**
     * 常量
     */
    //pullDownType下拉类型的值;1-正常可以下拉刷新  2-不可以下拉刷新(有阻尼效果 )
    public static final int PULL_DOWN_TYPE_REFRESH = 1;
    public static final int PULL_DOWN_TYPE_NOT_PULL = 2;
    //pullUpType上拉类型的值;1-可以上拉加载 2-到了底部自动加载更多  3-不可以上拉刷新(有阻尼效果)
    public static final int PULL_UP_TYPE_LOAD_PULL = 1;
    public static final int PULL_UP_TYPE_LOAD_AUTO = 2;
    public static final int PULL_UP_TYPE_NOT_PULL = 3;
    //pullDownState下拉状态的值;1-下拉  2-松开刷新  3-正在刷新  4-刷新完成
    public static final int PULL_DOWN_STATE_1 = 1;
    public static final int PULL_DOWN_STATE_2 = 2;
    public static final int PULL_DOWN_STATE_3 = 3;
    public static final int PULL_DOWN_STATE_4 = 4;
    //pullDownState上拉状态的值;1-上拉  2-松开加载  3-正在加载  4-加载完成
    public static final int PULL_UP_STATE_1 = 1;
    public static final int PULL_UP_STATE_2 = 2;
    public static final int PULL_UP_STATE_3 = 3;
    public static final int PULL_UP_STATE_4 = 4;

    /**
     * UI
     */
    private RecyclerView recyclerView;
    private RelativeLayout rlyHead;
    private RelativeLayout rlyFoot;


    private RelativeLayout rlyHeadState1;
    private RelativeLayout rlyHeadState2;
    private RelativeLayout rlyHeadState3;
    private RelativeLayout rlyHeadState4;
    private RelativeLayout rlyFootState1;
    private RelativeLayout rlyFootState2;
    private RelativeLayout rlyFootState3;
    private RelativeLayout rlyFootState4;

    /**
     * 变量
     */
    private int pullDownType = PULL_DOWN_TYPE_REFRESH;//下拉类型;
    private int pullUpType = PULL_UP_TYPE_LOAD_PULL;//上拉类型;
    private int pullDownState = PULL_DOWN_STATE_1;//下拉状态
    private int pullUpState = PULL_UP_STATE_1;//上拉状态
    private boolean isRefreshing = false;//正在刷新
    private boolean isLoading = false;//正在加载更多
    private int afterRefreshDelayTime = 200;//刷新完成之后,延迟收起头部视图的时间
    private boolean isTouching = false;//是否正在按压着屏幕

    // y方向上当前触摸点的前一次记录位置
    private int previousY = 0;
    // y方向上的触摸点的起始记录位置
    private int startY = 0;
    // y方向上的触摸点当前记录位置
    private int currentY = 0;
    // y方向上两次移动间移动的相对距离
    private int deltaY = 0;

    // 用于记录childView的初始位置
    private Rect rectRlv = new Rect();//recyclerView的初始位置
    private Rect rectRlyHead = new Rect();//头部的初始位置
    private Rect rectRlyFoot = new Rect();//尾部的初始位置

    //水平移动的距离
    private float moveHeight;

    /**
     * 接口
     */
    private OnRefreshListener onRefreshListener;//刷新回调
    private OnLoadListener onLoadListener;//加载回调
    private OnTouchHeightListener onTouchHeightListener;//拖动的高度监听,正数是下拉,负数是上啦(不会为0)


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

    public RefreshRelativeLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public RefreshRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        inflate(getContext(), getLayoutId(), this);
        initView();
    }


    private void initView() {
        recyclerView = (RecyclerView) findViewById(R.id.rlv);
        rlyHead = getHeadLayout((RelativeLayout) findViewById(R.id.rly_refresh_head));
        rlyFoot = getFootLayout((RelativeLayout) findViewById(R.id.rly_refresh_foot));

        rlyHeadState1 = getHeadState1Layout((RelativeLayout) findViewById(R.id.rly_head_state_1));
        rlyHeadState2 = getHeadState2Layout((RelativeLayout) findViewById(R.id.rly_head_state_2));
        rlyHeadState3 = getHeadState3Layout((RelativeLayout) findViewById(R.id.rly_head_state_3));
        rlyHeadState4 = getHeadState4Layout((RelativeLayout) findViewById(R.id.rly_head_state_4));

        rlyFootState1 = getFootState1Layout((RelativeLayout) findViewById(R.id.rly_foot_state_1));
        rlyFootState2 = getFootState2Layout((RelativeLayout) findViewById(R.id.rly_foot_state_2));
        rlyFootState3 = getFootState3Layout((RelativeLayout) findViewById(R.id.rly_foot_state_3));
        rlyFootState4 = getFootState4Layout((RelativeLayout) findViewById(R.id.rly_foot_state_4));

        //初始化一下三大件的位置
        initLayoutRect();
        recyclerView.setOnScrollChangeListener(new OnScrollChangeListener() {
            @Override
            public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
                //四个判定条件,1-不是正在加载 2-处于自动加载更多模式 3-列表已经到底部了 4-列表没有在顶部(3-4一起的意思是列表到底部了,且列表的数据必须大于一页)
                if (!isLoading && pullUpType == PULL_UP_TYPE_LOAD_AUTO && !recyclerView.canScrollVertically(1) && recyclerView.canScrollVertically(-1)) {
                    setPullUpState(PULL_UP_STATE_3);//到底部自动加载
                }
            }
        });
        recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));//recyclerView默认是ListView一样的列表
    }

    //供外部调用的功能性方法↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓分隔线↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓


    /**
     * 返回RecyclerView,作为数据操作使用
     * @return
     */
    public RecyclerView getRecyclerView() {
        return recyclerView;
    }

    /**
     * 设置下拉类型 1-正常可以下拉刷新(默认)  2-不可以下拉刷新(有阻尼效果 )
     * ( 最好是在刚初始化的时候就调用)
     *
     * @param type
     */
    public void setPullDownType(int type) {
        this.pullDownType = type;
        if (type == PULL_DOWN_TYPE_NOT_PULL) {
            rlyHead.setVisibility(GONE);
        } else {
            rlyHead.setVisibility(VISIBLE);
        }
        initLayoutRect();
    }

    /**
     * 设置上啦类型 1-可以下拉刷新(默认) 2-到了底部自动加载更多  3-不可以上拉刷新(有阻尼效果)
     * ( 最好是在刚初始化的时候就调用)
     *
     * @param type
     */
    public void setPullUpType(int type) {
        this.pullUpType = type;
        if (type == PULL_UP_TYPE_NOT_PULL) {
            rlyFoot.setVisibility(GONE);
        } else {
            rlyFoot.setVisibility(VISIBLE);
        }
        initLayoutRect();
    }

    /**
     * 设置正在刷新或者刷新完成
     *
     * @param bln
     */
    public void setRefreshing(final boolean bln) {
        if (bln) {
            setPullDownState(PULL_DOWN_STATE_3);
        } else {
            setPullDownState(PULL_DOWN_STATE_4);
        }
    }

    /**
     * 设置正在刷新或者刷新完成
     *
     * @param bln
     */
    public void setLoading(final boolean bln) {
        if (bln) {
            setPullUpState(PULL_UP_STATE_3);
        } else {
            setPullUpState(PULL_UP_STATE_4);
        }
    }

    /**
     * 监听下拉刷新状态
     *
     * @param listener
     */
    public void setOnRefreshListener(OnRefreshListener listener) {
        this.onRefreshListener = listener;
    }

    /**
     * 监听上啦或者自动加载状态
     *
     * @param listener
     */
    public void setOnLoadListener(OnLoadListener listener) {
        this.onLoadListener = listener;
    }

    /**
     * 拖动的高度监听,正数是下拉,负数是上啦(不会为0,且只监听拖动事件,按下和抬起均不回调)
     *
     * @param listener true-正在加载  false-加载完成
     */
    public void setOnLoadListener(OnTouchHeightListener listener) {
        this.onTouchHeightListener = listener;
    }

    /**
     * 设置刷新完成之后收起视图的时间,毫秒单位
     *
     * @param time
     */
    public void setAfterRefreshDelayTime(int time) {
        this.afterRefreshDelayTime = time;
    }
    //供外部调用的功能性方法↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑分隔线↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑


    //如需自定义样式,可以继承后重写下方的方法↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓分隔线↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

    /**
     * 使用的布局
     *
     * @return
     */
    public int getLayoutId() {
        return R.layout.refresh_relative_layout;
    }

    /**
     * 头部容器(如果需要特殊定制,可以做些修改,但建议不要改动头、尾的容器)
     *
     * @param rlyHead
     * @return
     */
    public RelativeLayout getHeadLayout(RelativeLayout rlyHead) {
        return rlyHead;
    }

    /**
     * 下拉刷新的4种状态的视图
     *
     * @param rlyHead
     * @return
     */
    public RelativeLayout getHeadState1Layout(RelativeLayout rlyHead) {
        return rlyHead;
    }

    public RelativeLayout getHeadState2Layout(RelativeLayout rlyHead) {
        return rlyHead;
    }

    public RelativeLayout getHeadState3Layout(RelativeLayout rlyHead) {
        return rlyHead;
    }

    public RelativeLayout getHeadState4Layout(RelativeLayout rlyHead) {
        return rlyHead;
    }

    /**
     * 上拉刷新的4种状态的视图
     *
     * @param rlyHead
     * @return
     */
    public RelativeLayout getFootState1Layout(RelativeLayout rlyHead) {
        return rlyHead;
    }

    public RelativeLayout getFootState2Layout(RelativeLayout rlyHead) {
        return rlyHead;
    }

    public RelativeLayout getFootState3Layout(RelativeLayout rlyHead) {
        return rlyHead;
    }

    public RelativeLayout getFootState4Layout(RelativeLayout rlyHead) {
        return rlyHead;
    }

    /**
     * 尾部容器(如果需要特殊定制,可以做些修改,但建议不要改动头、尾的容器)
     *
     * @param rlyFoot
     * @return
     */
    public RelativeLayout getFootLayout(RelativeLayout rlyFoot) {
        return rlyFoot;
    }

    /**
     * 回调动画的动画时长,可以根据需求调节快一点慢一点
     * @return
     */
    public int getAnimTime(){
        return 760;
    }

    //如需自定义样式,可以继承后重写上方的方法↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑分隔线↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑


    /**
     * 设置下拉的状态
     *
     * @param state 下拉状态的值;1-下拉  2-松开刷新  3-正在刷新  4-刷新完成
     */
    private void setPullDownState(int state) {
        if (state < 1 || state > 4) {
            Log.e("错误","下拉状态的值越界");
            return;
        }
        pullDownState = state;
        if (onRefreshListener != null && state == PULL_DOWN_STATE_3) {
            //下拉刷新
            onRefreshListener.onRefresh();
        }
        changePullDownState();
    }

    /**
     * 下拉状态改变
     */
    private void changePullDownState() {
        isRefreshing = false;
        rlyHeadState1.setVisibility(INVISIBLE);
        rlyHeadState2.setVisibility(INVISIBLE);
        rlyHeadState3.setVisibility(INVISIBLE);
        rlyHeadState4.setVisibility(INVISIBLE);
        switch (pullDownState) {
            case PULL_DOWN_STATE_1:
                //下拉刷新
                if (pullDownType != PULL_DOWN_TYPE_NOT_PULL) {
                    rlyHeadState1.setVisibility(VISIBLE);
                }
                break;
            case PULL_DOWN_STATE_2:
                //松开刷新
                if (pullDownType != PULL_DOWN_TYPE_NOT_PULL) {
                    rlyHeadState2.setVisibility(VISIBLE);
                }
                break;
            case PULL_DOWN_STATE_3:
                //正在刷新
                isRefreshing = true;//除了正在刷新之外,其他状态都不在刷新
                if (pullDownType != PULL_DOWN_TYPE_NOT_PULL) {
                    rlyHeadState3.setVisibility(VISIBLE);
                }
                //无论当前再什么位置,都跳到正在刷新的地方
                moveViewAnimation(rlyHead, 0);
                moveViewAnimation(recyclerView, rlyHead.getHeight());
                break;
            case PULL_DOWN_STATE_4:
                //刷新成功
                if (pullDownType != PULL_DOWN_TYPE_NOT_PULL) {
                    rlyHeadState4.setVisibility(VISIBLE);
                }
                //无论当前再什么位置,都要跳到初始的位置
                postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        moveViewAnimation(rlyHead, -1 * rlyHead.getHeight());
                        moveViewAnimation(recyclerView, 0);
                    }
                }, afterRefreshDelayTime);
                break;
        }
    }


    /**
     * 设置下拉的状态
     *
     * @param state 下拉状态的值;1-下拉  2-松开刷新  3-正在刷新  4-刷新完成
     */
    private void setPullUpState(int state) {
        if (state < 1 || state > 4) {
            Log.e("错误","上拉状态的值越界");
            return;
        }
        pullUpState = state;
        if (onLoadListener != null && state == PULL_UP_STATE_3) {
            //下拉刷新
            onLoadListener.onLoad();
        }
        changePullUpState();
    }

    /**
     * 下拉状态改变
     */
    private void changePullUpState() {
        isLoading = false;
        rlyFootState1.setVisibility(INVISIBLE);
        rlyFootState2.setVisibility(INVISIBLE);
        rlyFootState3.setVisibility(INVISIBLE);
        rlyFootState4.setVisibility(INVISIBLE);
        switch (pullUpState) {
            case PULL_UP_STATE_1:
                //下拉刷新(自动加载更多的模式下禁止显示)
                if (pullUpType != PULL_UP_TYPE_LOAD_AUTO && pullUpType != PULL_UP_TYPE_NOT_PULL) {
                    rlyFootState1.setVisibility(VISIBLE);
                }
                break;
            case PULL_UP_STATE_2:
                //松开刷新(自动加载模式下禁止显示)
                if (pullUpType != PULL_UP_TYPE_LOAD_AUTO && pullUpType != PULL_UP_TYPE_NOT_PULL) {
                    rlyFootState2.setVisibility(VISIBLE);
                }
                break;
            case PULL_UP_STATE_3:
                //正在刷新
                isLoading = true;//除了正在刷新之外,其他状态都不在刷新
                if (pullUpType != PULL_UP_TYPE_NOT_PULL) {
                    rlyFootState3.setVisibility(VISIBLE);
                }
                //无论当前再什么位置,都跳到正在刷新的地方
                moveViewAnimation(rlyFoot, getHeight() - rlyFoot.getHeight());
                moveViewAnimation(recyclerView, rlyFoot.getHeight() * -1);
                break;
            case PULL_UP_STATE_4:
                //刷新成功
                if (pullUpType != PULL_UP_TYPE_NOT_PULL) {
                    rlyFootState4.setVisibility(VISIBLE);
                }
                //无论当前再什么位置,都要跳到初始的位置
                postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        moveViewAnimation(rlyFoot, getHeight());
                        moveViewAnimation(recyclerView, 0);
                    }
                }, afterRefreshDelayTime);
                break;
        }
    }

    /**
     * 记录三大控件的初始位置
     */
    private void recordLayoutRect() {
        // 记录三大控件的初始位置
        rectRlv.set(recyclerView.getLeft(), 0, recyclerView.getRight(), getHeight());
        rectRlyHead.set(rlyHead.getLeft(), -1 * rlyHead.getHeight(), rlyHead.getRight(), 0);
        rectRlyFoot.set(rlyFoot.getLeft(), getHeight(), rlyFoot.getRight(), getHeight() + rlyFoot.getHeight());
    }

    /**
     * 初始化三大控件的位置
     */
    private void initLayoutRect() {
        post(new Runnable() {
            @Override
            public void run() {
                //初始化位置
                recyclerView.layout(recyclerView.getLeft(), 0, recyclerView.getRight(), getHeight());
                rlyHead.layout(rlyHead.getLeft(), -1 * rlyHead.getHeight(), rlyHead.getRight(), 0);
                rlyFoot.layout(rlyFoot.getLeft(), getHeight(), rlyFoot.getRight(), getHeight() + rlyFoot.getHeight());

                //绑定一下位置,以免页面重绘的时候位置发生错乱
                RelativeLayout.LayoutParams params =
                        (RelativeLayout.LayoutParams) recyclerView.getLayoutParams();
                params.topMargin=0;
                recyclerView.setLayoutParams(params);
                recyclerView.requestLayout();
                params =
                        (RelativeLayout.LayoutParams) rlyHead.getLayoutParams();
                params.topMargin=-1*rlyHead.getHeight();
                rlyHead.setLayoutParams(params);
                rlyHead.requestLayout();
                params =
                        (RelativeLayout.LayoutParams) rlyFoot.getLayoutParams();
                params.topMargin=getHeight();
                params.bottomMargin=getHeight()+rlyFoot.getHeight();
                rlyFoot.setLayoutParams(params);
                rlyFoot.requestLayout();

                //初始化状态
                setPullDownState(PULL_DOWN_STATE_1);
                setPullUpState(PULL_UP_STATE_1);
            }
        });
    }


    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        boolean isPullRecyclerView = false;//如果把头拉下来了然后再推回上去的时候,必须把recyclerView的触摸事件屏蔽掉,以免列表上移
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                isTouching = true;
                startY = (int) event.getY();
                previousY = startY;
                moveHeight = 0;
                recordLayoutRect();
                break;
            case MotionEvent.ACTION_MOVE:
                currentY = (int) event.getY();
                deltaY = currentY - previousY;
                previousY = currentY;
                //判定是否在顶部或者滑到了底部;第二类判定是,正在刷新什么的,禁止拖动列表
                if ((!recyclerView.canScrollVertically(-1) && (currentY - startY) > 0) ||
                        (!recyclerView.canScrollVertically(1) && (currentY - startY) < 0) ||
                        (rlyHead.getTop() > rectRlyHead.top && (currentY - startY) > 0) ||
                        (rlyFoot.getTop() < rectRlyFoot.top && (currentY - startY) < 0)) {
                    isPullRecyclerView = true;//下拉或者上啦的时候把recyclerView的触摸事件屏蔽掉
                    //计算阻尼
                    float distance = currentY - startY;
                    if (distance < 0) {
                        distance *= -1;
                    }
                    float damping = 0.5f;//阻尼值
                    float height = getHeight();
                    if (height != 0) {
                        if (distance > height) {
                            damping = 0;
                        } else {
                            damping = (height - distance) / height;
                        }
                    }
                    if (currentY - startY < 0) {
                        damping = 1 - damping;
                    }

                    //阻力值限制再0.3-0.5之间,平滑过度
                    damping *= 0.25;
                    damping += 0.25;

                    moveHeight = moveHeight + (deltaY * damping);
                    recyclerView.layout(rectRlv.left, (int) (rectRlv.top + moveHeight), rectRlv.right,
                            (int) (rectRlv.bottom + moveHeight));
                    rlyHead.layout(rectRlyHead.left, (int) (rectRlyHead.top + moveHeight), rectRlyHead.right,
                            (int) (rectRlyHead.bottom + moveHeight));
                    rlyFoot.layout(rectRlyFoot.left, (int) (rectRlyFoot.top + moveHeight), rectRlyFoot.right,
                            (int) (rectRlyFoot.bottom + moveHeight));

                    //判定是否是下拉动作并处于可以下拉刷新状态
                    if (pullDownType == PULL_DOWN_TYPE_REFRESH && moveHeight > 0) {
                        if (moveHeight > rlyHead.getHeight()) {
                            setPullDownState(PULL_DOWN_STATE_2);
                        } else {
                            setPullDownState(PULL_DOWN_STATE_1);
                        }
                    }

                    //判定是否是上拉动作并且处于上啦刷新状态
                    if (pullUpType == PULL_UP_TYPE_LOAD_PULL && moveHeight < 0) {
                        if (moveHeight * -1 > rlyFoot.getHeight()) {
                            setPullUpState(PULL_UP_STATE_2);
                        } else {
                            setPullUpState(PULL_UP_STATE_1);
                        }
                    }

                    //将当前拖动的高度回调
                    if (onTouchHeightListener != null) {
                        //需要列表处于到顶的状态
                        if (!recyclerView.canScrollVertically(-1) && moveHeight >= 1) {
                            onTouchHeightListener.onTouchHeight((int) moveHeight);
                        }
                        //需要列表处于到底的状态
                        if (!recyclerView.canScrollVertically(1) && moveHeight <= -1) {
                            onTouchHeightListener.onTouchHeight((int) moveHeight);
                        }
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
                isTouching = false;
                //判定是否是下拉动作并处于可以下拉刷新状态
                if (pullDownType == PULL_DOWN_TYPE_REFRESH && moveHeight > rlyHead.getHeight()) {
                    //达到了松开刷新的条件
                    setPullDownState(PULL_DOWN_STATE_3);
                } else if (pullUpType == PULL_UP_TYPE_LOAD_PULL && moveHeight * -1 > rlyFoot.getHeight()) {
                    //达到了松开加载的条件
                    setPullUpState(PULL_UP_STATE_3);
                } else {
                    //开始回移动画
                    moveViewAnimation(recyclerView, rectRlv.top);
                    moveViewAnimation(rlyHead, rlyHead.getTop() + rlyHead.getHeight(), 0, rectRlyHead);
                    moveViewAnimation(rlyFoot, rlyFoot.getTop() - rectRlyFoot.top, 0, rectRlyFoot);
                }
                //重置一些参数
                startY = 0;
                currentY = 0;
                break;
        }
        if (isPullRecyclerView) {
            return isPullRecyclerView;
        }
        return super.dispatchTouchEvent(event);
    }

    /**
     * 控件移动动画效果,从当前位置,移动到目标位置
     *
     * @param view  待移动的控件
     * @param stopY 移动到的目标位置
     */
    private void moveViewAnimation(View view, int stopY) {
        moveViewAnimation(view, view.getTop() - stopY, 0, new Rect(view.getLeft(), stopY, view.getRight(), stopY + view.getHeight()));
    }

    /**
     * 控件移动动画效果
     *
     * @param view   待移动的控件
     * @param startY 移动开始的位置(相对控件本身)
     * @param stopY  移动结束的位置(相对控件本身)
     * @param top    动画结束后的位置,即设置控件本身Top的位置
     * @param bottom 动画结束后的位置,即设置控件本身bottom的位置
     */
    private void moveViewAnimation(View view, int startY, int stopY, int top, int bottom) {
        moveViewAnimation(view, startY, stopY, new Rect(view.getLeft(), top, view.getRight(), bottom));
    }

    /**
     * 控件移动动画效果
     *
     * @param view   待移动的控件
     * @param startY 移动开始的位置(相对控件本身)
     * @param stopY  移动结束的位置(相对控件本身)
     * @param rect   动画结束后的位置,即设置控件本身的位置
     */
    private void moveViewAnimation(final View view, int startY, int stopY, Rect rect) {
        if (isTouching) {
            return;//如果还按压着屏幕,则不播放任何动画
        }
        TranslateAnimation animation = new TranslateAnimation(0.0f, 0.0f,
                startY, stopY);
        animation.setDuration(getAnimTime());//动画的持续时间
        animation.setFillAfter(true);
        //设置阻尼动画效果
        animation.setInterpolator(new DampInterpolator());
        view.setAnimation(animation);
        view.layout(rect.left, rect.top, rect.right, rect.bottom);
        recordLayoutRect();//记录位置,用于重绘
    }

    public class DampInterpolator implements Interpolator {
        @Override
        public float getInterpolation(float input) {
            //没看过源码,猜测是input是时间(0-1),返回值应该是进度(0-1)
            //先快后慢,为了更快更慢的效果,多乘了几次,现在这个效果比较满意
            return 1 - (1 - input) * (1 - input) * (1 - input) * (1 - input);
        }
    }

    public void requestLayout() {
        if (isRefreshing||isLoading) {
            //刷新recyclerView里面的数据的时候,会调用requestLayout()方法刷新控件,会导刷新刷新完数据之后没有三大部件显示动画就复位了,所以这里屏蔽掉requestLayout方法换成invalidate()刷新页面
            invalidate();
        } else {
            super.requestLayout();
        }
    }


    /**
     * 接口
     */
    public interface OnRefreshListener {
        void onRefresh();
    }

    public interface OnLoadListener {
        void onLoad();
    }

    public interface OnTouchHeightListener {
        void onTouchHeight(int num);
    }

}

2、源码(refresh_relative_layout.xml)

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/rly_bg"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <RelativeLayout
        android:id="@+id/rly_refresh_head"
        android:layout_width="match_parent"
        android:layout_height="60dp">

        <RelativeLayout
            android:id="@+id/rly_head_state_1"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <!--下拉但还未到头部的高度-->
            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:gravity="center"
                android:text="下拉刷新" />

        </RelativeLayout>

        <RelativeLayout
            android:id="@+id/rly_head_state_2"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:visibility="invisible">
            <!--下拉且超过了头部的高度,可松开刷新-->
            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:gravity="center"
                android:text="松开刷新" />

        </RelativeLayout>

        <RelativeLayout
            android:id="@+id/rly_head_state_3"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:visibility="invisible">
            <!--正在刷新-->
            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:gravity="center"
                android:text="正在刷新" />

        </RelativeLayout>

        <RelativeLayout
            android:id="@+id/rly_head_state_4"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:visibility="invisible">
            <!--刷新成功-->
            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:gravity="center"
                android:text="刷新成功" />

        </RelativeLayout>

    </RelativeLayout>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/rlv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:overScrollMode="never"
        android:scrollbars="none" />

    <RelativeLayout
        android:id="@+id/rly_refresh_foot"
        android:layout_width="match_parent"
        android:layout_height="60dp">

        <RelativeLayout
            android:id="@+id/rly_foot_state_1"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <!--下拉但还未到头部的高度-->
            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:gravity="center"
                android:text="上拉加载" />

        </RelativeLayout>

        <RelativeLayout
            android:id="@+id/rly_foot_state_2"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:visibility="invisible">
            <!--下拉且超过了头部的高度,可松开刷新-->
            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:gravity="center"
                android:text="松开加载" />

        </RelativeLayout>

        <RelativeLayout
            android:id="@+id/rly_foot_state_3"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:visibility="invisible">
            <!--正在加载-->
            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:gravity="center"
                android:text="正在加载" />

        </RelativeLayout>

        <RelativeLayout
            android:id="@+id/rly_foot_state_4"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:visibility="invisible">
            <!--刷新成功-->
            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:gravity="center"
                android:text="加载成功" />

        </RelativeLayout>

    </RelativeLayout>
</RelativeLayout>

 

最后,有些状态考虑的不一定周全,如果代码在使用的过程中出现了某些问题,可以在下方评论,也可以给我发邮件:652698011@qq.com

 

转载于:https://my.oschina.net/u/1462828/blog/1554536

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值