支持下拉刷新和上划加载更多的自定义RecyclerView(仿XListView效果)

本文介绍了如何使用自定义RecyclerView实现类似XListView的下拉刷新和上滑加载更多功能。通过监听触摸事件和滚动事件,实现弹性拖出、回弹效果,以及自动刷新和加载更多。同时,注意了头部和底部的代码分离,避免了滚动冲突。使用时需要继承内置的Adapter并实现刷新和加载监听器。
摘要由CSDN通过智能技术生成

首先看效果

下拉刷新:

      


上划加载

      

项目github地址:https://github.com/AlexZhuo/AlxRecyclerView

并且编译好的demo apk包也在github上


在项目更新的过程中,遇到了一个将XListView换成recyclerView的需求,而且更换完之后大体效果不能变,但是对于下拉刷新这样的效果,谷歌给出的解决方案是把RecyclerView放在一个SwipeRefreshLayout中,但是这样其实是拉下一个小圆形控件实现的,和XListView的header效果不同。在网上找了很多的别人代码,都没有实现我想要的效果,于是自己动手写了一个。

具体实现的效果有以下几条

下拉刷新功能:

1、实现一个有弹性的拖出效果:思路参考XListView,recyclerView的position=0的位置放一个header布局,这个布局的margin top默认为负的布局高度,所以这块布局就一直处于屏幕外部,在下拉的时候通过onTouchListener根据手指的移动动态修改margin top,慢慢的拖出,当拖出的距离也就是margin top变为正数以后,就盖面header布局的状态,改变箭头的方向并改变提示语


2、实现有弹性的回弹效果:用timerTask写了一个动态修改的header布局的margin top的动画,每隔一定的时间减小margin top的值,当用户松手的时候通过一个函数updateHeaderHeight()来执行这个动画。


3、实现用户非手动拖拉的自动刷新效果:这个recyclerView还有一个方法叫forceRefresh(),就是不需要用户手动下拉,头部自己滚动出来,然后刷新完再自己收回去,自动下拉也是用一个timerTask每隔十几毫秒增加margin top的值让头部慢慢露出来


上划加载更多功能:

1、实现滚动到底部自动停住效果: 有时候recyclerVIew滚动太快,滚到底部的时候会根据惯性向上飘,这个地方到底的时候监控recyclerView滚动速度,如果非常快说明是惯性滚动,就不修改footer布局的高度

2、实现向上拖动效果:复写了recyclerView的onScrollListener,在手指向上滚动的时候,通过updateFooterHeight()方法动态修改底部footerView的margin bottom,同headerView一样,在手指移动的时候让这个margin跟着变大,以增加footer布局的高度,而且手指移动的越网上,增加的margin的高度就越小,实现一个有弹性的上拉效果,防止误操作。

3、实现自动回弹的效果:通过监控footer布局的margin bottom来确定松手的时候是否需要开始刷新,如果margin bottom大于某个值得时候就修改footer布局的状态从normal变成ready,在ready状态下松手就开始刷新操作,回弹也像header布局一样通过一个timerTask每隔十几毫秒修改margin的大小来实现回弹效果


注意事项:

1、为了实现头部和底部的代码分离,头部用的是onTouchListener,底部用的是onScrollListener

2、本recyclerVIew里面已经内置了一个layoutManager,所以不要给recyclerView再设置layoutManager,否则会出现头部不出来,下拉报空指针的情况,底部出现但是滑动没有效果

3、这个recyclerView内置了一个抽象类作为adapter,请继承这个内置的AlxDragRecyclerViewAdapter使用,或者按照这里面的逻辑重新写adapter

有其他的问题欢迎问我

4、一些常用的功能,比如设置该控件是否能够支持下拉加载和上拉刷新,等等api接口,请直接参考XListView的用法即可


使用方法:

继承AlxDragRecyclerViewAdapter写一个adapter,然后写两个类分别实现OnRefreshListener和LoadMoreListener,把具体的刷新逻辑写在里面,在准备好显示数据后调用adapter的notifyDataSetChanged()方法或notifyItemInserted()方法,并执行该recyclerView的stopLoadmore()方法和stopRefresh()方法。


下面是代码,这个控件有很多的内部类,包括头部,底部的布局控件(CustomDragHeaderView,CustomDragFooterView),adapter,layoutmanager都已经以内部类的方式集成在里面,减少迁移时候的复杂度

import android.content.Context;
import android.graphics.Color;
import android.os.Handler;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.xxx.app.R;

import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

/**
 * Created by Alex on 2016/1/27.
 */
public class AlxRefreshLoadMoreRecyclerView extends RecyclerView {
    private int footerHeight = -1;
    LinearLayoutManager layoutManager;
    // -- footer view
    private CustomDragRecyclerFooterView mFooterView;
    private boolean mEnablePullLoad;
    private boolean mPullLoading;
    private boolean isBottom;
    private boolean mIsFooterReady = false;
    private LoadMoreListener loadMoreListener;

    // -- header view
    private CustomDragHeaderView mHeaderView;
    private boolean mEnablePullRefresh = true;
    private boolean mIsRefreshing;
    private boolean isHeader;
    private boolean mIsHeaderReady = false;
    private Timer timer;
    private float oldY;
    Handler handler = new Handler();
    private OnRefreshListener refreshListener;
    private AlxDragRecyclerViewAdapter adapter;
    private int maxPullHeight = 50;//最多下拉高度的px值

    private static final int HEADER_HEIGHT = 68;//头部高度68dp
    private static final int MAX_PULL_LENGTH = 150;//最多下拉150dp
    private OnClickListener footerClickListener;


    public AlxRefreshLoadMoreRecyclerView(Context context) {
        super(context);
        initView(context);
    }

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

    public AlxRefreshLoadMoreRecyclerView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initView(context);
    }

    public void setAdapter(AlxDragRecyclerViewAdapter adapter){
        super.setAdapter(adapter);
        this.adapter = adapter;
    }

    public boolean ismPullLoading() {
        return mPullLoading;
    }

    public boolean ismIsRefreshing() {
        return mIsRefreshing;
    }

    private void updateFooterHeight(float delta) {
        if(mFooterView==null)return;
        int bottomMargin = mFooterView.getBottomMargin();
//        Log.i("Alex3","初始delta是"+delta);
        if(delta>50)delta = delta/6;
        if(delta>0) {//越往下滑越难滑
            if(bottomMargin>maxPullHeight)delta = delta*0.65f;
            else if(bottomMargin>maxPullHeight * 0.83333f)delta = delta*0.7f;
            else if(bottomMargin>maxPullHeight * 0.66667f)delta = delta*0.75f;
            else if(bottomMargin>maxPullHeight >> 1)delta = delta*0.8f;
            else if(bottomMargin>maxPullHeight * 0.33333f)delta = delta*0.85f;
            else if(bottomMargin>maxPullHeight * 0.16667F && delta > 20)delta = delta*0.2f;//如果是因为惯性向下迅速的俯冲
            else if(bottomMargin>maxPullHeight * 0.16667F)delta = delta*0.9f;
//            Log.i("Alex3","bottomMargin是"+mFooterView.getBottomMargin()+" delta是"+delta);
        }

        int height = mFooterView.getBottomMargin() + (int) (delta+0.5);

        if (mE
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值