源码下载地址:http://download.csdn.net/detail/zhou_anzhuojinjie/9661542

话不多说直接上代码


dependencies {compile fileTree(include: ['*.jar'], dir: 'libs')androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {exclude group: 'com.android.support', module: 'support-annotations'})compile 'com.android.support:appcompat-v7:24.2.1'testCompile 'junit:junit:4.12'compile 'com.android.support:recyclerview-v7:24.2.1'
}

PullRefreshLayout

package com.linzhou.recyclerviewfresh.refresh;import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.LayerDrawable;
import android.support.annotation.ColorRes;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.DecelerateInterpolator;
import android.widget.ImageView;
import android.widget.RelativeLayout;/*** Created by joe on 2016/10/23.*/
public class PullRefreshLayout extends RelativeLayout {private int[] colors = {0xFFFF0000, 0xFFFF7F00, 0xFFFFFF00, 0xFF00FF00, 0xFF00FFFF, 0xFF0000FF, 0xFF8B00FF};private final int CIRCLE_BG_LIGHT = 0xFFFAFAFA;private MaterialProgressDrawable mProgress;private ValueAnimator mValueAnimator;private boolean mStart = false;private boolean mVisable = false;private static final int PULL_IMAGE_SIZE = 40;private static int PULL_IMAGE_SIZE_PX;//上拉View的大小(像素)private static int PULL_IMAGE_SIZE_PX_MAX ;//最大拉动的距离private static int PULL_IMAGE_SIZE_PX_EXECUTE ;//拉动到什么位置开始执行private static int PULL_IMAGE_SIZE_PX_EXECUTE_REFRESH ;//刷新是所在的位置private static float ROTATE_ANIM_ANGLE_PER;//根据最大距离计算旋转角度的比列// private static finalprivate ImageView mImageView;private boolean mIsFirst;private int mStartY, mLastY;private RecyclerView mRecyclerView;//private int mFirstVisiblePosition;private int mLastVisiblePosition;private boolean mIsCanScoll;private boolean mVisibleCanScoll;private boolean mPrepareAnimation;//准备执行动画private boolean mIsAllowLoadMore = true;//是否可以上拉刷新private boolean mIsDispatch = true;//是否分发事件public PullRefreshLayout(Context context) {this(context, null);}public PullRefreshLayout(Context context, AttributeSet attrs) {this(context, attrs, 0);}public PullRefreshLayout(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}/*** 是否允许下拉加载更多* @param allowLoadMore*/public void setAllowLoadMore(boolean allowLoadMore) {mIsAllowLoadMore = allowLoadMore;}public boolean isAllowLoadMore() {return mIsAllowLoadMore;}/*** 设置进度圈的颜色* @param colors 如:0xFFFF0000*/public void setColorSchemeColors(int... colors){this.colors = colors;}/*** 设置进度圈的颜色* @param colorResIds 如:R.color.red*/public void setColorSchemeResources(@ColorRes int... colorResIds) {final Resources res = getResources();int[] colorRes = new int[colorResIds.length];for (int i = 0; i < colorResIds.length; i++) {colorRes[i] = res.getColor(colorResIds[i]);}setColorSchemeColors(colorRes);}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {super.onLayout(changed, l, t, r, b);if (!mIsFirst) {createProgressView();over:for (int i = 0; i < getChildCount(); i++) {View childView = getChildAt(i);if (childView instanceof SwipeRefreshLayout) {ViewGroup viewGroup = (ViewGroup) childView;for (int j = 0; j < viewGroup.getChildCount(); j++) {View childViewJ = viewGroup.getChildAt(j);if (childViewJ instanceof RecyclerView) {mRecyclerView = (RecyclerView) childViewJ;break over;}}}if(childView instanceof RecyclerView){mRecyclerView = (RecyclerView) childView;break over;}}mIsFirst = true;}}@Overridepublic boolean dispatchTouchEvent(MotionEvent event) {if(!mIsAllowLoadMore) return super.dispatchTouchEvent(event);switch (event.getAction()) {case MotionEvent.ACTION_DOWN:mStartY = (int) event.getRawY();break;case MotionEvent.ACTION_MOVE:if (!mStart) {//如果不满足上拉的条件就直接分发事件if(!canPullUp()){return super.dispatchTouchEvent(event);}if (canPullUp() && !mIsCanScoll) {showRefreshArrow();mStartY = (int) event.getRawY();mIsCanScoll = true;} else {//mStartY = (int) event.getRawY();//hideRefreshArrow();//hide();}if (mVisibleCanScoll) {int endY = (int) event.getRawY();int offset = mStartY - endY;//System.out.println("----------------------offset:" + offset);LayoutParams lp = (LayoutParams) mImageView.getLayoutParams();int bottomMargin = lp.bottomMargin;bottomMargin += offset;if (bottomMargin >= PULL_IMAGE_SIZE_PX_MAX) {bottomMargin = PULL_IMAGE_SIZE_PX_MAX;}if (bottomMargin <= -PULL_IMAGE_SIZE_PX) {bottomMargin = -PULL_IMAGE_SIZE_PX;}lp.setMargins(lp.leftMargin, lp.topMargin, lp.rightMargin, bottomMargin);mImageView.setLayoutParams(lp);rotateAniamtor(bottomMargin * ROTATE_ANIM_ANGLE_PER);mStartY = endY;}LayoutParams lp = (LayoutParams) mImageView.getLayoutParams();//如果按住上拉时,上拉箭头向下滑动的时候事件不应分发if(mVisable && lp.bottomMargin > -PULL_IMAGE_SIZE_PX){mIsDispatch = false;}else if(mVisable && lp.bottomMargin == -PULL_IMAGE_SIZE_PX){//等到上拉箭头被隐藏掉的时候在分发事件mIsDispatch = true;}//是否分发事件if(!mIsDispatch) {return false;}else {return super.dispatchTouchEvent(event);}}break;case MotionEvent.ACTION_UP:case MotionEvent.ACTION_CANCEL:if (!mStart) {if (mVisibleCanScoll) {LayoutParams lp = (LayoutParams) mImageView.getLayoutParams();if (lp.bottomMargin >= PULL_IMAGE_SIZE_PX_EXECUTE) {//lp.setMargins(lp.leftMargin, lp.topMargin, lp.rightMargin, PULL_IMAGE_SIZE_PX / 3 * 2);//mImageView.setLayoutParams(lp);//start();getValueToTranslation();mPrepareAnimation = true;if (mOnPullListener != null) {mOnPullListener.onLoadMore(this);}} else {hideArrow();}}if (!mStart && !mPrepareAnimation)hideArrow();}mIsCanScoll = false;break;}return super.dispatchTouchEvent(event);}private void hideArrow() {LayoutParams lp = (LayoutParams) mImageView.getLayoutParams();//lp.setMargins(lp.leftMargin, lp.topMargin, lp.rightMargin, -PULL_IMAGE_SIZE_PX);//mImageView.setLayoutParams(lp);translationTo(lp.bottomMargin,-PULL_IMAGE_SIZE_PX,false);}private void showRefreshArrow() {mImageView.setVisibility(View.VISIBLE);visable();}/*** 隐藏箭头显示的载体ImageView*/private void hideRefreshArrow() {mImageView.setVisibility(View.GONE);}private boolean canPullUp() {if(mRecyclerView == null || mRecyclerView.getAdapter() == null) return false;RecyclerView.LayoutManager lm = mRecyclerView.getLayoutManager();mLastVisiblePosition = getLastVisibleItemPosition();int count = mRecyclerView.getAdapter().getItemCount();if (0 == count) {// 没有item的时候也可以上拉加载return true;} else if (mLastVisiblePosition == (count - 1)) {// 滑到底部了if (lm.findViewByPosition(count - 1).getBottom() <= getMeasuredHeight()) {return true;}}return false;}/*** 获取底部可见项的位置** @return*/private int getLastVisibleItemPosition() {RecyclerView.LayoutManager lm = mRecyclerView.getLayoutManager();int lastVisibleItemPosition = 0;if (lm instanceof GridLayoutManager) {lastVisibleItemPosition = ((GridLayoutManager) lm).findLastVisibleItemPosition();} else if (lm instanceof LinearLayoutManager) {lastVisibleItemPosition = ((LinearLayoutManager) lm).findLastVisibleItemPosition();}return lastVisibleItemPosition;}/*** 创建刷新View和初始化一些数据*/private void createProgressView() {mImageView = new ImageView(getContext());int size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, PULL_IMAGE_SIZE, getContext().getResources().getDisplayMetrics());PULL_IMAGE_SIZE_PX = size;PULL_IMAGE_SIZE_PX_MAX = PULL_IMAGE_SIZE_PX * 2;PULL_IMAGE_SIZE_PX_EXECUTE = PULL_IMAGE_SIZE_PX;PULL_IMAGE_SIZE_PX_EXECUTE_REFRESH = PULL_IMAGE_SIZE_PX / 3 * 2;ROTATE_ANIM_ANGLE_PER = (360.0f / PULL_IMAGE_SIZE_PX_MAX);LayoutParams lp = new LayoutParams(size, size);lp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);lp.addRule(RelativeLayout.CENTER_HORIZONTAL);lp.setMargins(lp.leftMargin, lp.topMargin, lp.rightMargin, -PULL_IMAGE_SIZE_PX);mImageView.setLayoutParams(lp);mImageView.setBackground(getShapeDrawable());addView(mImageView);mImageView.setVisibility(View.GONE);mProgress = new MaterialProgressDrawable(getContext(), mImageView);mProgress.setBackgroundColor(CIRCLE_BG_LIGHT);//圈圈颜色,可以是多种颜色mProgress.setColorSchemeColors(colors);//设置圈圈的各种大小mProgress.updateSizes(MaterialProgressDrawable.LARGE);mImageView.setImageDrawable(mProgress);}/*** mImageView的背景*/private Drawable getShapeDrawable() {/*** <layer-list xmlns:android="http://schemas.android.com/apk/res/android" ><!-- 第一层  上部和左部偏移一定距离--><item><shape android:shape="oval"><solid android:color="#f5f5f5" /><!-- 描边 --><strokeandroid:width="0.5dp"android:color="#99f5f5f5" /></shape></item><!-- 第二层 下部和有部偏移一定距离--><itemandroid:left="2dp"android:top="2dp"android:bottom="2dp"android:right="2dp"><shape android:shape="oval"><solid android:color="#ffffff" /><!-- 描边 --><!--<stroke android:width="0.33dp" android:color="#dedede" />--></shape></item></layer-list>*///代码实现GradientDrawable gradientDrawable = new GradientDrawable();gradientDrawable.setShape(GradientDrawable.OVAL);gradientDrawable.setColor(Color.parseColor("#f5f5f5"));int stroke = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 0.5f, getContext().getResources().getDisplayMetrics());gradientDrawable.setStroke(stroke,Color.parseColor("#99f5f5f5"));GradientDrawable gradientDrawable2 = new GradientDrawable();gradientDrawable2.setShape(GradientDrawable.OVAL);gradientDrawable2.setColor(Color.parseColor("#ffffff"));LayerDrawable drawable = new LayerDrawable(new Drawable[]{gradientDrawable,gradientDrawable2});int padding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, getContext().getResources().getDisplayMetrics());drawable.setLayerInset(1,padding,padding,padding,padding);第一个参数1代表数组的第二个元素,为白色return drawable;}/*** 隐藏箭头*/private void hide() {if (mValueAnimator != null) {mValueAnimator.cancel();mVisable = false;mVisibleCanScoll = false;}}private void visable() {if (mValueAnimator == null) {mValueAnimator = mValueAnimator.ofFloat(0f, 1f);mValueAnimator.setDuration(10);mValueAnimator.setInterpolator(new DecelerateInterpolator());mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {float n = (float) animation.getAnimatedValue();//圈圈的旋转角度mProgress.setProgressRotation(n * 0.5f);//圈圈周长,0f-1FmProgress.setStartEndTrim(0f, n * 0.8f);//箭头大小,0f-1FmProgress.setArrowScale(n);//透明度,0-255mProgress.setAlpha((int) (255 * n));}});mValueAnimator.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationEnd(Animator animation) {super.onAnimationEnd(animation);mVisable = true;}});}if (!mValueAnimator.isRunning()) {if (!mVisable) {//是否显示箭头mProgress.showArrow(true);mValueAnimator.start();mVisibleCanScoll = true;}}}private void start() {if (mVisable) {if (!mStart) {mProgress.start();mStart = true;}}}/*** 计算执行动画的距离参数*/private void getValueToTranslation() {//如果mImageView还没有被创建出来是不会执行的if(mImageView != null) {LayoutParams lp = (LayoutParams) mImageView.getLayoutParams();int bottomMargin = lp.bottomMargin;//执行平移translationTo(bottomMargin, PULL_IMAGE_SIZE_PX_EXECUTE_REFRESH, true);}}private void stop() {if (mStart) {mProgress.stop();mStart = false;mVisable = false;mVisibleCanScoll = false;}}/*** 执行平移动画* @param from* @param to*/private void translationTo(int from,int to,final boolean isShow){//1.调用ofInt(int...values)方法创建ValueAnimator对象ValueAnimator mAnimator = ValueAnimator.ofInt(from,to);//2.为目标对象的属性变化设置监听器mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {// 3.为目标对象的属性设置计算好的属性值int animatorValue = (int)animation.getAnimatedValue();MarginLayoutParams marginLayoutParams = (MarginLayoutParams) mImageView.getLayoutParams();marginLayoutParams.bottomMargin = animatorValue;mImageView.setLayoutParams(marginLayoutParams);}});mAnimator.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationEnd(Animator animation) {super.onAnimationEnd(animation);if(isShow){start();mPrepareAnimation = false;}else{hideRefreshArrow();hide();}}});//4.设置动画的持续时间、是否重复及重复次数等属性mAnimator.setDuration(100);//mAnimator.setRepeatCount(3);mAnimator.setRepeatMode(ValueAnimator.INFINITE);//5.为ValueAnimator设置目标对象并开始执行动画mAnimator.setTarget(mImageView);mAnimator.start();}/*** 旋转动画效果*/private void rotateAniamtor(float from){ObjectAnimator mAnimatorRotate = ObjectAnimator.ofFloat(mImageView, "rotation",from,from + 1);mAnimatorRotate.setRepeatMode(Animation.INFINITE);mAnimatorRotate.setRepeatCount(1);mAnimatorRotate.setDuration(10);mAnimatorRotate.start();}/*** 加载更多或停止加载更多* @param refreshing*/public void setRefreshing(boolean refreshing) {if(!mIsAllowLoadMore) return;if(refreshing){if(mStart) return;showRefreshArrow();getValueToTranslation();mPrepareAnimation = true;if (mOnPullListener != null) {mOnPullListener.onLoadMore(this);}mIsCanScoll = false;}else {stop();hideArrow();}}/*** 当前是否在上拉刷新* @return*/public boolean isRefreshing(){return mStart;}/*** 刷新加载回调接口** @author chenjing*/public interface OnPullListener {/*** 加载操作*/void onLoadMore(PullRefreshLayout pullRefreshLayout);}private OnPullListener mOnPullListener;public void setOnPullListener(OnPullListener listener) {mOnPullListener = listener;}}

MaterialProgressDrawable

package com.linzhou.recyclerviewfresh.refresh;import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.v4.view.animation.FastOutSlowInInterpolator;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import android.view.animation.Transformation;import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
/*** Created by joe on 2016/10/23.*/
public class MaterialProgressDrawable extends Drawable implements Animatable {private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator();private static final Interpolator MATERIAL_INTERPOLATOR = new FastOutSlowInInterpolator();private static final float FULL_ROTATION = 1080.0f;@Retention(RetentionPolicy.CLASS)@IntDef({LARGE, DEFAULT})public @interface ProgressDrawableSize {}// Maps to ProgressBar.Large stylepublic static final int LARGE = 0;// Maps to ProgressBar default stylepublic static final int DEFAULT = 1;// Maps to ProgressBar default styleprivate static final int CIRCLE_DIAMETER = 40;private static final float CENTER_RADIUS = 8.75f; //should add up to 10 when + stroke_widthprivate static final float STROKE_WIDTH = 2.5f;// Maps to ProgressBar.Large styleprivate static final int CIRCLE_DIAMETER_LARGE = 56;private static final float CENTER_RADIUS_LARGE = 12.5f;private static final float STROKE_WIDTH_LARGE = 3f;private final int[] COLORS = new int[] {Color.BLACK};/*** The value in the linear interpolator for animating the drawable at which* the color transition should start*/private static final float COLOR_START_DELAY_OFFSET = 0.75f;private static final float END_TRIM_START_DELAY_OFFSET = 0.5f;private static final float START_TRIM_DURATION_OFFSET = 0.5f;/** The duration of a single progress spin in milliseconds. */private static final int ANIMATION_DURATION = 1332;/** The number of points in the progress "star". */private static final float NUM_POINTS = 5f;/** The list of animators operating on this drawable. */private final ArrayList<Animation> mAnimators = new ArrayList<>();/** The indicator ring, used to manage animation state. */private final Ring mRing;/** Canvas rotation in degrees. */private float mRotation;/** Layout info for the arrowhead in dp */private static final int ARROW_WIDTH = 10;private static final int ARROW_HEIGHT = 5;private static final float ARROW_OFFSET_ANGLE = 5;/** Layout info for the arrowhead for the large spinner in dp */private static final int ARROW_WIDTH_LARGE = 12;private static final int ARROW_HEIGHT_LARGE = 6;private static final float MAX_PROGRESS_ARC = .8f;private Resources mResources;private View mParent;private Animation mAnimation;private float mRotationCount;private double mWidth;private double mHeight;boolean mFinishing;public MaterialProgressDrawable(Context context, View parent) {mParent = parent;mResources = context.getResources();mRing = new Ring(mCallback);mRing.setColors(COLORS);updateSizes(DEFAULT);setupAnimators();}private void setSizeParameters(double progressCircleWidth, double progressCircleHeight,double centerRadius, double strokeWidth, float arrowWidth, float arrowHeight) {final Ring ring = mRing;final DisplayMetrics metrics = mResources.getDisplayMetrics();final float screenDensity = metrics.density;mWidth = progressCircleWidth * screenDensity;mHeight = progressCircleHeight * screenDensity;ring.setStrokeWidth((float) strokeWidth * screenDensity);ring.setCenterRadius(centerRadius * screenDensity);ring.setColorIndex(0);ring.setArrowDimensions(arrowWidth * screenDensity, arrowHeight * screenDensity);ring.setInsets((int) mWidth, (int) mHeight);}public void updateSizes(@ProgressDrawableSize int size) {if (size == LARGE) {setSizeParameters(CIRCLE_DIAMETER_LARGE, CIRCLE_DIAMETER_LARGE, CENTER_RADIUS_LARGE,STROKE_WIDTH_LARGE, ARROW_WIDTH_LARGE, ARROW_HEIGHT_LARGE);} else {setSizeParameters(CIRCLE_DIAMETER, CIRCLE_DIAMETER, CENTER_RADIUS, STROKE_WIDTH,ARROW_WIDTH, ARROW_HEIGHT);}}/*** @param show Set to true to display the arrowhead on the progress spinner.*/public void showArrow(boolean show) {mRing.setShowArrow(show);}/*** @param scale Set the scale of the arrowhead for the spinner.*/public void setArrowScale(float scale) {mRing.setArrowScale(scale);}/*** Set the start and end trim for the progress spinner arc.** @param startAngle start angle* @param endAngle end angle*/public void setStartEndTrim(float startAngle, float endAngle) {mRing.setStartTrim(startAngle);mRing.setEndTrim(endAngle);}/*** Set the amount of rotation to apply to the progress spinner.** @param rotation Rotation is from [0..1]*/public void setProgressRotation(float rotation) {mRing.setRotation(rotation);}/*** Update the background color of the circle image view.*/public void setBackgroundColor(int color) {mRing.setBackgroundColor(color);}/*** Set the colors used in the progress animation from color resources.* The first color will also be the color of the bar that grows in response* to a user swipe gesture.** @param colors*/public void setColorSchemeColors(int... colors) {mRing.setColors(colors);mRing.setColorIndex(0);}@Overridepublic int getIntrinsicHeight() {return (int) mHeight;}@Overridepublic int getIntrinsicWidth() {return (int) mWidth;}@Overridepublic void draw(Canvas c) {final Rect bounds = getBounds();final int saveCount = c.save();c.rotate(mRotation, bounds.exactCenterX(), bounds.exactCenterY());mRing.draw(c, bounds);c.restoreToCount(saveCount);}@Overridepublic void setAlpha(int alpha) {mRing.setAlpha(alpha);}public int getAlpha() {return mRing.getAlpha();}@Overridepublic void setColorFilter(ColorFilter colorFilter) {mRing.setColorFilter(colorFilter);}@SuppressWarnings("unused")void setRotation(float rotation) {mRotation = rotation;invalidateSelf();}@SuppressWarnings("unused")private float getRotation() {return mRotation;}@Overridepublic int getOpacity() {return PixelFormat.TRANSLUCENT;}@Overridepublic boolean isRunning() {final ArrayList<Animation> animators = mAnimators;final int N = animators.size();for (int i = 0; i < N; i++) {final Animation animator = animators.get(i);if (animator.hasStarted() && !animator.hasEnded()) {return true;}}return false;}@Overridepublic void start() {mAnimation.reset();mRing.storeOriginals();// Already showing some part of the ringif (mRing.getEndTrim() != mRing.getStartTrim()) {mFinishing = true;mAnimation.setDuration(ANIMATION_DURATION/2);mParent.startAnimation(mAnimation);} else {mRing.setColorIndex(0);mRing.resetOriginals();mAnimation.setDuration(ANIMATION_DURATION);mParent.startAnimation(mAnimation);}}@Overridepublic void stop() {mParent.clearAnimation();setRotation(0);mRing.setShowArrow(false);mRing.setColorIndex(0);mRing.resetOriginals();}private float getMinProgressArc(Ring ring) {return (float) Math.toRadians(ring.getStrokeWidth() / (2 * Math.PI * ring.getCenterRadius()));}// Adapted from ArgbEvaluator.javaprivate int evaluateColorChange(float fraction, int startValue, int endValue) {int startInt = (Integer) startValue;int startA = (startInt >> 24) & 0xff;int startR = (startInt >> 16) & 0xff;int startG = (startInt >> 8) & 0xff;int startB = startInt & 0xff;int endInt = (Integer) endValue;int endA = (endInt >> 24) & 0xff;int endR = (endInt >> 16) & 0xff;int endG = (endInt >> 8) & 0xff;int endB = endInt & 0xff;return (int)((startA + (int)(fraction * (endA - startA))) << 24) |(int)((startR + (int)(fraction * (endR - startR))) << 16) |(int)((startG + (int)(fraction * (endG - startG))) << 8) |(int)((startB + (int)(fraction * (endB - startB))));}/*** Update the ring color if this is within the last 25% of the animation.* The new ring color will be a translation from the starting ring color to* the next color.*/private void updateRingColor(float interpolatedTime, Ring ring) {if (interpolatedTime > COLOR_START_DELAY_OFFSET) {// scale the interpolatedTime so that the full// transformation from 0 - 1 takes place in the// remaining timering.setColor(evaluateColorChange((interpolatedTime - COLOR_START_DELAY_OFFSET)/ (1.0f - COLOR_START_DELAY_OFFSET), ring.getStartingColor(),ring.getNextColor()));}}private void applyFinishTranslation(float interpolatedTime, Ring ring) {// shrink back down and complete a full rotation before// starting other circles// Rotation goes between [0..1].updateRingColor(interpolatedTime, ring);float targetRotation = (float) (Math.floor(ring.getStartingRotation() / MAX_PROGRESS_ARC)+ 1f);final float minProgressArc = getMinProgressArc(ring);final float startTrim = ring.getStartingStartTrim()+ (ring.getStartingEndTrim() - minProgressArc - ring.getStartingStartTrim())* interpolatedTime;ring.setStartTrim(startTrim);ring.setEndTrim(ring.getStartingEndTrim());final float rotation = ring.getStartingRotation()+ ((targetRotation - ring.getStartingRotation()) * interpolatedTime);ring.setRotation(rotation);}private void setupAnimators() {final Ring ring = mRing;final Animation animation = new Animation() {@Overridepublic void applyTransformation(float interpolatedTime, Transformation t) {if (mFinishing) {applyFinishTranslation(interpolatedTime, ring);} else {// The minProgressArc is calculated from 0 to create an// angle that matches the stroke width.final float minProgressArc = getMinProgressArc(ring);final float startingEndTrim = ring.getStartingEndTrim();final float startingTrim = ring.getStartingStartTrim();final float startingRotation = ring.getStartingRotation();updateRingColor(interpolatedTime, ring);// Moving the start trim only occurs in the first 50% of a// single ring animationif (interpolatedTime <= START_TRIM_DURATION_OFFSET) {// scale the interpolatedTime so that the full// transformation from 0 - 1 takes place in the// remaining timefinal float scaledTime = (interpolatedTime)/ (1.0f - START_TRIM_DURATION_OFFSET);final float startTrim = startingTrim+ ((MAX_PROGRESS_ARC - minProgressArc) * MATERIAL_INTERPOLATOR.getInterpolation(scaledTime));ring.setStartTrim(startTrim);}// Moving the end trim starts after 50% of a single ring// animation completesif (interpolatedTime > END_TRIM_START_DELAY_OFFSET) {// scale the interpolatedTime so that the full// transformation from 0 - 1 takes place in the// remaining timefinal float minArc = MAX_PROGRESS_ARC - minProgressArc;float scaledTime = (interpolatedTime - START_TRIM_DURATION_OFFSET)/ (1.0f - START_TRIM_DURATION_OFFSET);final float endTrim = startingEndTrim+ (minArc * MATERIAL_INTERPOLATOR.getInterpolation(scaledTime));ring.setEndTrim(endTrim);}final float rotation = startingRotation + (0.25f * interpolatedTime);ring.setRotation(rotation);float groupRotation = ((FULL_ROTATION / NUM_POINTS) * interpolatedTime)+ (FULL_ROTATION * (mRotationCount / NUM_POINTS));setRotation(groupRotation);}}};animation.setRepeatCount(Animation.INFINITE);animation.setRepeatMode(Animation.RESTART);animation.setInterpolator(LINEAR_INTERPOLATOR);animation.setAnimationListener(new Animation.AnimationListener() {@Overridepublic void onAnimationStart(Animation animation) {mRotationCount = 0;}@Overridepublic void onAnimationEnd(Animation animation) {// do nothing}@Overridepublic void onAnimationRepeat(Animation animation) {ring.storeOriginals();ring.goToNextColor();ring.setStartTrim(ring.getEndTrim());if (mFinishing) {// finished closing the last ring from the swipe gesture; go// into progress modemFinishing = false;animation.setDuration(ANIMATION_DURATION);ring.setShowArrow(false);} else {mRotationCount = (mRotationCount + 1) % (NUM_POINTS);}}});mAnimation = animation;}private final Callback mCallback = new Callback() {@Overridepublic void invalidateDrawable(Drawable d) {invalidateSelf();}@Overridepublic void scheduleDrawable(Drawable d, Runnable what, long when) {scheduleSelf(what, when);}@Overridepublic void unscheduleDrawable(Drawable d, Runnable what) {unscheduleSelf(what);}};private static class Ring {private final RectF mTempBounds = new RectF();private final Paint mPaint = new Paint();private final Paint mArrowPaint = new Paint();private final Callback mCallback;private float mStartTrim = 0.0f;private float mEndTrim = 0.0f;private float mRotation = 0.0f;private float mStrokeWidth = 5.0f;private float mStrokeInset = 2.5f;private int[] mColors;// mColorIndex represents the offset into the available mColors that the// progress circle should currently display. As the progress circle is// animating, the mColorIndex moves by one to the next available color.private int mColorIndex;private float mStartingStartTrim;private float mStartingEndTrim;private float mStartingRotation;private boolean mShowArrow;private Path mArrow;private float mArrowScale;private double mRingCenterRadius;private int mArrowWidth;private int mArrowHeight;private int mAlpha;private final Paint mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);private int mBackgroundColor;private int mCurrentColor;public Ring(Callback callback) {mCallback = callback;mPaint.setStrokeCap(Paint.Cap.SQUARE);mPaint.setAntiAlias(true);mPaint.setStyle(Paint.Style.STROKE);mArrowPaint.setStyle(Paint.Style.FILL);mArrowPaint.setAntiAlias(true);}public void setBackgroundColor(int color) {mBackgroundColor = color;}/*** Set the dimensions of the arrowhead.** @param width Width of the hypotenuse of the arrow head* @param height Height of the arrow point*/public void setArrowDimensions(float width, float height) {mArrowWidth = (int) width;mArrowHeight = (int) height;}/*** Draw the progress spinner*/public void draw(Canvas c, Rect bounds) {final RectF arcBounds = mTempBounds;arcBounds.set(bounds);arcBounds.inset(mStrokeInset, mStrokeInset);final float startAngle = (mStartTrim + mRotation) * 360;final float endAngle = (mEndTrim + mRotation) * 360;float sweepAngle = endAngle - startAngle;mPaint.setColor(mCurrentColor);c.drawArc(arcBounds, startAngle, sweepAngle, false, mPaint);drawTriangle(c, startAngle, sweepAngle, bounds);if (mAlpha < 255) {mCirclePaint.setColor(mBackgroundColor);mCirclePaint.setAlpha(255 - mAlpha);c.drawCircle(bounds.exactCenterX(), bounds.exactCenterY(), bounds.width() / 2,mCirclePaint);}}private void drawTriangle(Canvas c, float startAngle, float sweepAngle, Rect bounds) {if (mShowArrow) {if (mArrow == null) {mArrow = new Path();mArrow.setFillType(Path.FillType.EVEN_ODD);} else {mArrow.reset();}// Adjust the position of the triangle so that it is inset as// much as the arc, but also centered on the arc.float inset = (int) mStrokeInset / 2 * mArrowScale;float x = (float) (mRingCenterRadius * Math.cos(0) + bounds.exactCenterX());float y = (float) (mRingCenterRadius * Math.sin(0) + bounds.exactCenterY());// Update the path each time. This works around an issue in SKIA// where concatenating a rotation matrix to a scale matrix// ignored a starting negative rotation. This appears to have// been fixed as of API 21.mArrow.moveTo(0, 0);mArrow.lineTo(mArrowWidth * mArrowScale, 0);mArrow.lineTo((mArrowWidth * mArrowScale / 2), (mArrowHeight* mArrowScale));mArrow.offset(x - inset, y);mArrow.close();// draw a trianglemArrowPaint.setColor(mCurrentColor);c.rotate(startAngle + sweepAngle - ARROW_OFFSET_ANGLE, bounds.exactCenterX(),bounds.exactCenterY());c.drawPath(mArrow, mArrowPaint);}}/*** Set the colors the progress spinner alternates between.** @param colors Array of integers describing the colors. Must be non-<code>null</code>.*/public void setColors(@NonNull int[] colors) {mColors = colors;// if colors are reset, make sure to reset the color index as wellsetColorIndex(0);}/*** Set the absolute color of the progress spinner. This is should only* be used when animating between current and next color when the* spinner is rotating.** @param color int describing the color.*/public void setColor(int color) {mCurrentColor = color;}/*** @param index Index into the color array of the color to display in*            the progress spinner.*/public void setColorIndex(int index) {mColorIndex = index;mCurrentColor = mColors[mColorIndex];}/*** @return int describing the next color the progress spinner should use when drawing.*/public int getNextColor() {return mColors[getNextColorIndex()];}private int getNextColorIndex() {return (mColorIndex + 1) % (mColors.length);}/*** Proceed to the next available ring color. This will automatically* wrap back to the beginning of colors.*/public void goToNextColor() {setColorIndex(getNextColorIndex());}public void setColorFilter(ColorFilter filter) {mPaint.setColorFilter(filter);invalidateSelf();}/*** @param alpha Set the alpha of the progress spinner and associated arrowhead.*/public void setAlpha(int alpha) {mAlpha = alpha;}/*** @return Current alpha of the progress spinner and arrowhead.*/public int getAlpha() {return mAlpha;}/*** @param strokeWidth Set the stroke width of the progress spinner in pixels.*/public void setStrokeWidth(float strokeWidth) {mStrokeWidth = strokeWidth;mPaint.setStrokeWidth(strokeWidth);invalidateSelf();}@SuppressWarnings("unused")public float getStrokeWidth() {return mStrokeWidth;}@SuppressWarnings("unused")public void setStartTrim(float startTrim) {mStartTrim = startTrim;invalidateSelf();}@SuppressWarnings("unused")public float getStartTrim() {return mStartTrim;}public float getStartingStartTrim() {return mStartingStartTrim;}public float getStartingEndTrim() {return mStartingEndTrim;}public int getStartingColor() {return mColors[mColorIndex];}@SuppressWarnings("unused")public void setEndTrim(float endTrim) {mEndTrim = endTrim;invalidateSelf();}@SuppressWarnings("unused")public float getEndTrim() {return mEndTrim;}@SuppressWarnings("unused")public void setRotation(float rotation) {mRotation = rotation;invalidateSelf();}@SuppressWarnings("unused")public float getRotation() {return mRotation;}public void setInsets(int width, int height) {final float minEdge = (float) Math.min(width, height);float insets;if (mRingCenterRadius <= 0 || minEdge < 0) {insets = (float) Math.ceil(mStrokeWidth / 2.0f);} else {insets = (float) (minEdge / 2.0f - mRingCenterRadius);}mStrokeInset = insets;}@SuppressWarnings("unused")public float getInsets() {return mStrokeInset;}/*** @param centerRadius Inner radius in px of the circle the progress*            spinner arc traces.*/public void setCenterRadius(double centerRadius) {mRingCenterRadius = centerRadius;}public double getCenterRadius() {return mRingCenterRadius;}/*** @param show Set to true to show the arrow head on the progress spinner.*/public void setShowArrow(boolean show) {if (mShowArrow != show) {mShowArrow = show;invalidateSelf();}}/*** @param scale Set the scale of the arrowhead for the spinner.*/public void setArrowScale(float scale) {if (scale != mArrowScale) {mArrowScale = scale;invalidateSelf();}}/*** @return The amount the progress spinner is currently rotated, between [0..1].*/public float getStartingRotation() {return mStartingRotation;}/*** If the start / end trim are offset to begin with, store them so that* animation starts from that offset.*/public void storeOriginals() {mStartingStartTrim = mStartTrim;mStartingEndTrim = mEndTrim;mStartingRotation = mRotation;}/*** Reset the progress spinner to default rotation, start and end angles.*/public void resetOriginals() {mStartingStartTrim = 0;mStartingEndTrim = 0;mStartingRotation = 0;setStartTrim(0);setEndTrim(0);setRotation(0);}private void invalidateSelf() {mCallback.invalidateDrawable(null);}}
}

DividerItemDecoration

package com.linzhou.recyclerviewfresh.refresh;/*** Created by joe on 2016/10/23.*/
/** Copyright (C) 2014 The Android Open Source Project** Licensed under the Apache License, Version 2.0 (the "License");* limitations under the License.*/import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;/*** This class is from the v7 samples of the Android SDK. It's not by me!* <p/>* See the license above for details.*/
public class DividerItemDecoration extends RecyclerView.ItemDecoration {private static final int[] ATTRS = new int[]{android.R.attr.listDivider};public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;private Drawable mDivider;private int mOrientation;public DividerItemDecoration(Context context, int orientation) {final TypedArray a = context.obtainStyledAttributes(ATTRS);mDivider = a.getDrawable(0);a.recycle();setOrientation(orientation);}public void setOrientation(int orientation) {if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {throw new IllegalArgumentException("invalid orientation");}mOrientation = orientation;}@Overridepublic void onDraw(Canvas c, RecyclerView parent) {//Log.v("recyclerview - itemdecoration", "onDraw()");if (mOrientation == VERTICAL_LIST) {drawVertical(c, parent);} else {drawHorizontal(c, parent);}}public void drawVertical(Canvas c, RecyclerView parent) {final int left = parent.getPaddingLeft();final int right = parent.getWidth() - parent.getPaddingRight();final int childCount = parent.getChildCount();for (int i = 0; i < childCount; i++) {final View child = parent.getChildAt(i);RecyclerView v = new RecyclerView(parent.getContext());final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();final int top = child.getBottom() + params.bottomMargin;final int bottom = top + mDivider.getIntrinsicHeight();mDivider.setBounds(left, top, right, bottom);mDivider.draw(c);}}public void drawHorizontal(Canvas c, RecyclerView parent) {final int top = parent.getPaddingTop();final int bottom = parent.getHeight() - parent.getPaddingBottom();final int childCount = parent.getChildCount();for (int i = 0; i < childCount; i++) {final View child = parent.getChildAt(i);final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();final int left = child.getRight() + params.rightMargin;final int right = left + mDivider.getIntrinsicHeight();mDivider.setBounds(left, top, right, bottom);mDivider.draw(c);}}@Overridepublic void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {if (mOrientation == VERTICAL_LIST) {outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());} else {outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);}}
}

DividerGridItemDecoration

package com.linzhou.recyclerviewfresh.refresh;/*** Created by joe on 2016/10/23.*/
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.LayoutManager;
import android.support.v7.widget.RecyclerView.State;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.view.View;public class DividerGridItemDecoration extends RecyclerView.ItemDecoration
{private static final int[] ATTRS = new int[] { android.R.attr.listDivider };private Drawable mDivider;public DividerGridItemDecoration(Context context){final TypedArray a = context.obtainStyledAttributes(ATTRS);mDivider = a.getDrawable(0);a.recycle();}@Overridepublic void onDraw(Canvas c, RecyclerView parent, State state){drawHorizontal(c, parent);drawVertical(c, parent);}private int getSpanCount(RecyclerView parent){// 列数int spanCount = -1;LayoutManager layoutManager = parent.getLayoutManager();if (layoutManager instanceof GridLayoutManager){spanCount = ((GridLayoutManager) layoutManager).getSpanCount();} else if (layoutManager instanceof StaggeredGridLayoutManager){spanCount = ((StaggeredGridLayoutManager) layoutManager).getSpanCount();}return spanCount;}public void drawHorizontal(Canvas c, RecyclerView parent){int childCount = parent.getChildCount();for (int i = 0; i < childCount; i++){final View child = parent.getChildAt(i);final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();final int left = child.getLeft() - params.leftMargin;final int right = child.getRight() + params.rightMargin+ mDivider.getIntrinsicWidth();final int top = child.getBottom() + params.bottomMargin;final int bottom = top + mDivider.getIntrinsicHeight();mDivider.setBounds(left, top, right, bottom);mDivider.draw(c);}}public void drawVertical(Canvas c, RecyclerView parent){final int childCount = parent.getChildCount();for (int i = 0; i < childCount; i++){final View child = parent.getChildAt(i);final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();final int top = child.getTop() - params.topMargin;final int bottom = child.getBottom() + params.bottomMargin;final int left = child.getRight() + params.rightMargin;final int right = left + mDivider.getIntrinsicWidth();mDivider.setBounds(left, top, right, bottom);mDivider.draw(c);}}private boolean isLastColum(RecyclerView parent, int pos, int spanCount,int childCount){LayoutManager layoutManager = parent.getLayoutManager();if (layoutManager instanceof GridLayoutManager){if ((pos + 1) % spanCount == 0)// 如果是最后一列,则不需要绘制右边{return true;}} else if (layoutManager instanceof StaggeredGridLayoutManager){int orientation = ((StaggeredGridLayoutManager) layoutManager).getOrientation();if (orientation == StaggeredGridLayoutManager.VERTICAL){if ((pos + 1) % spanCount == 0)// 如果是最后一列,则不需要绘制右边{return true;}} else{childCount = childCount - childCount % spanCount;if (pos >= childCount)// 如果是最后一列,则不需要绘制右边return true;}}return false;}private boolean isLastRaw(RecyclerView parent, int pos, int spanCount,int childCount){LayoutManager layoutManager = parent.getLayoutManager();if (layoutManager instanceof GridLayoutManager){childCount = childCount - childCount % spanCount;if (pos >= childCount)// 如果是最后一行,则不需要绘制底部return true;} else if (layoutManager instanceof StaggeredGridLayoutManager){int orientation = ((StaggeredGridLayoutManager) layoutManager).getOrientation();// StaggeredGridLayoutManager 且纵向滚动if (orientation == StaggeredGridLayoutManager.VERTICAL){childCount = childCount - childCount % spanCount;// 如果是最后一行,则不需要绘制底部if (pos >= childCount)return true;} else// StaggeredGridLayoutManager 且横向滚动{// 如果是最后一行,则不需要绘制底部if ((pos + 1) % spanCount == 0){return true;}}}return false;}@Overridepublic void getItemOffsets(Rect outRect, int itemPosition,RecyclerView parent){int spanCount = getSpanCount(parent);int childCount = parent.getAdapter().getItemCount();if (isLastRaw(parent, itemPosition, spanCount, childCount))// 如果是最后一行,则不需要绘制底部{outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);} else if (isLastColum(parent, itemPosition, spanCount, childCount))// 如果是最后一列,则不需要绘制右边{outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());} else{outRect.set(0, 0, mDivider.getIntrinsicWidth(),mDivider.getIntrinsicHeight());}}
}

CommonAdapter

package com.linzhou.recyclerviewfresh.adapter;import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.ViewGroup;import java.util.List;/*** Created by joe on 2016/10/23.*/
public abstract class CommonAdapter<T> extends RecyclerView.Adapter<ViewHolder> {protected Context mContext;protected int mLayoutId;protected List<T> mDatas;protected LayoutInflater mLayoutInflater;public CommonAdapter(Context context,int layoutId,List<T> datas){this.mContext = context;this.mLayoutId = layoutId;this.mDatas = datas;mLayoutInflater = LayoutInflater.from(context);}@Overridepublic ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {ViewHolder viewHolder = ViewHolder.get(mContext, parent, mLayoutId);return viewHolder;}@Overridepublic void onBindViewHolder(ViewHolder holder, int position) {//暴露接口,使用viewHoler.getView(viewId)获取对应的控件,并且设置相应的数据convert(holder,mDatas.get(position),position);}@Overridepublic void onBindViewHolder(ViewHolder holder, int position, List<Object> payloads) {super.onBindViewHolder(holder, position, payloads);}public abstract void convert(ViewHolder viewHolder, T item, int position);@Overridepublic int getItemCount() {return mDatas == null ? 0 : mDatas.size();}
}

MultiItemCommonAdapter

package com.linzhou.recyclerviewfresh.adapter;import android.content.Context;
import android.view.ViewGroup;import java.util.List;/*** Created by joe on 2016/10/23.*/
public abstract class MultiItemCommonAdapter<T> extends CommonAdapter<T> {private MultiItemTypeSupport mMultiItemTypeSupport;public MultiItemCommonAdapter(Context context, List<T> datas ,MultiItemTypeSupport multiItemTypeSupport) {//layoutId是在onCreateViewHolder中加载布局的,因为这里还要重写该方法,所以不需要传layoutIdsuper(context, -1, datas);this.mMultiItemTypeSupport = multiItemTypeSupport;}@Overridepublic int getItemViewType(int position) {//根据position和java bean对象中的某个字段值就可以该条目是什么类型的了//类型可以从0开始,依次往上加,每个数字对应一种类型return mMultiItemTypeSupport.getItemViewType(position,mDatas.get(position));}@Overridepublic ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {//通过viewType获取不同条目的布局idint itemLayoutId = mMultiItemTypeSupport.getItemLayoutId(viewType);//通过布局id去创建ViewHolderViewHolder viewHolder = ViewHolder.get(mContext, parent, itemLayoutId);return viewHolder;}}

MultiItemTypeSupport

package com.linzhou.recyclerviewfresh.adapter;/*** Created by joe on 2016/10/23.*/
public interface MultiItemTypeSupport<T> {int getItemViewType(int position, T item);//通过position来返回条目的类型int getItemLayoutId(int itemType);//根据要显示的条目类型,去返回相应的布局id
}

ViewHolder

package com.linzhou.recyclerviewfresh.adapter;import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;/*** Created by joe on 2016/10/23.*/
public class ViewHolder extends RecyclerView.ViewHolder {private Context mContext;private View mConvertView;//条目布局private SparseArray<View> mViews;//保存条目布局中的所有控件(一个ViewHolder对象对应一个条目布局)public ViewHolder(Context context , View itemView, ViewGroup viewGroup) {super(itemView);this.mContext = context;this.mConvertView = itemView;mViews = new SparseArray<View>();}/*** 得到ViewHolder对象* @param context* @param parent* @param layoutId 条目布局的资源id* @return*/public static ViewHolder get(Context context ,ViewGroup parent,int layoutId){View itemView = LayoutInflater.from(context).inflate(layoutId, parent, false);ViewHolder viewHolder = new ViewHolder(context,itemView,parent);return viewHolder;}/*** 根据控件id获取条目布局(ViewHolder)中的控件* @param viewId 控件id* @param <T>* @return*/public <T extends View> T getView(int viewId){View view = mViews.get(viewId);if(view == null){view = mConvertView.findViewById(viewId);mViews.put(viewId,view);}return (T)view;}/*** 给TextView设置文本* @param viewId TextView的id* @param text 显示的文本* @return*/public ViewHolder setText(int viewId,String text){TextView textView = getView(viewId);textView.setText(text);return this;}}

MainActivity

package com.linzhou.recyclerviewfresh.activity;
import android.content.Context;
import android.graphics.Color;
import android.os.SystemClock;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;import com.linzhou.recyclerviewfresh.R;
import com.linzhou.recyclerviewfresh.adapter.CommonAdapter;
import com.linzhou.recyclerviewfresh.adapter.ViewHolder;
import com.linzhou.recyclerviewfresh.refresh.DividerItemDecoration;
import com.linzhou.recyclerviewfresh.refresh.PullRefreshLayout;import java.util.ArrayList;
import java.util.List;public class MainActivity extends AppCompatActivity {private RecyclerView mRecycler;private SwipeRefreshLayout mSwipe;private PullRefreshLayout mPull;private ArrayList<String> mDatas;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mRecycler = (RecyclerView) findViewById(R.id.recycler);mSwipe = (SwipeRefreshLayout) findViewById(R.id.swipe);mPull = (PullRefreshLayout) findViewById(R.id.pull);findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {mPull.setAllowLoadMore(!mPull.isAllowLoadMore());}});LinearLayoutManager layoutManager = new LinearLayoutManager(this);layoutManager.setOrientation(LinearLayoutManager.VERTICAL);mRecycler.setLayoutManager(layoutManager);mRecycler.addItemDecoration(new DividerItemDecoration(this,LinearLayoutManager.VERTICAL));mPull.setColorSchemeColors(Color.BLACK,Color.CYAN,Color.YELLOW);mDatas = new ArrayList<>();for(int i=0;i<220;i++){mDatas.add(i+"");}final MyAdapter adapter = new MyAdapter(this,R.layout.recyclercview_item,mDatas);mRecycler.setAdapter(adapter);mSwipe.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {@Overridepublic void onRefresh() {new Thread(new Runnable() {@Overridepublic void run() {SystemClock.sleep(2000);mDatas.clear();for(int i=0;i<220;i++){mDatas.add(i+"");}runOnUiThread(new Runnable() {@Overridepublic void run() {adapter.notifyDataSetChanged();mSwipe.setRefreshing(false);}});}}).start();}});mPull.setOnPullListener(new PullRefreshLayout.OnPullListener() {@Overridepublic void onLoadMore(final PullRefreshLayout pullRefreshLayout) {new Thread(new Runnable() {@Overridepublic void run() {SystemClock.sleep(2000);for (int i = 0; i < 10; i++) {mDatas.add(mDatas.size() + "");}runOnUiThread(new Runnable() {@Overridepublic void run() {adapter.notifyDataSetChanged();pullRefreshLayout.setRefreshing(false);}});}}).start();}});}class MyAdapter extends CommonAdapter<String> {public MyAdapter(Context context, int layoutId, List<String> datas) {super(context, layoutId, datas);}@Overridepublic void convert(ViewHolder viewHolder, String item, int position) {viewHolder.setText(R.id.recyclerView_tv,item);}}
}

recyclercview_item

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><TextView
        android:id="@+id/recyclerView_tv"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"/>
</LinearLayout>

activity_main

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:id="@+id/activity_main"android:layout_width="match_parent"android:layout_height="match_parent"tools:context="com.linzhou.recyclerviewfresh.activity.MainActivity"><com.linzhou.recyclerviewfresh.refresh.PullRefreshLayoutandroid:id="@+id/pull"android:layout_width="match_parent"android:layout_height="match_parent"><android.support.v4.widget.SwipeRefreshLayoutandroid:id="@+id/swipe"android:layout_width="match_parent"android:layout_height="match_parent"><android.support.v7.widget.RecyclerViewandroid:id="@+id/recycler"android:layout_width="match_parent"android:layout_height="match_parent"android:cacheColorHint="@null"android:scrollbars="vertical"></android.support.v7.widget.RecyclerView></android.support.v4.widget.SwipeRefreshLayout></com.linzhou.recyclerviewfresh.refresh.PullRefreshLayout><Buttonandroid:id="@+id/btn"android:layout_toRightOf="@id/btn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="允许"/>
</RelativeLayout>

完工!

源码下载地址:http://download.csdn.net/detail/zhou_anzhuojinjie/9661542

结合SwipeRefreshLayout可以上拉加载更多下拉刷新的RecyclerView相关推荐

  1. ListView上拉加载和下拉刷新多种实现方式

    ListView上拉加载和下拉刷新多种实现方式 该篇为ListView下拉刷新和上拉加载实现的各种方法大合集.可能在具体的细节逻辑上处理不太到位,但基本上完成逻辑的实现.细节方面,个人可以根据自己的需 ...

  2. 如何实现上拉加载,下拉刷新?

    如何实现上拉加载,下拉刷新? 一.前言 二.实现原理 上拉加载 下拉刷新 三.案例 小结 一.前言 下拉刷新和上拉加载这两种交互方式通常出现在移动端中 本质上等同于PC网页中的分页,只是交互形式不同 ...

  3. 如何实现上拉加载和下拉刷新

    下拉刷新和上拉加载这两种交互⽅式通常出现在移动端中 本质上等同于PC⽹⻚中的分⻚,只是交互形式不同 开源社区也有很多优秀的解决⽅案,如 iscroll . better-scroll . pullto ...

  4. h5 加载更多下拉按钮_更好的按钮设计的5个技巧

    h5 加载更多下拉按钮 重点 (Top highlight) 第6部分 (Part 6) After we went through all the very basics of UI design ...

  5. 面试官:JavaScript如何实现上拉加载,下拉刷新?

    一.前言 下拉刷新和上拉加载这两种交互方式通常出现在移动端中 本质上等同于PC网页中的分页,只是交互形式不同 开源社区也有很多优秀的解决方案,如iscroll.better-scroll.pullto ...

  6. html5上拉下拉刷新,APP 上拉加载,下拉刷新 介绍

    开发APP时,很多时候都会用上,上拉加载数据,下拉刷新等功能,本文件介绍两种. 一种是原生APP自带的上拉加载,下拉刷新功能,一种是用JS 插件写的上拉加载,下拉刷新. 1.原生APP 的 上拉加载, ...

  7. better-scroll 上拉加载,下拉刷新(解决移动端长页面卡顿)

    一.Better Scroll 滚动原理 1.下图能直观的表示better-scroll的滚动原理 2.html设置 <div class="wrapper"> < ...

  8. 小程序上拉加载,下拉刷新

    小程序上拉加载,下拉刷新 data: {collectinformation: null,number: 1,size: 10,isOpenLoading: true,isEmpty: true,is ...

  9. 基于iSroll 5.0实现的上拉加载和下拉刷新插件

    Updownload.js 基于iSroll 5.0实现的上拉加载和下拉刷新插件 移动端效果比较好,开发者工具打开后,需要刷新下页面. [演示地址:] https://chenyk2016.githu ...

最新文章

  1. java 有什么方法可以动态或循环的生成对象名
  2. 避免showModalDialog打开的窗口Page_Load只执行一次
  3. it 部门的建议_应对IT项目阻力的8个建议
  4. Pytest装饰器@pytest.mark.parametrize一键生成接口正交试验用例
  5. centos 需要哪些常用端口_Docker 最常用的镜像命令和容器命令
  6. java 怎么链接ndk的库_使用ndk-build链接现有的静态库
  7. 扫描过程_整体扫描+材料聚焦方法在审题过程中的运用
  8. python基本词汇的特点_开课吧老师为你讲解 Python都有什么优点?
  9. 洛谷P5050 【模板】多项式多点求值
  10. Linux系统下init进程的前世今生
  11. 计算机无法识别 此硬盘,如果计算机无法识别硬盘驱动器,该怎么办?
  12. WPF 通过Image控件实现多张图片的播放
  13. java公倍数_java中如何计算最小公倍数
  14. 打造自己的图像识别模型
  15. 0元永久授权,etl作业批量调度必备软件 Taskctl Free应用版
  16. CRM系统 - 总结 (一) 权限
  17. python应为缩进块_Python的缩进
  18. 《RefineMask:Towards High-Quality Instance Segmentation with Fine-Grained Features》论文笔记
  19. H.264_AVC视频编码技术学习
  20. 五 蓝牙低功耗(BLE)协议栈 之 ATT层

热门文章

  1. 求帮忙看看这怎么解决。 求帮助
  2. traceroute命令讲解
  3. BZOJ2893:征服王(费用流)
  4. RK3399Pro sudo apt-get upgrade相关问题
  5. java开发魔术相机,百变魔术相机
  6. 计算机关机慢怎么解决方法,电脑关机慢怎么办?加快电脑关机速度方法汇总
  7. unity texture贴图纹理
  8. mysql barracuda_MySQL Antelope和Barracuda的区别分析
  9. hadoop常见架构
  10. linux中的ldd命令简介