标签:
这次在项目中实现的是下拉刷新功能,包含两个问题:自定义样式 & 强制刷新。
0. 效果图(模仿git的一个demo):
1. 自定义样式
下拉刷新用到的是普遍的框架PullToRefreshView.java下面会有代码展示,这个框架在网上都能随便搜到,然后定义自定义样式RefreshView.java,下面是代码:
PullToRefreshView.java
package com.baofoo.mobile.wallet.common.view.pullable;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v4.view.MotionEventCompat;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.Transformation;
import android.widget.AbsListView;
import android.widget.ImageView;
/**
* 下拉刷新框架
* Created by zst on 16/1/7.
*/
public class PullToRefreshView extends ViewGroup {
private static final int DRAG_MAX_DISTANCE = 120;
private static final float DRAG_RATE = .5f;
private static final float DECELERATE_INTERPOLATION_FACTOR = 2f;
public static final int MAX_OFFSET_ANIMATION_DURATION = 700;
public static final int RESTORE_ANIMATION_DURATION = 2350;
private static final int INVALID_POINTER = -1;
private View mTarget;
private ImageView mRefreshImageView;
private Interpolator mDecelerateInterpolator;
private int mTouchSlop;
private int mTotalDragDistance;
private RefreshView mRefreshView;
private float mCurrentDragPercent;
private int mCurrentOffsetTop;
private boolean mRefreshing;
private int mActivePointerId;
private boolean mIsBeingDragged;
private float mInitialMotionY;
private int mFrom;
private float mFromDragPercent;
private boolean mNotify;
private OnRefreshListener mListener;
public PullToRefreshView(Context context) {
this(context, null);
}
public PullToRefreshView(Context context, AttributeSet attrs) {
super(context, attrs);
mDecelerateInterpolator = new DecelerateInterpolator(DECELERATE_INTERPOLATION_FACTOR);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
float density = context.getResources().getDisplayMetrics().density;
mTotalDragDistance = Math.round((float) DRAG_MAX_DISTANCE * density);
mRefreshImageView = new ImageView(context);
mRefreshView = new RefreshView(getContext(), this);
mRefreshImageView.setImageDrawable(mRefreshView);
addView(mRefreshImageView);
setWillNotDraw(false);
ViewCompat.setChildrenDrawingOrderEnabled(this, true);
}
public int getTotalDragDistance() {
return mTotalDragDistance;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
ensureTarget();
if (mTarget == null)
return;
widthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth() - getPaddingRight() - getPaddingLeft(), MeasureSpec.EXACTLY);
heightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(), MeasureSpec.EXACTLY);
mTarget.measure(widthMeasureSpec, heightMeasureSpec);
mRefreshImageView.measure(widthMeasureSpec, heightMeasureSpec);
}
private void ensureTarget() {
if (mTarget != null)
return;
if (getChildCount() > 0) {
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (child != mRefreshImageView)
mTarget = child;
}
}
}
@Override
public boolean onInterceptTouchEvent(@NonNull MotionEvent ev) {
if (!isEnabled() || canChildScrollUp() || mRefreshing) {
return false;
}
final int action = MotionEventCompat.getActionMasked(ev);
switch (action) {
case MotionEvent.ACTION_DOWN:
setTargetOffsetTop(0, true);
mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
mIsBeingDragged = false;
final float initialMotionY = getMotionEventY(ev, mActivePointerId);
if (initialMotionY == -1) {
return false;
}
mInitialMotionY = initialMotionY;
break;
case MotionEvent.ACTION_MOVE:
if (mActivePointerId == INVALID_POINTER) {
return false;
}
final float y = getMotionEventY(ev, mActivePointerId);
if (y == -1) {
return false;
}
final float yDiff = y - mInitialMotionY;
if (yDiff > mTouchSlop && !mIsBeingDragged) {
mIsBeingDragged = true;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mIsBeingDragged = false;
mActivePointerId = INVALID_POINTER;
break;
case MotionEventCompat.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
break;
}
return mIsBeingDragged;
}
@Override
public boolean onTouchEvent(@NonNull MotionEvent ev) {
if (!mIsBeingDragged) {
return super.onTouchEvent(ev);
}
final int action = MotionEventCompat.getActionMasked(ev);
switch (action) {
case MotionEvent.ACTION_MOVE: {
final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
if (pointerIndex < 0) {
return false;
}
final float y = MotionEventCompat.getY(ev, pointerIndex);
final float yDiff = y - mInitialMotionY;
final float scrollTop = yDiff * DRAG_RATE;
mCurrentDragPercent = scrollTop / mTotalDragDistance;
if (mCurrentDragPercent < 0) {
return false;
}
float boundedDragPercent = Math.min(1f, Math.abs(mCurrentDragPercent));
float extraOS = Math.abs(scrollTop) - mTotalDragDistance;
float slingshotDist = mTotalDragDistance;
float tensionSlingshotPercent = Math.max(0,
Math.min(extraOS, slingshotDist * 2) / slingshotDist);
float tensionPercent = (float) ((tensionSlingshotPercent / 4) - Math.pow(
(tensionSlingshotPercent / 4), 2)) * 2f;
float extraMove = (slingshotDist) * tensionPercent / 2;
int targetY = (int) ((slingshotDist * boundedDragPercent) + extraMove);
mRefreshView.setPercent(mCurrentDragPercent);
setTargetOffsetTop(targetY - mCurrentOffsetTop, true);
break;
}
case MotionEventCompat.ACTION_POINTER_DOWN:
final int index = MotionEventCompat.getActionIndex(ev);
mActivePointerId = MotionEventCompat.getPointerId(ev, index);
break;
case MotionEventCompat.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
if (mActivePointerId == INVALID_POINTER) {
return false;
}
final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
final float y = MotionEventCompat.getY(ev, pointerIndex);
final float overScrollTop = (y - mInitialMotionY) * DRAG_RATE;
mIsBeingDragged = false;
if (overScrollTop > mTotalDragDistance) {
setRefreshing(true, true);
} else {
mRefreshing = false;
animateOffsetToPosition(mAnimateToStartPosition);
}
mActivePointerId = INVALID_POINTER;
return false;
}
}
return true;
}
private void animateOffsetToPosition(Animation animation) {
mFrom = mCurrentOffsetTop;
mFromDragPercent = mCurrentDragPercent;
long animationDuration = (long) Math.abs(MAX_OFFSET_ANIMATION_DURATION * mFromDragPercent);
animation.reset();
animation.setDuration(animationDuration);
animation.setInterpolator(mDecelerateInterpolator);
animation.setAnimationListener(mToStartListener);
mRefreshImageView.clearAnimation();
mRefreshImageView.startAnimation(animation);
}
private void animateOffsetToCorrectPosition() {
mFrom = mCurrentOffsetTop;
mFromDragPercent = mCurrentDragPercent;
mAnimateToCorrectPosition.reset();
mAnimateToCorrectPosition.setDuration(RESTORE_ANIMATION_DURATION);
mAnimateToCorrectPosition.setInterpolator(mDecelerateInterpolator);
mRefreshImageView.clearAnimation();
mRefreshImageView.startAnimation(mAnimateToCorrectPosition);
if (mRefreshing) {
mRefreshView.start();
if (mNotify) {
if (mListener != null) {
mListener.onRefresh();
}
}
} else {
mRefreshView.stop();
animateOffsetToPosition(mAnimateToStartPosition);
}
mCurrentOffsetTop = mTarget.getTop();
}
private final Animation mAnimateToStartPosition = new Animation() {
@Override
public void applyTransformation(float interpolatedTime, @NonNull Transformation t) {
moveToStart(interpolatedTime);
}
};
private Animation mAnimateToEndPosition = new Animation() {
@Override
public void applyTransformation(float interpolatedTime, @NonNull Transformation t) {
moveToEnd(interpolatedTime);
}
};
private final Animation mAnimateToCorrectPosition = new Animation() {
@Override
public void applyTransformation(float interpolatedTime, @NonNull Transformation t) {
int targetTop;
int endTarget = mTotalDragDistance;
targetTop = (mFrom + (int) ((endTarget - mFrom) * interpolatedTime));
int offset = targetTop - mTarget.getTop();
mCurrentDragPercent = mFromDragPercent - (mFromDragPercent - 1.0f) * interpolatedTime;
mRefreshView.setPercent(mCurrentDragPercent);
setTargetOffsetTop(offset, false /* requires update */);
}
};
private void moveToStart(float interpolatedTime) {
int targetTop = mFrom - (int) (mFrom * interpolatedTime);
float targetPercent = mFromDragPercent * (1.0f - interpolatedTime);
int offset = targetTop - mTarget.getTop();
mCurrentDragPercent = targetPercent;
mRefreshView.setPercent(mCurrentDragPercent);
setTargetOffsetTop(offset, false);
}
private void moveToEnd(float interpolatedTime) {
int targetTop = mFrom - (int) (mFrom * interpolatedTime);
float targetPercent = mFromDragPercent * (1.0f + interpolatedTime);
int offset = targetTop - mTarget.getTop();
mCurrentDragPercent = targetPercent;
mRefreshView.setPercent(mCurrentDragPercent);
setTargetOffsetTop(offset, false);
}
public void setRefreshing(boolean refreshing) {
if (mRefreshing != refreshing) {
setRefreshing(refreshing, false /* notify */);
}
}
private void setRefreshing(boolean refreshing, final boolean notify) {
if (mRefreshing != refreshing) {
mNotify = notify;
ensureTarget();
mRefreshing = refreshing;
if (mRefreshing) {
mRefreshView.setPercent(1f);
animateOffsetToCorrectPosition();
} else {
mRefreshView.setEndOfRefreshing(true);
animateOffsetToPosition(mAnimateToEndPosition);
}
}
}
private Animation.AnimationListener mToStartListener = new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
mRefreshView.stop();
mCurrentOffsetTop = mTarget.getTop();
}
};
private void onSecondaryPointerUp(MotionEvent ev) {
final int pointerIndex = MotionEventCompat.getActionIndex(ev);
final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
if (pointerId == mActivePointerId) {
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
}
}
private float getMotionEventY(MotionEvent ev, int activePointerId) {
final int index = MotionEventCompat.findPointerIndex(ev, activePointerId);
if (index < 0) {
return -1;
}
return MotionEventCompat.getY(ev, index);
}
private void setTargetOffsetTop(int offset, boolean requiresUpdate) {
mTarget.offsetTopAndBottom(offset);
mRefreshView.offsetTopAndBottom(offset);
mCurrentOffsetTop = mTarget.getTop();
if (requiresUpdate && android.os.Build.VERSION.SDK_INT < 11) {
invalidate();
}
}
private boolean canChildScrollUp() {
if (android.os.Build.VERSION.SDK_INT < 14) {
if (mTarget instanceof AbsListView) {
final AbsListView absListView = (AbsListView) mTarget;
return absListView.getChildCount() > 0
&& (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0)
.getTop() < absListView.getPaddingTop());
} else {
return mTarget.getScrollY() > 0;
}
} else {
return ViewCompat.canScrollVertically(mTarget, -1);
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
ensureTarget();
if (mTarget == null)
return;
int height = getMeasuredHeight();
int width = getMeasuredWidth();
int left = getPaddingLeft();
int top = getPaddingTop();
int right = getPaddingRight();
int bottom = getPaddingBottom();
mTarget.layout(left, top + mCurrentOffsetTop, left + width - right, top + height - bottom + mCurrentOffsetTop);
mRefreshImageView.layout(left, top, left + width - right, top + height - bottom);
}
public void setOnRefreshListener(OnRefreshListener listener) {
mListener = listener;
}
public interface OnRefreshListener {
void onRefresh();
}
}
RefreshView.java
package com.baofoo.mobile.wallet.common.view.pullable;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.annotation.NonNull;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.Interpolator;
import android.view.animation.Transformation;
import com.baofoo.mobile.wallet.R;
import com.baofoo.mobile.wallet.common.view.pullable.PullToRefreshView;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
/**
* 下拉刷新布局样式
* Created by zst on 02/03/2015.
* https://dribbble.com/shots/1623131-Pull-to-Refresh
*/
public class RefreshView extends Drawable implements Drawable.Callback, Animatable {
private static final float SCALE_START_PERCENT = 0.5f;
private static final int ANIMATION_DURATION = 1000;
private static final float SIDE_CLOUDS_INITIAL_SCALE = 1.05f;
private static final float SIDE_CLOUDS_FINAL_SCALE = 1.55f;
private static final float CENTER_CLOUDS_INITIAL_SCALE = 0.8f;
private static final float CENTER_CLOUDS_FINAL_SCALE = 1.30f;
private static final Interpolator ACCELERATE_DECELERATE_INTERPOLATOR = new AccelerateDecelerateInterpolator();
// Multiply with this animation interpolator time
private static final int LOADING_ANIMATION_COEFFICIENT = 80;
private static final int SLOW_DOWN_ANIMATION_COEFFICIENT = 6;
// Amount of lines when is going lading animation
private static final int WIND_SET_AMOUNT = 10;
private static final int Y_SIDE_CLOUDS_SLOW_DOWN_COF = 4;
private static final int X_SIDE_CLOUDS_SLOW_DOWN_COF = 2;
private static final int MIN_WIND_LINE_WIDTH = 50;
private static final int MAX_WIND_LINE_WIDTH = 300;
private static final int MIN_WIND_X_OFFSET = 1000;
private static final int MAX_WIND_X_OFFSET = 2000;
private static final int RANDOM_Y_COEFFICIENT = 5;
private Context mContext;
private PullToRefreshView mParent;
private Matrix mMatrix;
private Matrix mAdditionalMatrix;
private Animation mAnimation;
private int mTop;
private int mScreenWidth;
private boolean mInverseDirection;
//KEY: Y position, Value: X offset of wind
private Map<Float, Float> mWinds;
private Paint mWindPaint;
private float mWindLineWidth;
private boolean mNewWindSet;
private int mJetWidthCenter;
private int mJetHeightCenter;
private float mJetTopOffset;
private int mFrontCloudHeightCenter;
private int mFrontCloudWidthCenter;
private int mRightCloudsWidthCenter;
private int mRightCloudsHeightCenter;
private int mLeftCloudsWidthCenter;
private int mLeftCloudsHeightCenter;
private float mPercent = 0.0f;
private Bitmap mJet;
private Bitmap mFrontClouds;
private Bitmap mLeftClouds;
private Bitmap mRightClouds;
private boolean isRefreshing = false;
private float mLoadingAnimationTime;
private float mLastAnimationTime;
private Random mRandom;
private boolean mEndOfRefreshing;
private enum AnimationPart {
FIRST,
SECOND,
THIRD,
FOURTH
}
public RefreshView(Context context, PullToRefreshView parent) {
mContext = context;
mParent = parent;
mMatrix = new Matrix();
mAdditionalMatrix = new Matrix();
mWinds = new HashMap<>();
mRandom = new Random();
mWindPaint = new Paint();
mWindPaint.setColor(mContext.getResources().getColor(android.R.color.white));
mWindPaint.setStrokeWidth(3);
mWindPaint.setAlpha(50);
initiateDimens();
createBitmaps();
setupAnimations();
}
private void initiateDimens() {
mScreenWidth = mContext.getResources().getDisplayMetrics().widthPixels;
mJetTopOffset = mParent.getTotalDragDistance() * 0.5f;
mTop = -mParent.getTotalDragDistance();
}
private void createBitmaps() {
mLeftClouds = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.clouds_left);
mRightClouds = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.clouds_right);
mFrontClouds = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.clouds_center);
mJet = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.airplane);
mJetWidthCenter = mJet.getWidth() / 2;
mJetHeightCenter = mJet.getHeight() / 2;
mFrontCloudWidthCenter = mFrontClouds.getWidth() / 2;
mFrontCloudHeightCenter = mFrontClouds.getHeight() / 2;
mRightCloudsWidthCenter = mRightClouds.getWidth() / 2;
mRightCloudsHeightCenter = mRightClouds.getHeight() / 2;
mLeftCloudsWidthCenter = mLeftClouds.getWidth() / 2;
mLeftCloudsHeightCenter = mLeftClouds.getHeight() / 2;
}
public void offsetTopAndBottom(int offset) {
mTop += offset;
invalidateSelf();
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override
public void invalidateDrawable(@NonNull Drawable who) {
final Callback callback = getCallback();
if (callback != null) {
callback.invalidateDrawable(this);
}
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override
public void scheduleDrawable(Drawable who, Runnable what, long when) {
final Callback callback = getCallback();
if (callback != null) {
callback.scheduleDrawable(this, what, when);
}
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override
public void unscheduleDrawable(Drawable who, Runnable what) {
final Callback callback = getCallback();
if (callback != null) {
callback.unscheduleDrawable(this, what);
}
}
@Override
public void setAlpha(int alpha) {
}
@Override
public void setColorFilter(ColorFilter cf) {
}
/**
* Our animation depend on type of current work of refreshing.
* We should to do different things when it's end of refreshing
*
* @param endOfRefreshing - we will check current state of refresh with this
*/
public void setEndOfRefreshing(boolean endOfRefreshing) {
mEndOfRefreshing = endOfRefreshing;
}
@Override
public void draw(@NonNull Canvas canvas) {
final int saveCount = canvas.save();
// DRAW BACKGROUND.
canvas.drawColor(mContext.getResources().getColor(R.color.sky_background));
if (isRefreshing) {
// Set up new set of wind
while (mWinds.size() < WIND_SET_AMOUNT) {
float y = (float) (mParent.getTotalDragDistance() / (Math.random() * RANDOM_Y_COEFFICIENT));
float x = random(MIN_WIND_X_OFFSET, MAX_WIND_X_OFFSET);
// Magic with checking interval between winds
if (mWinds.size() > 1) {
y = 0;
while (y == 0) {
float tmp = (float) (mParent.getTotalDragDistance() / (Math.random() * RANDOM_Y_COEFFICIENT));
for (Map.Entry<Float, Float> wind : mWinds.entrySet()) {
// We want that interval will be greater than fifth part of draggable distance
if (Math.abs(wind.getKey() - tmp) > mParent.getTotalDragDistance() / RANDOM_Y_COEFFICIENT) {
y = tmp;
} else {
y = 0;
break;
}
}
}
}
mWinds.put(y, x);
drawWind(canvas, y, x);
}
// Draw current set of wind
if (mWinds.size() >= WIND_SET_AMOUNT) {
for (Map.Entry<Float, Float> wind : mWinds.entrySet()) {
drawWind(canvas, wind.getKey(), wind.getValue());
}
}
// We should to create new set of winds
if (mInverseDirection && mNewWindSet) {
mWinds.clear();
mNewWindSet = false;
mWindLineWidth = random(MIN_WIND_LINE_WIDTH, MAX_WIND_LINE_WIDTH);
}
// needed for checking direction
mLastAnimationTime = mLoadingAnimationTime;
}
drawJet(canvas);
drawSideClouds(canvas);
drawCenterClouds(canvas);
canvas.restoreToCount(saveCount);
}
/**
* Draw wind on loading animation
*
* @param canvas - area where we will draw
* @param y - y position fot one of lines
* @param xOffset - x offset for on of lines
*/
private void drawWind(Canvas canvas, float y, float xOffset) {
/* We should multiply current animation time with this coefficient for taking all screen width in time
Removing slowing of animation with dividing on {@LINK #SLOW_DOWN_ANIMATION_COEFFICIENT}
And we should don't forget about distance that should "fly" line that depend on screen of device and x offset
*/
float cof = (mScreenWidth + xOffset) / (LOADING_ANIMATION_COEFFICIENT / SLOW_DOWN_ANIMATION_COEFFICIENT);
float time = mLoadingAnimationTime;
// HORRIBLE HACK FOR REVERS ANIMATION THAT SHOULD WORK LIKE RESTART ANIMATION
if (mLastAnimationTime - mLoadingAnimationTime > 0) {
mInverseDirection = true;
// take time from 0 to end of animation time
time = (LOADING_ANIMATION_COEFFICIENT / SLOW_DOWN_ANIMATION_COEFFICIENT) - mLoadingAnimationTime;
} else {
mNewWindSet = true;
mInverseDirection = false;
}
// Taking current x position of drawing wind
// For fully disappearing of line we should subtract wind line width
float x = (mScreenWidth - (time * cof)) + xOffset - mWindLineWidth;
float xEnd = x + mWindLineWidth;
canvas.drawLine(x, y, xEnd, y, mWindPaint);
}
private void drawSideClouds(Canvas canvas) {
Matrix matrixLeftClouds = mMatrix;
Matrix matrixRightClouds = mAdditionalMatrix;
matrixLeftClouds.reset();
matrixRightClouds.reset();
// Drag percent will newer get more then 1 here
float dragPercent = Math.min(1f, Math.abs(mPercent));
boolean overdrag = false;
// But we check here for overdrag
if (mPercent > 1.0f) {
overdrag = true;
}
float scale;
float scalePercentDelta = dragPercent - SCALE_START_PERCENT;
if (scalePercentDelta > 0) {
float scalePercent = scalePercentDelta / (1.0f - SCALE_START_PERCENT);
scale = SIDE_CLOUDS_INITIAL_SCALE + (SIDE_CLOUDS_FINAL_SCALE - SIDE_CLOUDS_INITIAL_SCALE) * scalePercent;
} else {
scale = SIDE_CLOUDS_INITIAL_SCALE;
}
// Current y position of clouds
float dragYOffset = mParent.getTotalDragDistance() * (1.0f - dragPercent);
// Position where clouds fully visible on screen and we should drag them with content of listView
int cloudsVisiblePosition = mParent.getTotalDragDistance() / 2 - mLeftCloudsHeightCenter;
boolean needMoveCloudsWithContent = false;
if (dragYOffset < cloudsVisiblePosition) {
needMoveCloudsWithContent = true;
}
float offsetRightX = mScreenWidth - mRightClouds.getWidth();
float offsetRightY = (needMoveCloudsWithContent
? mParent.getTotalDragDistance() * dragPercent - mLeftClouds.getHeight()
: dragYOffset)
+ (overdrag ? mTop : 0);
float offsetLeftX = 0;
float offsetLeftY = (needMoveCloudsWithContent
? mParent.getTotalDragDistance() * dragPercent - mLeftClouds.getHeight()
: dragYOffset)
+ (overdrag ? mTop : 0);
// Magic with animation on loading process
if (isRefreshing) {
if (checkCurrentAnimationPart(AnimationPart.FIRST)) {
offsetLeftY += getAnimationPartValue(AnimationPart.FIRST) / Y_SIDE_CLOUDS_SLOW_DOWN_COF;
offsetRightX -= getAnimationPartValue(AnimationPart.FIRST) / X_SIDE_CLOUDS_SLOW_DOWN_COF;
} else if (checkCurrentAnimationPart(AnimationPart.SECOND)) {
offsetLeftY += getAnimationPartValue(AnimationPart.SECOND) / Y_SIDE_CLOUDS_SLOW_DOWN_COF;
offsetRightX -= getAnimationPartValue(AnimationPart.SECOND) / X_SIDE_CLOUDS_SLOW_DOWN_COF;
} else if (checkCurrentAnimationPart(AnimationPart.THIRD)) {
offsetLeftY -= getAnimationPartValue(AnimationPart.THIRD) / Y_SIDE_CLOUDS_SLOW_DOWN_COF;
offsetRightX += getAnimationPartValue(AnimationPart.THIRD) / X_SIDE_CLOUDS_SLOW_DOWN_COF;
} else if (checkCurrentAnimationPart(AnimationPart.FOURTH)) {
offsetLeftY -= getAnimationPartValue(AnimationPart.FOURTH) / X_SIDE_CLOUDS_SLOW_DOWN_COF;
offsetRightX += getAnimationPartValue(AnimationPart.FOURTH) / Y_SIDE_CLOUDS_SLOW_DOWN_COF;
}
}
matrixRightClouds.postScale(scale, scale, mRightCloudsWidthCenter, mRightCloudsHeightCenter);
matrixRightClouds.postTranslate(offsetRightX, offsetRightY);
matrixLeftClouds.postScale(scale, scale, mLeftCloudsWidthCenter, mLeftCloudsHeightCenter);
matrixLeftClouds.postTranslate(offsetLeftX, offsetLeftY);
canvas.drawBitmap(mLeftClouds, matrixLeftClouds, null);
canvas.drawBitmap(mRightClouds, matrixRightClouds, null);
}
private void drawCenterClouds(Canvas canvas) {
Matrix matrix = mMatrix;
matrix.reset();
float dragPercent = Math.min(1f, Math.abs(mPercent));
float scale;
float overdragPercent = 0;
boolean overdrag = false;
if (mPercent > 1.0f) {
overdrag = true;
// Here we want know about how mach percent of over drag we done
overdragPercent = Math.abs(1.0f - mPercent);
}
float scalePercentDelta = dragPercent - SCALE_START_PERCENT;
if (scalePercentDelta > 0) {
float scalePercent = scalePercentDelta / (1.0f - SCALE_START_PERCENT);
scale = CENTER_CLOUDS_INITIAL_SCALE + (CENTER_CLOUDS_FINAL_SCALE - CENTER_CLOUDS_INITIAL_SCALE) * scalePercent;
} else {
scale = CENTER_CLOUDS_INITIAL_SCALE;
}
float parallaxPercent = 0;
boolean parallax = false;
// Current y position of clouds
float dragYOffset = mParent.getTotalDragDistance() * dragPercent;
// Position when should start parallax scrolling
int startParallaxHeight = mParent.getTotalDragDistance() - mFrontCloudHeightCenter;
if (dragYOffset > startParallaxHeight) {
parallax = true;
parallaxPercent = dragYOffset - startParallaxHeight;
}
float offsetX = (mScreenWidth / 2) - mFrontCloudWidthCenter;
float offsetY = dragYOffset
- (parallax ? mFrontCloudHeightCenter + parallaxPercent : mFrontCloudHeightCenter)
+ (overdrag ? mTop : 0);
float sx = overdrag ? scale + overdragPercent / 4 : scale;
float sy = overdrag ? scale + overdragPercent / 2 : scale;
if (isRefreshing && !overdrag) {
if (checkCurrentAnimationPart(AnimationPart.FIRST)) {
sx = scale - (getAnimationPartValue(AnimationPart.FIRST) / LOADING_ANIMATION_COEFFICIENT) / 8;
} else if (checkCurrentAnimationPart(AnimationPart.SECOND)) {
sx = scale - (getAnimationPartValue(AnimationPart.SECOND) / LOADING_ANIMATION_COEFFICIENT) / 8;
} else if (checkCurrentAnimationPart(AnimationPart.THIRD)) {
sx = scale + (getAnimationPartValue(AnimationPart.THIRD) / LOADING_ANIMATION_COEFFICIENT) / 6;
} else if (checkCurrentAnimationPart(AnimationPart.FOURTH)) {
sx = scale + (getAnimationPartValue(AnimationPart.FOURTH) / LOADING_ANIMATION_COEFFICIENT) / 6;
}
sy = sx;
}
matrix.postScale(sx, sy, mFrontCloudWidthCenter, mFrontCloudHeightCenter);
matrix.postTranslate(offsetX, offsetY);
canvas.drawBitmap(mFrontClouds, matrix, null);
}
private void drawJet(Canvas canvas) {
Matrix matrix = mMatrix;
matrix.reset();
float dragPercent = mPercent;
float rotateAngle = 0;
// Check overdrag
if (dragPercent > 1.0f && !mEndOfRefreshing) {
rotateAngle = (dragPercent % 1) * 10;
dragPercent = 1.0f;
}
float offsetX = ((mScreenWidth * dragPercent) / 2) - mJetWidthCenter;
float offsetY = mJetTopOffset
+ (mParent.getTotalDragDistance() / 2)
* (1.0f - dragPercent)
- mJetHeightCenter;
if (isRefreshing) {
if (checkCurrentAnimationPart(AnimationPart.FIRST)) {
offsetY -= getAnimationPartValue(AnimationPart.FIRST);
} else if (checkCurrentAnimationPart(AnimationPart.SECOND)) {
offsetY -= getAnimationPartValue(AnimationPart.SECOND);
} else if (checkCurrentAnimationPart(AnimationPart.THIRD)) {
offsetY += getAnimationPartValue(AnimationPart.THIRD);
} else if (checkCurrentAnimationPart(AnimationPart.FOURTH)) {
offsetY += getAnimationPartValue(AnimationPart.FOURTH);
}
}
matrix.setTranslate(offsetX, offsetY);
if (dragPercent == 1.0f) {
matrix.preRotate(rotateAngle, mJetWidthCenter, mJetHeightCenter);
}
canvas.drawBitmap(mJet, matrix, null);
}
public float random(int min, int max) {
// nextInt is normally exclusive of the top value,
// so add 1 to make it inclusive
return mRandom.nextInt((max - min) + 1) + min;
}
/**
* We need a special value for different part of animation
*
* @param part - needed part
* @return - value for needed part
*/
private float getAnimationPartValue(AnimationPart part) {
switch (part) {
case FIRST: {
return mLoadingAnimationTime;
}
case SECOND: {
return getAnimationTimePart(AnimationPart.FOURTH) - (mLoadingAnimationTime - getAnimationTimePart(AnimationPart.FOURTH));
}
case THIRD: {
return mLoadingAnimationTime - getAnimationTimePart(AnimationPart.SECOND);
}
case FOURTH: {
return getAnimationTimePart(AnimationPart.THIRD) - (mLoadingAnimationTime - getAnimationTimePart(AnimationPart.FOURTH));
}
default:
return 0;
}
}
/**
* On drawing we should check current part of animation
*
* @param part - needed part of animation
* @return - return true if current part
*/
private boolean checkCurrentAnimationPart(AnimationPart part) {
switch (part) {
case FIRST: {
return mLoadingAnimationTime < getAnimationTimePart(AnimationPart.FOURTH);
}
case SECOND:
case THIRD: {
return mLoadingAnimationTime < getAnimationTimePart(part);
}
case FOURTH: {
return mLoadingAnimationTime > getAnimationTimePart(AnimationPart.THIRD);
}
default:
return false;
}
}
/**
* Get part of animation duration
*
* @param part - needed part of time
* @return - interval of time
*/
private int getAnimationTimePart(AnimationPart part) {
switch (part) {
case SECOND: {
return LOADING_ANIMATION_COEFFICIENT / 2;
}
case THIRD: {
return getAnimationTimePart(AnimationPart.FOURTH) * 3;
}
case FOURTH: {
return LOADING_ANIMATION_COEFFICIENT / 4;
}
default:
return 0;
}
}
public void setPercent(float percent) {
mPercent = percent;
}
public void resetOriginals() {
setPercent(0);
}
@Override
protected void onBoundsChange(@NonNull Rect bounds) {
super.onBoundsChange(bounds);
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
@Override
public boolean isRunning() {
return false;
}
@Override
public void start() {
mAnimation.reset();
isRefreshing = true;
mParent.startAnimation(mAnimation);
mLastAnimationTime = 0;
mWinds.clear();
mWindLineWidth = random(MIN_WIND_LINE_WIDTH, MAX_WIND_LINE_WIDTH);
}
@Override
public void stop() {
mParent.clearAnimation();
isRefreshing = false;
mEndOfRefreshing = false;
resetOriginals();
}
private void setupAnimations() {
mAnimation = new Animation() {
@Override
public void applyTransformation(float interpolatedTime, @NonNull Transformation t) {
setLoadingAnimationTime(interpolatedTime);
}
};
mAnimation.setRepeatCount(Animation.INFINITE);
mAnimation.setRepeatMode(Animation.REVERSE);
mAnimation.setInterpolator(ACCELERATE_DECELERATE_INTERPOLATOR);
mAnimation.setDuration(ANIMATION_DURATION);
}
private void setLoadingAnimationTime(float loadingAnimationTime) {
/**SLOW DOWN ANIMATION IN {@link #SLOW_DOWN_ANIMATION_COEFFICIENT} time */
mLoadingAnimationTime = LOADING_ANIMATION_COEFFICIENT * (loadingAnimationTime / SLOW_DOWN_ANIMATION_COEFFICIENT);
invalidateSelf();
}
}
<?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:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/home_bg">
<!-- body -->
<!-- 下拉刷新 -->
<com.baofoo.mobile.wallet.common.view.pullable.PullToRefreshView
android:id="@+id/rv_pull"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- your body -->
</com.baofoo.mobile.wallet.common.view.pullable.PullToRefreshView>
<!-- 头部 -->
<LinearLayout
android:id="@+id/rl_top_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:orientation="vertical"
android:visibility="gone"
android:background="@color/main_title_bg"
android:padding="10dp">
<TextView
android:id="@+id/tv_top_seat"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:contentDescription="占位"/>
<ImageView
android:id="@+id/image_left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/home_top_title"/>
</LinearLayout>
</RelativeLayout>强制更新使用的是notifyDataSetChanged记得使用这个方法之前需要把list数据clear一下,否则里面的内容会重复显示,但是这个方法有时候不管用,这个时候就需要强制更新了。
强制更新是重写notifyDataSetChanged方法。
所有代码:
调用代码:
HttpUtil.remoteRequest(HttpUtil.HOME_URL, params, new AbstractCallback<ResultDto>() {
@Override
public void execute(ResultDto result) {
if (result.isSuccess()) {
try {
//缓存最新json串
String objString = result.getObj().getString("obj");
FileUtils.saveJsonToLocal(objString, StaticUtils.HOMECACHAINTERFACE);
//格式化list
bannersList.clear();
noticesList.clear();
funcsList.clear();
productsList.clear();
//解析json串
try {
JSONObject jsonObj = new JSONObject(objString);
parseJson(jsonObj);
} catch (JSONException e) {
e.printStackTrace();
}
//下拉更新所有Adapter
bannersAdapter.notifyDataSetChanged();
funcsViewPagerAdapter.notifyDataSetChanged();
productsAdapter.notifyDataSetChanged();
UiUtils.showToast("刷新成功");
} catch (JSONException e) {
e.printStackTrace();
}
} else {
UiUtils.showToast("刷新失败");
}
//关闭下拉刷新动画
rvPull.setRefreshing(false);
isCloseRefreshAnim = true;
}
});
package com.baofoo.mobile.wallet.home;
import android.app.Activity;
import android.support.v4.view.PagerAdapter;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import com.baofoo.mobile.wallet.R;
import com.baofoo.mobile.wallet.common.manager.ClickFilter;
import com.baofoo.mobile.wallet.home.domain.Banners;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import java.util.List;
/**
* 首页banners Adapter
* Created by zst on 2016/3/15.
*/
public class BannersAdapter extends PagerAdapter {
private Activity activity;
private List<Banners> bannersList;
private ImageLoader imageLoader;
private DisplayImageOptions options;
private int mChildCount = 0;//强制刷新
@Override
public void notifyDataSetChanged() {//强制刷新
mChildCount = getCount();
super.notifyDataSetChanged();
}
@Override
public int getItemPosition(Object object) {//强制刷新
if (mChildCount > 0) {
mChildCount--;
return POSITION_NONE;
}
return super.getItemPosition(object);
}
public BannersAdapter(Activity activity, List<Banners> bannersList) {
this.activity = activity;
this.bannersList = bannersList;
imageLoader = ImageLoader.getInstance();
options = new DisplayImageOptions.Builder()
.showImageOnLoading(R.drawable.finance_advert_default)
.showImageForEmptyUri(R.drawable.finance_advert_default)
.showImageOnFail(R.drawable.finance_advert_default)
.cacheInMemory(true)
.cacheOnDisk(true)
.build();
}
@Override
public int getCount() {
return bannersList.size();
}
@Override
public boolean isViewFromObject(View view, Object o) {
return view == o;
}
@Override
public Object instantiateItem(ViewGroup container, final int position) {
ImageView imageView = new ImageView(activity);
imageView.setScaleType(ImageView.ScaleType.FIT_XY);// 基于控件大小填充图片
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
ClickFilter.clickFilterUrl(activity, bannersList.get(position).click);
}
});
imageLoader.displayImage(bannersList.get(position).img, imageView, options);
container.addView(imageView);
return imageView;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
}
android下拉刷新 & 强制刷新notifyDataSetChanged
标签:
原文地址:http://blog.csdn.net/u012246458/article/details/51034046