项目地址:https://git.oschina.net/whos/SwipeRefreshAndLoadLayout/wikis/home
关于Google推出的下拉刷新控件SwipeRefreshLayout的相关使用方法,大家可以去参考http://blog.csdn.net/geeklei/article/details/38876981,本文也借鉴了其中的一些内容和“颜路的博客”中《官方下拉刷新SwipeRefreshLayout增加上拉加载更多》一文。
话不多说,直接先上改造效果图(截屏时卡,凑合看吧):
下拉刷新和上拉加载
简单讲下原始代码的原理:
下拉时,计算手指移动距离,如果超过一个系统默认的临界值mTouchSlop,该事件就不下发到子控件进行处理,而是SwipeRefreshLayout自己处理。
变量mDistanceToTriggerSync指定了下拉刷新的临界值,如果下拉距离没有大于该值,则计算下拉距离和mDistanceToTriggerSync的比值,并用该值作为进度百分比对进度条mProgressBar进行设置,同时移动子控件(ListView之类)的位置,屏幕上可以看到进度条颜色缓慢拉长的动画,同时子控件向下移动。
如果下拉距离大于mDistanceToTriggerSync,则设置动画把子控件位置复位,然后启动下拉刷新的色条循环动画,并执行下拉刷新的监听事件。
关于进度条SwipeProgressBar的动画显示,Google的代码里埋藏了一个坑人的陷阱。现象就是如果你在底部加了进度条,动画效果异常,不会出现渐变的色条,只是生硬的转换。上面参考的文章里也碰到了这个问题。其实原因很简单,看下图:
把进度条SwipeProgressBar的高度设置大了后,可以看出其动画效果是在进度条的中心向外部循环画圆,每个循环中圆的颜色不同。重点是圆心的位置。
看SwipeProgressBar的如下代码,会发现在计算圆心高度cy的时候,取值是进度条高度的一半,这样的话圆心会一直在上面,底部进度条自然动画异常
- void draw(Canvas canvas) {
- final int width = mBounds.width();
- final int height = mBounds.height();
- final int cx = width / 2;
- final int cy = height / 2;
- boolean drawTriggerWhileFinishing = false;
- int restoreCount = canvas.save();
- canvas.clipRect(mBounds);
修改SwipeProgressBar的代码,使其圆心在所在进度条的中心:
- void draw(Canvas canvas) {
- final int width = mBounds.width();
- final int height = mBounds.height();
- final int cx = width / 2;
-
- final int cy = mBounds.bottom - height / 2;
- boolean drawTriggerWhileFinishing = false;
- int restoreCount = canvas.save();
- canvas.clipRect(mBounds);
效果如图:
明白了原始代码的原理,就好入手进行修改了,修改的代码会在后面贴出来,注释很详细,这里就不具体分析了。对SDK<14的滑动部分暂时没有进行处理,直接返回了false,待后续改进。(已改进)
下面看修改后的功能:
1.可设置是否打开下拉刷新功能,可设置是否打开上拉加载功能,默认全部打开。
2.可设置是否在数据不满一屏的情况下打开上拉加载功能,默认关闭。
3.可单独设置上下进度条的颜色,也可同时设置一样的颜色。
啰嗦了这么多,上代码:
SwipeProgressBar:
- package com.dahuo.learn.swiperefreshandload.view;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- import android.graphics.Canvas;
- import android.graphics.Paint;
- import android.graphics.Rect;
- import android.graphics.RectF;
- import android.support.v4.view.ViewCompat;
- import android.view.View;
- import android.view.animation.AnimationUtils;
- import android.view.animation.Interpolator;
-
-
-
-
-
-
-
-
-
- final class SwipeProgressBar {
-
-
- private final static int COLOR1 = 0xB3000000;
- private final static int COLOR2 = 0x80000000;
- private final static int COLOR3 = 0x4d000000;
- private final static int COLOR4 = 0x1a000000;
-
-
- private static final int ANIMATION_DURATION_MS = 2000;
-
-
- private static final int FINISH_ANIMATION_DURATION_MS = 1000;
-
-
- private static final Interpolator INTERPOLATOR = BakedBezierInterpolator.getInstance();
-
- private final Paint mPaint = new Paint();
- private final RectF mClipRect = new RectF();
- private float mTriggerPercentage;
- private long mStartTime;
- private long mFinishTime;
- private boolean mRunning;
-
-
- private int mColor1;
- private int mColor2;
- private int mColor3;
- private int mColor4;
- private View mParent;
-
- private Rect mBounds = new Rect();
-
- public SwipeProgressBar(View parent) {
- mParent = parent;
- mColor1 = COLOR1;
- mColor2 = COLOR2;
- mColor3 = COLOR3;
- mColor4 = COLOR4;
- }
-
-
-
-
-
-
-
-
-
-
-
- void setColorScheme(int color1, int color2, int color3, int color4) {
- mColor1 = color1;
- mColor2 = color2;
- mColor3 = color3;
- mColor4 = color4;
- }
-
-
-
-
-
-
- void setTriggerPercentage(float triggerPercentage) {
- mTriggerPercentage = triggerPercentage;
- mStartTime = 0;
- ViewCompat.postInvalidateOnAnimation(mParent);
- }
-
-
-
-
- void start() {
- if (!mRunning) {
- mTriggerPercentage = 0;
- mStartTime = AnimationUtils.currentAnimationTimeMillis();
- mRunning = true;
- mParent.postInvalidate();
- }
- }
-
-
-
-
- void stop() {
- if (mRunning) {
- mTriggerPercentage = 0;
- mFinishTime = AnimationUtils.currentAnimationTimeMillis();
- mRunning = false;
- mParent.postInvalidate();
- }
- }
-
-
-
-
- boolean isRunning() {
- return mRunning || mFinishTime > 0;
- }
-
- void draw(Canvas canvas) {
- final int width = mBounds.width();
- final int height = mBounds.height();
- final int cx = width / 2;
-
- final int cy = mBounds.bottom - height / 2;
- boolean drawTriggerWhileFinishing = false;
- int restoreCount = canvas.save();
- canvas.clipRect(mBounds);
-
- if (mRunning || (mFinishTime > 0)) {
- long now = AnimationUtils.currentAnimationTimeMillis();
- long elapsed = (now - mStartTime) % ANIMATION_DURATION_MS;
- long iterations = (now - mStartTime) / ANIMATION_DURATION_MS;
- float rawProgress = (elapsed / (ANIMATION_DURATION_MS / 100f));
-
-
-
- if (!mRunning) {
-
-
- if ((now - mFinishTime) >= FINISH_ANIMATION_DURATION_MS) {
- mFinishTime = 0;
- return;
- }
-
-
-
-
- long finishElapsed = (now - mFinishTime) % FINISH_ANIMATION_DURATION_MS;
- float finishProgress = (finishElapsed / (FINISH_ANIMATION_DURATION_MS / 100f));
- float pct = (finishProgress / 100f);
-
- float clearRadius = width / 2 * INTERPOLATOR.getInterpolation(pct);
- mClipRect.set(cx - clearRadius, 0, cx + clearRadius, height);
- canvas.saveLayerAlpha(mClipRect, 0, 0);
-
-
-
-
- drawTriggerWhileFinishing = true;
- }
-
-
- if (iterations == 0) {
- canvas.drawColor(mColor1);
- } else {
- if (rawProgress >= 0 && rawProgress < 25) {
- canvas.drawColor(mColor4);
- } else if (rawProgress >= 25 && rawProgress < 50) {
- canvas.drawColor(mColor1);
- } else if (rawProgress >= 50 && rawProgress < 75) {
- canvas.drawColor(mColor2);
- } else {
- canvas.drawColor(mColor3);
- }
- }
-
-
-
-
-
-
-
- if ((rawProgress >= 0 && rawProgress <= 25)) {
- float pct = (((rawProgress + 25) * 2) / 100f);
- drawCircle(canvas, cx, cy, mColor1, pct);
- }
- if (rawProgress >= 0 && rawProgress <= 50) {
- float pct = ((rawProgress * 2) / 100f);
- drawCircle(canvas, cx, cy, mColor2, pct);
- }
- if (rawProgress >= 25 && rawProgress <= 75) {
- float pct = (((rawProgress - 25) * 2) / 100f);
- drawCircle(canvas, cx, cy, mColor3, pct);
- }
- if (rawProgress >= 50 && rawProgress <= 100) {
- float pct = (((rawProgress - 50) * 2) / 100f);
- drawCircle(canvas, cx, cy, mColor4, pct);
- }
- if ((rawProgress >= 75 && rawProgress <= 100)) {
- float pct = (((rawProgress - 75) * 2) / 100f);
- drawCircle(canvas, cx, cy, mColor1, pct);
- }
- if (mTriggerPercentage > 0 && drawTriggerWhileFinishing) {
-
-
-
-
- canvas.restoreToCount(restoreCount);
- restoreCount = canvas.save();
- canvas.clipRect(mBounds);
- drawTrigger(canvas, cx, cy);
- }
-
- ViewCompat.postInvalidateOnAnimation(mParent);
- } else {
-
- if (mTriggerPercentage > 0 && mTriggerPercentage <= 1.0) {
- drawTrigger(canvas, cx, cy);
- }
- }
- canvas.restoreToCount(restoreCount);
- }
-
- private void drawTrigger(Canvas canvas, int cx, int cy) {
- mPaint.setColor(mColor1);
- canvas.drawCircle(cx, cy, cx * mTriggerPercentage, mPaint);
- }
-
-
-
-
-
-
-
-
-
-
- private void drawCircle(Canvas canvas, float cx, float cy, int color, float pct) {
- mPaint.setColor(color);
- canvas.save();
- canvas.translate(cx, cy);
- float radiusScale = INTERPOLATOR.getInterpolation(pct);
- canvas.scale(radiusScale, radiusScale);
- canvas.drawCircle(0, 0, cx, mPaint);
- canvas.restore();
- }
-
-
-
-
- void setBounds(int left, int top, int right, int bottom) {
- mBounds.left = left;
- mBounds.top = top;
- mBounds.right = right;
- mBounds.bottom = bottom;
- }
- }
SwipeRefreshLayout:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- package com.dahuo.learn.swiperefreshandload.view;
-
- import android.content.Context;
- import android.content.res.Resources;
- import android.content.res.TypedArray;
- import android.graphics.Canvas;
- import android.support.v4.view.MotionEventCompat;
- import android.support.v4.view.ViewCompat;
- import android.util.AttributeSet;
- import android.util.DisplayMetrics;
- import android.util.Log;
- import android.view.MotionEvent;
- import android.view.View;
- import android.view.ViewConfiguration;
- import android.view.ViewGroup;
- import android.view.animation.AccelerateInterpolator;
- import android.view.animation.Animation;
- import android.view.animation.Animation.AnimationListener;
- import android.view.animation.DecelerateInterpolator;
- import android.view.animation.Transformation;
- import android.widget.AbsListView;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- public class SwipeRefreshLayout extends ViewGroup {
- private static final String LOG_TAG = SwipeRefreshLayout.class.getSimpleName();
-
- private static final long RETURN_TO_ORIGINAL_POSITION_TIMEOUT = 300;
- private static final float ACCELERATE_INTERPOLATION_FACTOR = 1.5f;
- private static final float DECELERATE_INTERPOLATION_FACTOR = 2f;
- private static final float PROGRESS_BAR_HEIGHT = 4;
- private static final float MAX_SWIPE_DISTANCE_FACTOR = .6f;
- private static final int REFRESH_TRIGGER_DISTANCE = 120;
- private static final int INVALID_POINTER = -1;
-
- private SwipeProgressBar mProgressBar;
- private SwipeProgressBar mProgressBarBottom;
- private View mTarget;
- private int mOriginalOffsetTop;
- private OnRefreshListener mRefreshListener;
- private OnLoadListener mLoadListener;
- private int mFrom;
- private boolean mRefreshing = false;
- private boolean mLoading = false;
- private int mTouchSlop;
- private float mDistanceToTriggerSync = -1;
- private int mMediumAnimationDuration;
- private float mFromPercentage = 0;
- private float mCurrPercentage = 0;
- private int mProgressBarHeight;
- private int mCurrentTargetOffsetTop;
-
- private float mInitialMotionY;
- private float mLastMotionY;
- private boolean mIsBeingDragged;
- private int mActivePointerId = INVALID_POINTER;
-
-
-
- private boolean mReturningToStart;
- private final DecelerateInterpolator mDecelerateInterpolator;
- private final AccelerateInterpolator mAccelerateInterpolator;
- private static final int[] LAYOUT_ATTRS = new int[] {
- android.R.attr.enabled
- };
- private Mode mMode = Mode.getDefault();
-
-
- private Mode mLastDirection = Mode.DISABLED;
- private int mDirection = 0;
-
- private float mStartPoint;
- private boolean up;
- private boolean down;
-
- private boolean loadNoFull = false;
-
-
- private final Animation mAnimateToStartPosition = new Animation() {
- @Override
- public void applyTransformation(float interpolatedTime, Transformation t) {
- int targetTop = 0;
- if (mFrom != mOriginalOffsetTop) {
- targetTop = (mFrom + (int)((mOriginalOffsetTop - mFrom) * interpolatedTime));
- }
- int offset = targetTop - mTarget.getTop();
-
-
-
-
-
- setTargetOffsetTopAndBottom(offset);
- }
- };
-
-
- private Animation mShrinkTrigger = new Animation() {
- @Override
- public void applyTransformation(float interpolatedTime, Transformation t) {
- float percent = mFromPercentage + ((0 - mFromPercentage) * interpolatedTime);
- mProgressBar.setTriggerPercentage(percent);
- }
- };
-
-
- private Animation mShrinkTriggerBottom = new Animation() {
- @Override
- public void applyTransformation(float interpolatedTime, Transformation t) {
- float percent = mFromPercentage + ((0 - mFromPercentage) * interpolatedTime);
- mProgressBarBottom.setTriggerPercentage(percent);
- }
- };
-
-
- private final AnimationListener mReturnToStartPositionListener = new BaseAnimationListener() {
- @Override
- public void onAnimationEnd(Animation animation) {
-
-
- mCurrentTargetOffsetTop = 0;
- mLastDirection = Mode.DISABLED;
- }
- };
-
-
- private final AnimationListener mShrinkAnimationListener = new BaseAnimationListener() {
- @Override
- public void onAnimationEnd(Animation animation) {
- mCurrPercentage = 0;
- }
- };
-
-
- private final Runnable mReturnToStartPosition = new Runnable() {
-
- @Override
- public void run() {
- mReturningToStart = true;
- animateOffsetToStartPosition(mCurrentTargetOffsetTop + getPaddingTop(),
- mReturnToStartPositionListener);
- }
-
- };
-
-
- private final Runnable mCancel = new Runnable() {
-
- @Override
- public void run() {
- mReturningToStart = true;
-
-
- if (mProgressBar != null || mProgressBarBottom != null) {
- mFromPercentage = mCurrPercentage;
- if(mDirection > 0 && ((mMode == Mode.PULL_FROM_START) || (mMode == Mode.BOTH)))
- {
- mShrinkTrigger.setDuration(mMediumAnimationDuration);
- mShrinkTrigger.setAnimationListener(mShrinkAnimationListener);
- mShrinkTrigger.reset();
- mShrinkTrigger.setInterpolator(mDecelerateInterpolator);
- startAnimation(mShrinkTrigger);
- }
- else if(mDirection < 0 && ((mMode == Mode.PULL_FROM_END) || (mMode == Mode.BOTH)))
- {
- mShrinkTriggerBottom.setDuration(mMediumAnimationDuration);
- mShrinkTriggerBottom.setAnimationListener(mShrinkAnimationListener);
- mShrinkTriggerBottom.reset();
- mShrinkTriggerBottom.setInterpolator(mDecelerateInterpolator);
- startAnimation(mShrinkTriggerBottom);
- }
- }
- mDirection = 0;
- animateOffsetToStartPosition(mCurrentTargetOffsetTop + getPaddingTop(),
- mReturnToStartPositionListener);
- }
-
- };
-
-
-
-
-
- public SwipeRefreshLayout(Context context) {
- this(context, null);
- }
-
-
-
-
-
-
- public SwipeRefreshLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
-
- mMediumAnimationDuration = getResources().getInteger(
- android.R.integer.config_mediumAnimTime);
-
- setWillNotDraw(false);
- mProgressBar = new SwipeProgressBar(this);
- mProgressBarBottom = new SwipeProgressBar(this);
- final DisplayMetrics metrics = getResources().getDisplayMetrics();
- mProgressBarHeight = (int) (metrics.density * PROGRESS_BAR_HEIGHT);
- mDecelerateInterpolator = new DecelerateInterpolator(DECELERATE_INTERPOLATION_FACTOR);
- mAccelerateInterpolator = new AccelerateInterpolator(ACCELERATE_INTERPOLATION_FACTOR);
-
- final TypedArray a = context.obtainStyledAttributes(attrs, LAYOUT_ATTRS);
- setEnabled(a.getBoolean(0, true));
- a.recycle();
- }
-
- @Override
- public void onAttachedToWindow() {
- super.onAttachedToWindow();
- removeCallbacks(mCancel);
- removeCallbacks(mReturnToStartPosition);
- }
-
- @Override
- public void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- removeCallbacks(mReturnToStartPosition);
- removeCallbacks(mCancel);
- }
-
-
- private void animateOffsetToStartPosition(int from, AnimationListener listener) {
- mFrom = from;
- mAnimateToStartPosition.reset();
- mAnimateToStartPosition.setDuration(mMediumAnimationDuration);
- mAnimateToStartPosition.setAnimationListener(listener);
- mAnimateToStartPosition.setInterpolator(mDecelerateInterpolator);
- mTarget.startAnimation(mAnimateToStartPosition);
- }
-
-
-
-
-
- public void setOnRefreshListener(OnRefreshListener listener) {
- mRefreshListener = listener;
- }
-
- public void setOnLoadListener(OnLoadListener listener) {
- mLoadListener = listener;
- }
-
-
- private void setTriggerPercentage(float percent) {
- if (percent == 0f) {
-
-
- mCurrPercentage = 0;
- return;
- }
- mCurrPercentage = percent;
- if (((mMode == Mode.PULL_FROM_START) || (mMode == Mode.BOTH))
- && mLastDirection != Mode.PULL_FROM_END && !mLoading)
- {
- mProgressBar.setTriggerPercentage(percent);
- }
- else if(((mMode == Mode.PULL_FROM_END) || (mMode == Mode.BOTH))
- && mLastDirection != Mode.PULL_FROM_START && !mRefreshing)
- {
- mProgressBarBottom.setTriggerPercentage(percent);
- }
- }
-
-
-
-
-
-
-
- public void setRefreshing(boolean refreshing) {
- if (mRefreshing != refreshing) {
- ensureTarget();
- mCurrPercentage = 0;
- mRefreshing = refreshing;
- if (mRefreshing) {
- mProgressBar.start();
- } else {
- mLastDirection = Mode.DISABLED;
- mProgressBar.stop();
- }
- }
- }
-
- public void setLoading(boolean loading) {
- if (mLoading != loading) {
- ensureTarget();
- mCurrPercentage = 0;
- mLoading = loading;
- if (mLoading) {
- mProgressBarBottom.start();
- } else {
- mLastDirection = Mode.DISABLED;
- mProgressBarBottom.stop();
- }
- }
- }
-
-
-
-
- @Deprecated
- private void setColorScheme(int colorRes1, int colorRes2, int colorRes3, int colorRes4) {
- setColorSchemeResources(colorRes1, colorRes2, colorRes3, colorRes4);
- }
-
-
-
-
-
-
- public void setTopColor(int colorRes1, int colorRes2, int colorRes3,
- int colorRes4)
- {
- setColorSchemeResources(colorRes1, colorRes2, colorRes3, colorRes4);
- }
-
- public void setBottomColor(int colorRes1, int colorRes2, int colorRes3,
- int colorRes4)
- {
- setColorSchemeResourcesBottom(colorRes1, colorRes2, colorRes3, colorRes4);
- }
-
- public void setColor(int colorRes1, int colorRes2, int colorRes3,
- int colorRes4){
- setColorSchemeResources(colorRes1, colorRes2, colorRes3, colorRes4);
- setColorSchemeResourcesBottom(colorRes1, colorRes2, colorRes3, colorRes4);
- }
-
- private void setColorSchemeResources(int colorRes1, int colorRes2, int colorRes3,
- int colorRes4) {
- final Resources res = getResources();
- setColorSchemeColors(res.getColor(colorRes1), res.getColor(colorRes2),
- res.getColor(colorRes3), res.getColor(colorRes4));
- }
-
- private void setColorSchemeResourcesBottom(int colorRes1, int colorRes2, int colorRes3,
- int colorRes4) {
- final Resources res = getResources();
- setColorSchemeColorsBottom(res.getColor(colorRes1), res.getColor(colorRes2),
- res.getColor(colorRes3), res.getColor(colorRes4));
- }
-
-
-
-
-
-
- private void setColorSchemeColors(int color1, int color2, int color3, int color4) {
- ensureTarget();
- mProgressBar.setColorScheme(color1, color2, color3, color4);
- }
-
- private void setColorSchemeColorsBottom(int color1, int color2, int color3, int color4) {
- ensureTarget();
- mProgressBarBottom.setColorScheme(color1, color2, color3, color4);
- }
-
-
-
-
- public boolean isRefreshing() {
- return mRefreshing;
- }
-
- public boolean isLoading() {
- return mLoading;
- }
-
- private void ensureTarget() {
-
- if (mTarget == null) {
- if (getChildCount() > 1 && !isInEditMode()) {
- throw new IllegalStateException(
- "SwipeRefreshLayout can host only one direct child");
- }
- mTarget = getChildAt(0);
- mOriginalOffsetTop = mTarget.getTop() + getPaddingTop();
- }
- if (mDistanceToTriggerSync == -1) {
- if (getParent() != null && ((View)getParent()).getHeight() > 0) {
- final DisplayMetrics metrics = getResources().getDisplayMetrics();
- mDistanceToTriggerSync = (int) Math.min(
- ((View) getParent()) .getHeight() * MAX_SWIPE_DISTANCE_FACTOR,
- REFRESH_TRIGGER_DISTANCE * metrics.density);
- }
- }
- }
-
- @Override
- public void draw(Canvas canvas) {
- super.draw(canvas);
- mProgressBar.draw(canvas);
- mProgressBarBottom.draw(canvas);
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- final int width = getMeasuredWidth();
- final int height = getMeasuredHeight();
- mProgressBar.setBounds(0, 0, width, mProgressBarHeight);
- if (getChildCount() == 0) {
- return;
- }
- final View child = getChildAt(0);
- final int childLeft = getPaddingLeft();
- final int childTop = mCurrentTargetOffsetTop + getPaddingTop();
- final int childWidth = width - getPaddingLeft() - getPaddingRight();
- final int childHeight = height - getPaddingTop() - getPaddingBottom();
- child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
- mProgressBarBottom.setBounds(0, height-mProgressBarHeight, width, height);
- }
-
- @Override
- public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- if (getChildCount() > 1 && !isInEditMode()) {
- throw new IllegalStateException("SwipeRefreshLayout can host only one direct child");
- }
- if (getChildCount() > 0) {
- getChildAt(0).measure(
- MeasureSpec.makeMeasureSpec(
- getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
- MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(
- getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
- MeasureSpec.EXACTLY));
- }
- }
-
-
-
-
-
- public 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);
- }
- }
-
- public boolean canChildScrollDown() {
- if (android.os.Build.VERSION.SDK_INT < 14) {
- if (mTarget instanceof AbsListView) {
- final AbsListView absListView = (AbsListView) mTarget;
- View lastChild = absListView.getChildAt(absListView.getChildCount() - 1);
- if (lastChild != null) {
- return (absListView.getLastVisiblePosition() == (absListView.getCount() - 1))
- && lastChild.getBottom() > absListView.getPaddingBottom();
- }
- else
- {
- return false;
- }
- } else {
- return mTarget.getHeight() - mTarget.getScrollY() > 0;
- }
- } else {
- return ViewCompat.canScrollVertically(mTarget, 1);
- }
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- ensureTarget();
-
- final int action = MotionEventCompat.getActionMasked(ev);
-
- if (mReturningToStart && action == MotionEvent.ACTION_DOWN) {
- mReturningToStart = false;
- }
-
- if (!isEnabled() || mReturningToStart) {
-
- return false;
- }
-
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- mLastMotionY = mInitialMotionY = ev.getY();
- mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
- mIsBeingDragged = false;
- mCurrPercentage = 0;
- mStartPoint = mInitialMotionY;
-
-
-
-
- up = canChildScrollUp();
- down = canChildScrollDown();
- break;
-
- case MotionEvent.ACTION_MOVE:
- if (mActivePointerId == INVALID_POINTER) {
- Log.e(LOG_TAG, "Got ACTION_MOVE event but don't have an active pointer id.");
- return false;
- }
-
- final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
- if (pointerIndex < 0) {
- Log.e(LOG_TAG, "Got ACTION_MOVE event but have an invalid active pointer id.");
- return false;
- }
-
- final float y = MotionEventCompat.getY(ev, pointerIndex);
-
- final float yDiff = y - mStartPoint;
-
- if((mLastDirection == Mode.PULL_FROM_START && yDiff < 0) ||
- (mLastDirection == Mode.PULL_FROM_END && yDiff > 0))
- {
- return false;
- }
-
-
- if ((canChildScrollUp() && yDiff > 0) || (canChildScrollDown() && yDiff < 0))
- {
- mStartPoint = y;
- }
-
-
- if (yDiff > mTouchSlop)
- {
-
- if (canChildScrollUp() || mLastDirection == Mode.PULL_FROM_END)
- {
- mIsBeingDragged = false;
- return false;
- }
- if ((mMode == Mode.PULL_FROM_START) || (mMode == Mode.BOTH))
- {
- mLastMotionY = y;
- mIsBeingDragged = true;
- mLastDirection = Mode.PULL_FROM_START;
- }
- }
-
- else if (-yDiff > mTouchSlop) {
-
- if (canChildScrollDown() || mLastDirection == Mode.PULL_FROM_START)
- {
- mIsBeingDragged = false;
- return false;
- }
-
- if (!up && !down && !loadNoFull)
- {
- mIsBeingDragged = false;
- return false;
- }
- if ((mMode == Mode.PULL_FROM_END) || (mMode == Mode.BOTH))
- {
- mLastMotionY = y;
- mIsBeingDragged = true;
- mLastDirection = Mode.PULL_FROM_END;
- }
- }
- break;
-
- case MotionEventCompat.ACTION_POINTER_UP:
- onSecondaryPointerUp(ev);
- break;
-
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- mIsBeingDragged = false;
- mCurrPercentage = 0;
- mActivePointerId = INVALID_POINTER;
- mLastDirection = Mode.DISABLED;
- break;
- }
-
- return mIsBeingDragged;
- }
-
- @Override
- public void requestDisallowInterceptTouchEvent(boolean b) {
-
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- final int action = MotionEventCompat.getActionMasked(ev);
-
- if (mReturningToStart && action == MotionEvent.ACTION_DOWN) {
- mReturningToStart = false;
- }
-
- if (!isEnabled() || mReturningToStart) {
-
- return false;
- }
-
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- mLastMotionY = mInitialMotionY = ev.getY();
- mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
- mIsBeingDragged = false;
- mCurrPercentage = 0;
- mStartPoint = mInitialMotionY;
-
- up = canChildScrollUp();
- down = canChildScrollDown();
- break;
-
- case MotionEvent.ACTION_MOVE:
- final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
- if (pointerIndex < 0) {
- Log.e(LOG_TAG, "Got ACTION_MOVE event but have an invalid active pointer id.");
- return false;
- }
-
- final float y = MotionEventCompat.getY(ev, pointerIndex);
-
- final float yDiff = y - mStartPoint;
-
- if((mLastDirection == Mode.PULL_FROM_START && yDiff < 0) ||
- (mLastDirection == Mode.PULL_FROM_END && yDiff > 0))
- {
- return true;
- }
-
- if (!mIsBeingDragged && (yDiff > 0 && mLastDirection == Mode.PULL_FROM_START)
- || (yDiff < 0 && mLastDirection == Mode.PULL_FROM_END)) {
- mIsBeingDragged = true;
- }
-
- if (mIsBeingDragged) {
-
- if (yDiff > mDistanceToTriggerSync) {
-
- if(mLastDirection == Mode.PULL_FROM_END)
- {
- return true;
-
- }
- if ((mMode == Mode.PULL_FROM_START) || (mMode == Mode.BOTH))
- {
- mLastDirection = Mode.PULL_FROM_START;
- startRefresh();
- }
- }
- else if (-yDiff > mDistanceToTriggerSync) {
- if((!up && !down && !loadNoFull) || mLastDirection == Mode.PULL_FROM_START)
- {
- return true;
- }
- if ((mMode == Mode.PULL_FROM_END) || (mMode == Mode.BOTH))
- {
- mLastDirection = Mode.PULL_FROM_END;
- startLoad();
- }
- }else {
- if (!up && !down && yDiff < 0 && !loadNoFull)
- {
- return true;
- }
-
-
- setTriggerPercentage(
- mAccelerateInterpolator.getInterpolation(
- Math.abs(yDiff) / mDistanceToTriggerSync));
- updateContentOffsetTop((int)yDiff);
- if (mTarget.getTop() == getPaddingTop()) {
-
-
-
- removeCallbacks(mCancel);
- mLastDirection = Mode.DISABLED;
- } else {
- mDirection = (yDiff > 0 ? 1 : -1);
- updatePositionTimeout();
- }
- }
- mLastMotionY = y;
- }
- break;
-
- case MotionEventCompat.ACTION_POINTER_DOWN: {
- final int index = MotionEventCompat.getActionIndex(ev);
- mLastMotionY = MotionEventCompat.getY(ev, index);
- mActivePointerId = MotionEventCompat.getPointerId(ev, index);
- break;
- }
-
- case MotionEventCompat.ACTION_POINTER_UP:
- onSecondaryPointerUp(ev);
- break;
-
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- mIsBeingDragged = false;
- mCurrPercentage = 0;
- mActivePointerId = INVALID_POINTER;
- mLastDirection = Mode.DISABLED;
- return false;
- }
-
- return true;
- }
-
- private void startRefresh() {
- if (!mLoading && !mRefreshing)
- {
- removeCallbacks(mCancel);
- mReturnToStartPosition.run();
- setRefreshing(true);
- mRefreshListener.onRefresh();
- }
- }
-
- private void startLoad() {
- if (!mLoading && !mRefreshing)
- {
- removeCallbacks(mCancel);
- mReturnToStartPosition.run();
- setLoading(true);
- mLoadListener.onLoad();
- }
- }
-
-
- private void updateContentOffsetTop(int targetTop) {
- final int currentTop = mTarget.getTop();
- if (targetTop > mDistanceToTriggerSync) {
- targetTop = (int) mDistanceToTriggerSync;
- }
-
-
-
-
- setTargetOffsetTopAndBottom(targetTop - currentTop);
- }
-
-
- private void setTargetOffsetTopAndBottom(int offset) {
- mTarget.offsetTopAndBottom(offset);
- mCurrentTargetOffsetTop = mTarget.getTop();
- }
-
- private void updatePositionTimeout() {
- removeCallbacks(mCancel);
- postDelayed(mCancel, RETURN_TO_ORIGINAL_POSITION_TIMEOUT);
- }
-
- 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;
- mLastMotionY = MotionEventCompat.getY(ev, newPointerIndex);
- mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
- }
- }
-
-
-
-
-
- public interface OnRefreshListener {
- public void onRefresh();
- }
-
- public interface OnLoadListener {
- public void onLoad();
- }
-
- public void setMode(Mode mode)
- {
- this.mMode = mode;
- }
-
- public void setLoadNoFull(boolean load)
- {
- this.loadNoFull = load;
- }
-
- public static enum Mode {
-
-
-
- DISABLED(0x0),
-
-
-
-
-
-
- PULL_FROM_START(0x1),
-
-
-
-
-
-
- PULL_FROM_END(0x2),
-
-
-
-
- BOTH(0x3);
-
- static Mode getDefault() {
- return BOTH;
- }
-
- boolean permitsPullToRefresh() {
- return !(this == DISABLED);
- }
- boolean permitsPullFromStart() {
- return (this == Mode.BOTH || this == Mode.PULL_FROM_START);
- }
- boolean permitsPullFromEnd() {
- return (this == Mode.BOTH || this == Mode.PULL_FROM_END);
- }
-
- private int mIntValue;
-
-
- Mode(int modeInt) {
- mIntValue = modeInt;
- }
-
- int getIntValue() {
- return mIntValue;
- }
-
- }
-
-
-
-
-
- private class BaseAnimationListener implements AnimationListener {
- @Override
- public void onAnimationStart(Animation animation) {
- }
-
- @Override
- public void onAnimationEnd(Animation animation) {
- }
-
- @Override
- public void onAnimationRepeat(Animation animation) {
- }
- }
- }