天气热,人也变的懒散了,晚上如汗蒸,睡眠很不好,精神状态也不好。
一、前言
自从使用了RecyclerView
,就爱上了它,灵活性非常的强大,效果绚丽,如果想进一步了解,请关注:
在通用适配篇中使用的SwipeRefreshLayout
实现的下拉刷新功能,本篇带大家实现自定义的下拉刷新,先来啾啾效果图:
PullRefreshLoadView(下拉刷新)
PullRefreshLoadView
下拉控件有4个状态,分别是:
STATE_PULL_TO_REFRESH 下拉刷新状态
STATE_RELEASE_TO_REFRESH 释放立即刷新状态
STATE_REFRESHING 正在刷新状态
STATE_REFRESHED 刷新完成状态
实现的原理:重写RecyclerView
的onTouchEvent
方法,获取getRawY
差值动态计算PullRefreshLoadView
的高度。
原理还是比较简单的,但这里有2点你需要注意:
1、setVisibility(View.INVISIBLE)
无效,在刷新状态的时需要对箭头图标进行隐藏,发现View.INVISIBLE
不管用,这个就纠结了,网上几乎找不到这方面的问题,后来看到一篇文章就说加一句clearAnimation();
靠,还真解决了:
mArrowIv.clearAnimation();
mArrowIv.setVisibility(View.INVISIBLE);
2、无限循环动画的匿名updatelistener
里面直接执行invalidate
方法,由于对view
的强引用,导致view
无法被正常回收,进一步导致view
的context
的activity
无法被回收,最终产生内存泄露。解决方案就是重写一个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();
}
}
接着来看看RecyclerView
的onTouchEvent
方法:
@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