基于RecyclerView通用适配自定义下拉刷新

天气热,人也变的懒散了,晚上如汗蒸,睡眠很不好,精神状态也不好。

一、前言

自从使用了RecyclerView,就爱上了它,灵活性非常的强大,效果绚丽,如果想进一步了解,请关注:

RecyclerView 之通用适配

在通用适配篇中使用的SwipeRefreshLayout实现的下拉刷新功能,本篇带大家实现自定义的下拉刷新,先来啾啾效果图:

pull

PullRefreshLoadView(下拉刷新)

PullRefreshLoadView下拉控件有4个状态,分别是:

  • STATE_PULL_TO_REFRESH 下拉刷新状态

  • STATE_RELEASE_TO_REFRESH 释放立即刷新状态

  • STATE_REFRESHING 正在刷新状态

  • STATE_REFRESHED 刷新完成状态

实现的原理:重写RecyclerViewonTouchEvent方法,获取getRawY差值动态计算PullRefreshLoadView的高度。

原理还是比较简单的,但这里有2点你需要注意:

1、setVisibility(View.INVISIBLE)无效,在刷新状态的时需要对箭头图标进行隐藏,发现View.INVISIBLE不管用,这个就纠结了,网上几乎找不到这方面的问题,后来看到一篇文章就说加一句clearAnimation();靠,还真解决了:

mArrowIv.clearAnimation();
mArrowIv.setVisibility(View.INVISIBLE);

2、无限循环动画的匿名updatelistener里面直接执行invalidate方法,由于对view的强引用,导致view无法被正常回收,进一步导致viewcontextactivity无法被回收,最终产生内存泄露。解决方案就是重写一个updatelistener,在构造函数中实现对view的弱引用:

private static class MyUpdateListener implements ValueAnimator.AnimatorUpdateListener {
    private WeakReference<BallSpinFadeLoader> mWeakReference;
    public MyUpdateListener(BallSpinFadeLoader ballSpinFadeLoader) {
        mWeakReference = new WeakReference<BallSpinFadeLoader>(ballSpinFadeLoader);
    }
    @Override
    public void onAnimationUpdate(ValueAnimator valueAnimator) {
        BallSpinFadeLoader ball = mWeakReference.get();
        if (ball == null) {
            return;
        }
    }
}

下面是PullRefreshLoadView源码:

package com.github.baserecycleradapter.widget;

import android.animation.ValueAnimator;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.github.baserecycleradapter.R;

/**
 * Created by Administrator on 8/24 0024.
 */
public class PullRefreshLoadView extends LinearLayout {

    private Context mContext; //上下文

    private LinearLayout mContainer;

    private ImageView mArrowIv;  //箭头

    private TextView mStatusTv;  //下拉刷新  释放刷新  刷新完成

    private BallSpinFadeLoader mLoader;  //loading 图标

    private int mState;     //状态

    private int mHeight;  //高度

    private Animation mArrowDownAnim;//向下动画
    private Animation mArrowUpAnim;  //向上动画

    public final static int STATE_PULL_TO_REFRESH = 0;
    public final static int STATE_RELEASE_TO_REFRESH = 1;
    public final static int STATE_REFRESHING = 2;
    public final static int STATE_REFRESHED = 3;

    public final static String PULL_TO_REFRESH = "下拉刷新";
    public final static String RELEASE_TO_REFRESH = "释放立即刷新";
    public final static String REFRESHING = "正在刷新...";
    public final static String REFRESHED = "刷新完成";

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

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

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

    private void initView(Context context) {
        mContext = context;
        mState = STATE_PULL_TO_REFRESH;
        mContainer = (LinearLayout) LayoutInflater.from(getContext()).inflate(
                R.layout.rv_refresh, null);
        LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
        lp.setMargins(0, 0, 0, 0);
        setLayoutParams(lp);
        setPadding(0, 0, 0, 0);

        addView(mContainer, new LayoutParams(LayoutParams.MATCH_PARENT, 0));
        setGravity(Gravity.BOTTOM);

        mArrowIv = (ImageView) mContainer.findViewById(R.id.iv_arrow);
        mStatusTv = (TextView) mContainer.findViewById(R.id.tv_refresh_status);
        mLoader = (BallSpinFadeLoader) mContainer.findViewById(R.id.ball_loader);

        mArrowDownAnim = new RotateAnimation(180.0f, 0.0f,
                Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        mArrowDownAnim.setDuration(180);
        mArrowDownAnim.setFillAfter(true);

        mArrowUpAnim = new RotateAnimation(0.0f, 180.0f,
                Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        mArrowUpAnim.setDuration(180);
        mArrowUpAnim.setFillAfter(true);

        measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        mHeight = getMeasuredHeight();
    }


    /**
     * @param resId
     */
    public void setArrowImageView(int resId) {
        if (mArrowIv != null) {
            mArrowIv.setImageResource(resId);
        }
    }

    public int getState() {
        return mState;
    }

    public int getVisibleHeight() {
        LayoutParams lp = (LayoutParams) mContainer.getLayoutParams();
        return lp.height;
    }


    public void setVisibleHeight(int height) {
        if (height <= 0)
            height = 0;
        LayoutParams lp = (LayoutParams) mContainer.getLayoutParams();
        lp.height = height;
        mContainer.setLayoutParams(lp);
    }

    public void refreshComplete() {
        setState(STATE_REFRESHED);
        reset();
    }

    /**
     * @param delta
     */
    public void onMove(float delta) {
        if (getVisibleHeight() > 0 || delta > 0) {
            setVisibleHeight((int) (delta + getVisibleHeight()));
            if (mState <= STATE_RELEASE_TO_REFRESH) {
                if (getVisibleHeight() > mHeight) {
                    setState(STATE_RELEASE_TO_REFRESH);
                } else {
                    setState(STATE_PULL_TO_REFRESH);
                }
            }
        }
    }

    public boolean releaseAction() {
        boolean isOnRefresh = false;
        int height = getVisibleHeight();
        int destHeight = 0;
        if (height == 0) isOnRefresh = false;

        if (mState == STATE_REFRESHING) {
            destHeight = mHeight;
        }

        if (height > mHeight && mState < STATE_REFRESHING) {
            setState(STATE_REFRESHING);
            destHeight = mHeight;
            isOnRefresh = true;
        }

        smoothScrollTo(destHeight);

        return isOnRefresh;
    }

    public void reset() {
        smoothScrollTo(0);
        postDelayed(new Runnable() {
            @Override
            public void run() {
                setState(STATE_PULL_TO_REFRESH);
            }
        }, 200);
    }

    /**
     * @param state
     */
    public void setState(int state) {
        if (state == mState)
            return;
        if (state == STATE_REFRESHING) {
            if (!mLoader.isLoading()) {
                mLoader.startAnimator();
            }
            mArrowIv.clearAnimation();
            mStatusTv.setText(REFRESHING);
            mLoader.setVisibility(View.VISIBLE);
            mArrowIv.setVisibility(View.INVISIBLE);
        } else if (state == STATE_PULL_TO_REFRESH) {
            mArrowIv.setVisibility(View.VISIBLE);
            mLoader.setVisibility(View.INVISIBLE);
            mStatusTv.setText(PULL_TO_REFRESH);
            if (mState == STATE_RELEASE_TO_REFRESH) {
                mArrowIv.startAnimation(mArrowDownAnim);
            } else if (mState == STATE_REFRESHING) {
                mArrowIv.clearAnimation();
            }
        } else if (state == STATE_RELEASE_TO_REFRESH) {
            mArrowIv.setVisibility(View.VISIBLE);
            mLoader.setVisibility(View.INVISIBLE);
            mStatusTv.setText(RELEASE_TO_REFRESH);
            if (mState != STATE_RELEASE_TO_REFRESH) {
                mArrowIv.clearAnimation();
                mArrowIv.startAnimation(mArrowUpAnim);
            }
        } else if (state == STATE_REFRESHED) {
            mArrowIv.clearAnimation();
            mStatusTv.setText(REFRESHED);
            mArrowIv.setVisibility(View.INVISIBLE);
            mLoader.setVisibility(View.INVISIBLE);
        }
        mState = state;
    }

    /**
     * @param destHeight
     */
    private void smoothScrollTo(int destHeight) {
        ValueAnimator animator = ValueAnimator.ofInt(getVisibleHeight(), destHeight);
        animator.setDuration(300).start();
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                setVisibleHeight((int) animation.getAnimatedValue());
            }
        });
        animator.start();
    }

}

接着来看看RecyclerViewonTouchEvent方法:

@Override
public boolean onTouchEvent(MotionEvent e) {
    if (mLastY == -1) {
        mLastY = e.getRawY();
    }
    switch (e.getAction()) {
        case MotionEvent.ACTION_DOWN:
            mLastY = e.getRawY();
            break;
        case MotionEvent.ACTION_MOVE:
            if (isScrollTop()) {
                float deltaY = e.getRawY() - mLastY;
                mPullRefreshLoadView.onMove(deltaY / 3);
                mLastY = e.getRawY();
                if (mPullRefreshLoadView.getVisibleHeight() > 0 &&
                        mPullRefreshLoadView.getState() < mPullRefreshLoadView.STATE_REFRESHING) {
                    return false;
                }
            }
            break;
        default:
        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_CANCEL:
        case MotionEvent.ACTION_OUTSIDE:
            mLastY = -1; // reset
            if (mPullRefreshLoadView.releaseAction()) {
                if (mCompleteListener != null) {
                    mCompleteListener.onRefreshComplete();
                }
            }
            break;
    }
    return super.onTouchEvent(e);
}

源码地址,如果你喜欢,还请 star

  • 5
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值