zoukankan      html  css  js  c++  java
  • 安卓自带下拉刷新SwipeRefreshLayout加入上拉刷新功能

    在项目里面要用到刷新库。曾经都是使用第三方的。只是看到官方出了
    

    SwipeRefreshLayout之后就用SwipeRefreshLayout。可是不知道什么原因官方SwipeRefreshLayout仅仅提供下拉刷新功能,非常多时候我们须要上拉刷新功能。所下面载v4源代码改动SwipeRefreshLayout。与之相关联的文件有两个各自是SwipeProgressBar,BakedBezierInterpolator把这三个文件复制到项目里面,改动一下包名就能够了。

    怎样实现上拉刷新功能。事实上我们看一下onInterceptTouchEvent和onTouchEvent就明确了,改的地方也不多。

    1、仿canChildScrollUp()函数写canChildScrollDown()用来推断一下能否够上拉。这里我仅仅处理api>14的。由于项目最低版本号是14的。所以我也就没有处理<14的情况

    /**
         * 检查能否够上拉,对于版本号14下面的暂不支持
         * @return
         */
        public boolean canChildScrollDown() {
            if (android.os.Build.VERSION.SDK_INT < 14) {
                if (mTarget instanceof AbsListView) {
                    final AbsListView absListView = (AbsListView) mTarget;
                   //仿照canChildScrollUp中的这部分,推断加入<14的代码
                } else {
                    return mTarget.getScrollY() > 0;
                }
            } else {
                return ViewCompat.canScrollVertically(mTarget, 1);
            }
        }
    

    2、改动onInterceptTouchEvent。加入&& (!canChildScrollUp() || !canChildScrollDown()

    @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            ensureTarget();
            boolean handled = false;
            if (mReturningToStart && ev.getAction() == MotionEvent.ACTION_DOWN) {
                mReturningToStart = false;
            }
            if (isEnabled() && !mReturningToStart && (!canChildScrollUp() || !canChildScrollDown())) {
                handled = onTouchEvent(ev);
            }
            return !handled ? super.onInterceptTouchEvent(ev) : handled;
        }

    3、改动onTouchEvent,仅仅要在 float yDiff = eventY - mDownEvent.getY();以下加入

     if (!canChildScrollDown()) {
                 yDiff = -eventY + mDownEvent.getY();
      }

    4、总体的代码例如以下:

    /*
     * Copyright (C) 2013 The Android Open Source Project
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package com.jwzhangjie.baseui.widget;
    
    import android.view.animation.Interpolator;
    
    /**
     * A pre-baked bezier-curved interpolator for indeterminate progress animations.
     */
    final class BakedBezierInterpolator implements Interpolator {
        private static final BakedBezierInterpolator INSTANCE = new BakedBezierInterpolator();
    
        public final static BakedBezierInterpolator getInstance() {
            return INSTANCE;
        }
    
        /**
         * Use getInstance instead of instantiating.
         */
        private BakedBezierInterpolator() {
            super();
        }
    
        /**
         * Lookup table values.
         * Generated using a Bezier curve from (0,0) to (1,1) with control points:
         * P0 (0,0)
         * P1 (0.4, 0)
         * P2 (0.2, 1.0)
         * P3 (1.0, 1.0)
         *
         * Values sampled with x at regular intervals between 0 and 1.
         */
        private static final float[] VALUES = new float[] {
            0.0f, 0.0002f, 0.0009f, 0.0019f, 0.0036f, 0.0059f, 0.0086f, 0.0119f, 0.0157f, 0.0209f,
            0.0257f, 0.0321f, 0.0392f, 0.0469f, 0.0566f, 0.0656f, 0.0768f, 0.0887f, 0.1033f, 0.1186f,
            0.1349f, 0.1519f, 0.1696f, 0.1928f, 0.2121f, 0.237f, 0.2627f, 0.2892f, 0.3109f, 0.3386f,
            0.3667f, 0.3952f, 0.4241f, 0.4474f, 0.4766f, 0.5f, 0.5234f, 0.5468f, 0.5701f, 0.5933f,
            0.6134f, 0.6333f, 0.6531f, 0.6698f, 0.6891f, 0.7054f, 0.7214f, 0.7346f, 0.7502f, 0.763f,
            0.7756f, 0.7879f, 0.8f, 0.8107f, 0.8212f, 0.8326f, 0.8415f, 0.8503f, 0.8588f, 0.8672f,
            0.8754f, 0.8833f, 0.8911f, 0.8977f, 0.9041f, 0.9113f, 0.9165f, 0.9232f, 0.9281f, 0.9328f,
            0.9382f, 0.9434f, 0.9476f, 0.9518f, 0.9557f, 0.9596f, 0.9632f, 0.9662f, 0.9695f, 0.9722f,
            0.9753f, 0.9777f, 0.9805f, 0.9826f, 0.9847f, 0.9866f, 0.9884f, 0.9901f, 0.9917f, 0.9931f,
            0.9944f, 0.9955f, 0.9964f, 0.9973f, 0.9981f, 0.9986f, 0.9992f, 0.9995f, 0.9998f, 1.0f, 1.0f
        };
    
        private static final float STEP_SIZE = 1.0f / (VALUES.length - 1);
    
        @Override
        public float getInterpolation(float input) {
            if (input >= 1.0f) {
                return 1.0f;
            }
    
            if (input <= 0f) {
                return 0f;
            }
    
            int position = Math.min(
                    (int)(input * (VALUES.length - 1)),
                    VALUES.length - 2);
    
            float quantized = position * STEP_SIZE;
            float difference = input - quantized;
            float weight = difference / STEP_SIZE;
    
            return VALUES[position] + weight * (VALUES[position + 1] - VALUES[position]);
        }
    
    }
    

    /*
     * Copyright (C) 2013 The Android Open Source Project
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package com.jwzhangjie.baseui.widget;
    
    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;
    
    
    /**
     * Custom progress bar that shows a cycle of colors as widening circles that
     * overdraw each other. When finished, the bar is cleared from the inside out as
     * the main cycle continues. Before running, this can also indicate how close
     * the user is to triggering something (e.g. how far they need to pull down to
     * trigger a refresh).
     */
    final class SwipeProgressBar {
    
        // Default progress animation colors are grays.
        private final static int COLOR1 = 0xB3000000;
        private final static int COLOR2 = 0x80000000;
        private final static int COLOR3 = 0x4d000000;
        private final static int COLOR4 = 0x1a000000;
    
        // The duration of the animation cycle.
        private static final int ANIMATION_DURATION_MS = 2000;
    
        // The duration of the animation to clear the bar.
        private static final int FINISH_ANIMATION_DURATION_MS = 1000;
    
        // Interpolator for varying the speed of the animation.
        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;
    
        // Colors used when rendering the animation,
        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;
        }
    
        /**
         * Set the four colors used in the progress animation. The first color will
         * also be the color of the bar that grows in response to a user swipe
         * gesture.
         *
         * @param color1 Integer representation of a color.
         * @param color2 Integer representation of a color.
         * @param color3 Integer representation of a color.
         * @param color4 Integer representation of a color.
         */
        void setColorScheme(int color1, int color2, int color3, int color4) {
            mColor1 = color1;
            mColor2 = color2;
            mColor3 = color3;
            mColor4 = color4;
        }
    
        /**
         * Update the progress the user has made toward triggering the swipe
         * gesture. and use this value to update the percentage of the trigger that
         * is shown.
         */
        void setTriggerPercentage(float triggerPercentage) {
            mTriggerPercentage = triggerPercentage;
            mStartTime = 0;
            ViewCompat.postInvalidateOnAnimation(mParent);
        }
    
        /**
         * Start showing the progress animation.
         */
        void start() {
            if (!mRunning) {
                mTriggerPercentage = 0;
                mStartTime = AnimationUtils.currentAnimationTimeMillis();
                mRunning = true;
                mParent.postInvalidate();
            }
        }
    
        /**
         * Stop showing the progress animation.
         */
        void stop() {
            if (mRunning) {
                mTriggerPercentage = 0;
                mFinishTime = AnimationUtils.currentAnimationTimeMillis();
                mRunning = false;
                mParent.postInvalidate();
            }
        }
    
        /**
         * @return Return whether the progress animation is currently running.
         */
        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 = 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 we're not running anymore, that means we're running through
                // the finish animation.
                if (!mRunning) {
                    // If the finish animation is done, don't draw anything, and
                    // don't repost.
                    if ((now - mFinishTime) >= FINISH_ANIMATION_DURATION_MS) {
                        mFinishTime = 0;
                        return;
                    }
    
                    // Otherwise, use a 0 opacity alpha layer to clear the animation
                    // from the inside out. This layer will prevent the circles from
                    // drawing within its bounds.
                    long finishElapsed = (now - mFinishTime) % FINISH_ANIMATION_DURATION_MS;
                    float finishProgress = (finishElapsed / (FINISH_ANIMATION_DURATION_MS / 100f));
                    float pct = (finishProgress / 100f);
                    // Radius of the circle is half of the screen.
                    float clearRadius = width / 2 * INTERPOLATOR.getInterpolation(pct);
                    mClipRect.set(cx - clearRadius, 0, cx + clearRadius, height);
                    canvas.saveLayerAlpha(mClipRect, 0, 0);
                    // Only draw the trigger if there is a space in the center of
                    // this refreshing view that needs to be filled in by the
                    // trigger. If the progress view is just still animating, let it
                    // continue animating.
                    drawTriggerWhileFinishing = true;
                }
    
                // First fill in with the last color that would have finished drawing.
                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);
                    }
                }
    
                // Then draw up to 4 overlapping concentric circles of varying radii, based on how far
                // along we are in the cycle.
                // progress 0-50 draw mColor2
                // progress 25-75 draw mColor3
                // progress 50-100 draw mColor4
                // progress 75 (wrap to 25) draw mColor1
                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) {
                    // There is some portion of trigger to draw. Restore the canvas,
                    // then draw the trigger. Otherwise, the trigger does not appear
                    // until after the bar has finished animating and appears to
                    // just jump in at a larger width than expected.
                    canvas.restoreToCount(restoreCount);
                    restoreCount = canvas.save();
                    canvas.clipRect(mBounds);
                    drawTrigger(canvas, cx, cy);
                }
                // Keep running until we finish out the last cycle.
                ViewCompat.postInvalidateOnAnimation(mParent);
            } else {
                // Otherwise if we're in the middle of a trigger, draw that.
                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);
        }
    
        /**
         * Draws a circle centered in the view.
         *
         * @param canvas the canvas to draw on
         * @param cx the center x coordinate
         * @param cy the center y coordinate
         * @param color the color to draw
         * @param pct the percentage of the view that the circle should cover
         */
        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();
        }
    
        /**
         * Set the drawing bounds of this SwipeProgressBar.
         */
        void setBounds(int left, int top, int right, int bottom) {
            mBounds.left = left;
            mBounds.top = top;
            mBounds.right = right;
            mBounds.bottom = bottom;
        }
    }

    /*
     * Copyright (C) 2013 The Android Open Source Project
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package com.jwzhangjie.baseui.widget;
    
    import com.jwzhangjie.baseui.utils.AppLog;
    
    import android.content.Context;
    import android.content.res.Resources;
    import android.content.res.TypedArray;
    import android.graphics.Canvas;
    import android.support.v4.view.ViewCompat;
    import android.util.AttributeSet;
    import android.util.DisplayMetrics;
    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;
    
    
    /**
     * The SwipeRefreshLayout should be used whenever the user can refresh the
     * contents of a view via a vertical swipe gesture. The activity that
     * instantiates this view should add an OnRefreshListener to be notified
     * whenever the swipe to refresh gesture is completed. The SwipeRefreshLayout
     * will notify the listener each and every time the gesture is completed again;
     * the listener is responsible for correctly determining when to actually
     * initiate a refresh of its content. If the listener determines there should
     * not be a refresh, it must call setRefreshing(false) to cancel any visual
     * indication of a refresh. If an activity wishes to show just the progress
     * animation, it should call setRefreshing(true). To disable the gesture and progress
     * animation, call setEnabled(false) on the view.
     *
     * <p> This layout should be made the parent of the view that will be refreshed as a
     * result of the gesture and can only support one direct child. This view will
     * also be made the target of the gesture and will be forced to match both the
     * width and the height supplied in this layout. The SwipeRefreshLayout does not
     * provide accessibility events; instead, a menu item must be provided to allow
     * refresh of the content wherever this gesture is used.</p>
     */
    public class SwipeRefreshLayout extends ViewGroup {
        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 SwipeProgressBar mProgressBar; //the thing that shows progress is going
        private View mTarget; //the content that gets pulled down
        private int mOriginalOffsetTop;
        private OnRefreshListener mListener;
        private MotionEvent mDownEvent;
        private int mFrom;
        private boolean mRefreshing = false;
        private int mTouchSlop;
        private float mDistanceToTriggerSync = -1;
        private float mPrevY;
        private int mMediumAnimationDuration;
        private float mFromPercentage = 0;
        private float mCurrPercentage = 0;
        private int mProgressBarHeight;
        private int mCurrentTargetOffsetTop;
        // Target is returning to its start offset because it was cancelled or a
        // refresh was triggered.
        private boolean mReturningToStart;
        private final DecelerateInterpolator mDecelerateInterpolator;
        private final AccelerateInterpolator mAccelerateInterpolator;
        private static final int[] LAYOUT_ATTRS = new int[] {
            android.R.attr.enabled
        };
    
        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();
                final int currentTop = mTarget.getTop();
                if (offset + currentTop < 0) {
                    offset = 0 - currentTop;
                }
                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 final AnimationListener mReturnToStartPositionListener = new BaseAnimationListener() {
            @Override
            public void onAnimationEnd(Animation animation) {
                // Once the target content has returned to its start position, reset
                // the target offset to 0
                mCurrentTargetOffsetTop = 0;
            }
        };
    
        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);
            }
    
        };
    
        // Cancel the refresh gesture and animate everything back to its original state.
        private final Runnable mCancel = new Runnable() {
    
            @Override
            public void run() {
                mReturningToStart = true;
                // Timeout fired since the user last moved their finger; animate the
                // trigger to 0 and put the target back at its original position
                if (mProgressBar != null) {
                    mFromPercentage = mCurrPercentage;
                    mShrinkTrigger.setDuration(mMediumAnimationDuration);
                    mShrinkTrigger.setAnimationListener(mShrinkAnimationListener);
                    mShrinkTrigger.reset();
                    mShrinkTrigger.setInterpolator(mDecelerateInterpolator);
                    startAnimation(mShrinkTrigger);
                }
                animateOffsetToStartPosition(mCurrentTargetOffsetTop + getPaddingTop(),
                        mReturnToStartPositionListener);
            }
    
        };
    
        /**
         * Simple constructor to use when creating a SwipeRefreshLayout from code.
         * @param context
         */
        public SwipeRefreshLayout(Context context) {
            this(context, null);
        }
    
        /**
         * Constructor that is called when inflating SwipeRefreshLayout from XML.
         * @param context
         * @param attrs
         */
        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);
            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);
        }
    
        /**
         * Set the listener to be notified when a refresh is triggered via the swipe
         * gesture.
         */
        public void setOnRefreshListener(OnRefreshListener listener) {
            mListener = listener;
        }
    
        private void setTriggerPercentage(float percent) {
            if (percent == 0f) {
                // No-op. A null trigger means it's uninitialized, and setting it to zero-percent
                // means we're trying to reset state, so there's nothing to reset in this case.
                mCurrPercentage = 0;
                return;
            }
            mCurrPercentage = percent;
            mProgressBar.setTriggerPercentage(percent);
        }
    
        /**
         * Notify the widget that refresh state has changed. Do not call this when
         * refresh is triggered by a swipe gesture.
         *
         * @param refreshing Whether or not the view should show refresh progress.
         */
        public void setRefreshing(boolean refreshing) {
            if (mRefreshing != refreshing) {
                ensureTarget();
                mCurrPercentage = 0;
                mRefreshing = refreshing;
                if (mRefreshing) {
                    mProgressBar.start();
                } else {
                    mProgressBar.stop();
                }
            }
        }
    
        /**
         * Set the four colors used in the progress animation. The first color will
         * also be the color of the bar that grows in response to a user swipe
         * gesture.
         *
         * @param colorRes1 Color resource.
         * @param colorRes2 Color resource.
         * @param colorRes3 Color resource.
         * @param colorRes4 Color resource.
         */
        public void setColorScheme(int colorRes1, int colorRes2, int colorRes3, int colorRes4) {
            ensureTarget();
            final Resources res = getResources();
            final int color1 = res.getColor(colorRes1);
            final int color2 = res.getColor(colorRes2);
            final int color3 = res.getColor(colorRes3);
            final int color4 = res.getColor(colorRes4);
            mProgressBar.setColorScheme(color1, color2, color3,color4);
        }
    
        /**
         * @return Whether the SwipeRefreshWidget is actively showing refresh
         *         progress.
         */
        public boolean isRefreshing() {
            return mRefreshing;
        }
    
        private void ensureTarget() {
            // Don't bother getting the parent height if the parent hasn't been laid out yet.
            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);
        }
    
        @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);
        }
    
        @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));
            }
        }
    
        /**
         * @return Whether it is possible for the child view of this layout to
         *         scroll up. Override this if the child view is a custom view.
         */
        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);
            }
        }
        /**
         * 检查能否够上拉,对于版本号14下面的暂不支持
         * @return
         */
        public boolean canChildScrollDown() {
            if (android.os.Build.VERSION.SDK_INT < 14) {
                if (mTarget instanceof AbsListView) {
                    final AbsListView absListView = (AbsListView) mTarget;
                    AppLog.e(absListView.getFirstVisiblePosition()+"  :   "+absListView.getChildAt(absListView.getChildCount()-1).getBottom()+"  :   "+absListView.getPaddingBottom());
                    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
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            ensureTarget();
            boolean handled = false;
            if (mReturningToStart && ev.getAction() == MotionEvent.ACTION_DOWN) {
                mReturningToStart = false;
            }
            if (isEnabled() && !mReturningToStart && (!canChildScrollUp() || !canChildScrollDown())) {
                handled = onTouchEvent(ev);
            }
            return !handled ? super.onInterceptTouchEvent(ev) : handled;
        }
    
        @Override
        public void requestDisallowInterceptTouchEvent(boolean b) {
            // Nope.
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            final int action = event.getAction();
            boolean handled = false;
            switch (action) {
                case MotionEvent.ACTION_DOWN:
                    mCurrPercentage = 0;
                    mDownEvent = MotionEvent.obtain(event);
                    mPrevY = mDownEvent.getY();
                    break;
                case MotionEvent.ACTION_MOVE:
                	AppLog.e(mTarget.getBottom()+"  bottom");
                    if (mDownEvent != null && !mReturningToStart) {
                        final float eventY = event.getY();
                        float yDiff = eventY - mDownEvent.getY();
                        if (!canChildScrollDown()) {
                        	yDiff = -eventY + mDownEvent.getY();
    					}
                        if (yDiff > mTouchSlop) {
                            // User velocity passed min velocity; trigger a refresh
                            if (yDiff > mDistanceToTriggerSync) {
                                // User movement passed distance; trigger a refresh
                                startRefresh();
                                handled = true;
                                break;
                            } else {
                                // Just track the user's movement
                                setTriggerPercentage(
                                        mAccelerateInterpolator.getInterpolation(
                                                yDiff / mDistanceToTriggerSync));
                                float offsetTop = yDiff;
                                if (mPrevY > eventY) {
                                    offsetTop = yDiff - mTouchSlop;
                                }
                                updateContentOffsetTop((int) (offsetTop));
                                if (mPrevY > eventY && (mTarget.getTop() < mTouchSlop)) {
                                    // If the user puts the view back at the top, we
                                    // don't need to. This shouldn't be considered
                                    // cancelling the gesture as the user can restart from the top.
                                    removeCallbacks(mCancel);
                                } else {
                                    updatePositionTimeout();
                                }
                                mPrevY = event.getY();
                                handled = true;
                            }
                        }
                    }
                    break;
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:
                    if (mDownEvent != null) {
                        mDownEvent.recycle();
                        mDownEvent = null;
                    }
                    break;
            }
            return handled;
        }
    
        private void startRefresh() {
            removeCallbacks(mCancel);
            mReturnToStartPosition.run();
            setRefreshing(true);
            mListener.onRefresh();
        }
    
        private void updateContentOffsetTop(int targetTop) {
            final int currentTop = mTarget.getTop();
            if (targetTop > mDistanceToTriggerSync) {
                targetTop = (int) mDistanceToTriggerSync;
            } else if (targetTop < 0) {
                targetTop = 0;
            }
            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);
        }
    
        /**
         * Classes that wish to be notified when the swipe gesture correctly
         * triggers a refresh should implement this interface.
         */
        public interface OnRefreshListener {
            public void onRefresh();
        }
    
        /**
         * Simple AnimationListener to avoid having to implement unneeded methods in
         * AnimationListeners.
         */
        private class BaseAnimationListener implements AnimationListener {
            @Override
            public void onAnimationStart(Animation animation) {
            }
    
            @Override
            public void onAnimationEnd(Animation animation) {
            }
    
            @Override
            public void onAnimationRepeat(Animation animation) {
            }
        }
    }

    因为布局我们改,所以上拉刷新的时候。还是顶部滚动栏出现


  • 相关阅读:
    Linux内核网络协议栈优化总纲
    Java实现 蓝桥杯VIP 算法训练 连续正整数的和
    Java实现 蓝桥杯VIP 算法训练 连续正整数的和
    Java实现 蓝桥杯VIP 算法训练 寂寞的数
    Java实现 蓝桥杯VIP 算法训练 寂寞的数
    Java实现 蓝桥杯VIP 算法训练 学做菜
    Java实现 蓝桥杯VIP 算法训练 学做菜
    Java实现 蓝桥杯VIP 算法训练 判断字符位置
    Java实现 蓝桥杯VIP 算法训练 判断字符位置
    Java实现 蓝桥杯VIP 算法训练 链表数据求和操作
  • 原文地址:https://www.cnblogs.com/yjbjingcha/p/6878034.html
Copyright © 2011-2022 走看看