美文网首页
下拉刷新效果实现

下拉刷新效果实现

作者: 记得写丶 | 来源:发表于2017-09-04 21:14 被阅读0次

    SwipeRefreshLayout一样使用简单

    • 最近在做一个高仿的猫眼App,观察猫眼电影App的下拉刷新后,发现其中还是有小细节的,所以决定自己动手撸了一个猫眼下拉刷新(其实是网上实在没有找到可以抄的代码。。),结合[SuperRefreshLayout]
      (https://github.com/nuptboyzhb/SuperSwipeRefreshLayout)加上自定义的一个刷新头部达到了猫眼App的下拉刷新效果
    1716281-47296fa4c3109c9b.gif

    效果和正版基本上是一样的,接下来贴出实现方法和思路由于是基于SuperRefreshLayout重写的下拉刷新的接口实现,所以原有的功能都会有(无入侵,支持RecyclerView,ScrollView,etc)

    代码编写

    /**
     * @Author Zheng Haibo
     * @PersonalWebsite http://www.mobctrl.net
     * @Description 自定义CustomeSwipeRefreshLayout<br>
     * 支持下拉刷新和上拉加载更多<br>
     * 非侵入式,对原来的ListView、RecyclerView没有任何影响,用法和SwipeRefreshLayout类似。<br>
     * 可自定义头部View的样式,调用setHeaderView方法即可 <br>
     * 可自定义页尾View的样式,调用setFooterView方法即可 <br>
     * 支持RecyclerView,ListView,ScrollView,GridView等等。<br>
     * 被包含的View(RecyclerView,ListView etc.)可跟随手指的滑动而滑动<br>
     * 默认是跟随手指的滑动而滑动,也可以设置为不跟随:setTargetScrollWithLayout(false) 回调方法更多<br>
     * 比如:onRefresh() onPullDistance(int distance)和onPullEnable(boolean
     * enable)<br>
     * 开发人员可以根据下拉过程中distance的值做一系列动画。 <br>
     */
    @SuppressLint("ClickableViewAccessibility")
    public class SuperSwipeRefreshLayout extends ViewGroup {
        private static final String LOG_TAG = "CustomeSwipeRefreshLayout";
        private static final int HEADER_VIEW_HEIGHT = 30;// HeaderView height (dp)
    
        private static final float DECELERATE_INTERPOLATION_FACTOR = 2f;
        private static final int INVALID_POINTER = -1;
        private static final float DRAG_RATE = 0.4f;
    
        private static final int SCALE_DOWN_DURATION = 150;
        private static final int ANIMATE_TO_TRIGGER_DURATION = 200;
        private static final int ANIMATE_TO_START_DURATION = 200;
        private static final int DEFAULT_CIRCLE_TARGET = 64;
    
        // SuperSwipeRefreshLayout内的目标View,比如RecyclerView,ListView,ScrollView,GridView
        // etc.
        private View mTarget;
    
        private OnPullRefreshListener mListener;// 下拉刷新listener
        private OnPushLoadMoreListener mOnPushLoadMoreListener;// 上拉加载更多
    
        private boolean mRefreshing = false;
        private boolean mLoadMore = false;
        private int mTouchSlop;
        private float mTotalDragDistance = -1;
        private int mMediumAnimationDuration;
        private int mCurrentTargetOffsetTop;
        private boolean mOriginalOffsetCalculated = false;
    
        private float mInitialMotionY;
        private boolean mIsBeingDragged;
        private int mActivePointerId = INVALID_POINTER;
        private boolean mScale;
    
        private boolean mReturningToStart;
        private final DecelerateInterpolator mDecelerateInterpolator;
        private static final int[] LAYOUT_ATTRS = new int[]{android.R.attr.enabled};
    
        private HeadViewContainer mHeadViewContainer;
        private RelativeLayout mFooterViewContainer;
        private int mHeaderViewIndex = -1;
        private int mFooterViewIndex = -1;
    
        protected int mFrom;
    
        private float mStartingScale;
    
        protected int mOriginalOffsetTop;
    
        private Animation mScaleAnimation;
    
        private Animation mScaleDownAnimation;
    
        private Animation mScaleDownToStartAnimation;
    
        // 最后停顿时的偏移量px,与DEFAULT_CIRCLE_TARGET正比
        private float mSpinnerFinalOffset;
    
        private boolean mNotify;
    
        private int mHeaderViewWidth;// headerView的宽度
    
        private int mFooterViewWidth;
    
        private int mHeaderViewHeight;
    
        private int mFooterViewHeight;
    
        private boolean mUsingCustomStart;
    
        private boolean targetScrollWithLayout = true;
    
        private int pushDistance = 0;
    
        private CircleProgressView defaultProgressView = null;
    
        private boolean usingDefaultHeader = true;
    
        private float density = 1.0f;
    
        private boolean isProgressEnable = true;
    
        /**
         * 下拉时,超过距离之后,弹回来的动画监听器
         */
        private AnimationListener mRefreshListener = new AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                isProgressEnable = false;
            }
    
            @Override
            public void onAnimationRepeat(Animation animation) {
            }
    
            @Override
            public void onAnimationEnd(Animation animation) {
                isProgressEnable = true;
                if (mRefreshing) {
                    if (mNotify) {
                        if (usingDefaultHeader) {
                            ViewCompat.setAlpha(defaultProgressView, 1.0f);
                            defaultProgressView.setOnDraw(true);
                            new Thread(defaultProgressView).start();
                        }
                        if (mListener != null) {
                            mListener.onRefresh();
                        }
                    }
                } else {
                    mHeadViewContainer.setVisibility(View.GONE);
                    if (mScale) {
                        setAnimationProgress(0);
                    } else {
                        setTargetOffsetTopAndBottom(mOriginalOffsetTop
                                - mCurrentTargetOffsetTop, true);
                    }
                }
                mCurrentTargetOffsetTop = mHeadViewContainer.getTop();
                updateListenerCallBack();
            }
        };
    
        /**
         * 更新回调
         */
        private void updateListenerCallBack() {
            int distance = mCurrentTargetOffsetTop + mHeadViewContainer.getHeight();
            if (mListener != null) {
                mListener.onPullDistance(distance);
            }
            if (usingDefaultHeader && isProgressEnable) {
                defaultProgressView.setPullDistance(distance);
            }
        }
    
        /**
         * 添加头布局
         *
         * @param child
         */
        public void setHeaderView(View child) {
            if (child == null) {
                return;
            }
            if (mHeadViewContainer == null) {
                return;
            }
            usingDefaultHeader = false;
            mHeadViewContainer.removeAllViews();
            int size = UiUtils.dp2px(getContext(), 35f);
            RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(size, size);
            params.setMargins(0,0,0,UiUtils.dp2px(getContext(),10f));
            params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
            params.addRule(RelativeLayout.CENTER_HORIZONTAL);
            mHeadViewContainer.addView(child,params);
        }
    
        public int getHeaderHeight(){
            return mHeaderViewHeight;
        }
    
        public void setFooterView(View child) {
            if (child == null) {
                return;
            }
            if (mFooterViewContainer == null) {
                return;
            }
            mFooterViewContainer.removeAllViews();
            RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(
                    mFooterViewWidth, mFooterViewHeight);
            mFooterViewContainer.addView(child, layoutParams);
        }
    
        public SuperSwipeRefreshLayout(Context context) {
            this(context, null);
        }
    
        @SuppressWarnings("deprecation")
        public SuperSwipeRefreshLayout(Context context, AttributeSet attrs) {
            super(context, attrs);
    
            /**
             * getScaledTouchSlop是一个距离,表示滑动的时候,手的移动要大于这个距离才开始移动控件。如果小于这个距离就不触发移动控件
             */
            mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
    
            mMediumAnimationDuration = getResources().getInteger(
                    android.R.integer.config_mediumAnimTime);
    
            setWillNotDraw(false);
            mDecelerateInterpolator = new DecelerateInterpolator(
                    DECELERATE_INTERPOLATION_FACTOR);
    
            final TypedArray a = context
                    .obtainStyledAttributes(attrs, LAYOUT_ATTRS);
            setEnabled(a.getBoolean(0, true));
            a.recycle();
    
            WindowManager wm = (WindowManager) context
                    .getSystemService(Context.WINDOW_SERVICE);
            Display display = wm.getDefaultDisplay();
            final DisplayMetrics metrics = getResources().getDisplayMetrics();
            mHeaderViewWidth = (int) display.getWidth();
            mFooterViewWidth = (int) display.getWidth();
            mHeaderViewHeight = (int) (HEADER_VIEW_HEIGHT * metrics.density);
            mFooterViewHeight = (int) (HEADER_VIEW_HEIGHT * metrics.density);
            defaultProgressView = new CircleProgressView(getContext());
            createHeaderViewContainer();
            createFooterViewContainer();
            ViewCompat.setChildrenDrawingOrderEnabled(this, true);
            mSpinnerFinalOffset = DEFAULT_CIRCLE_TARGET * metrics.density;
            density = metrics.density;
            mTotalDragDistance = mSpinnerFinalOffset;
        }
    
    
    
        /**
         * 孩子节点绘制的顺序
         *
         * @param childCount
         * @param i
         * @return
         */
        @Override
        protected int getChildDrawingOrder(int childCount, int i) {
            // 将新添加的View,放到最后绘制
            if (mHeaderViewIndex < 0 && mFooterViewIndex < 0) {
                return i;
            }
            if (i == childCount - 2) {
                return mHeaderViewIndex;
            }
            if (i == childCount - 1) {
                return mFooterViewIndex;
            }
            int bigIndex = mFooterViewIndex > mHeaderViewIndex ? mFooterViewIndex
                    : mHeaderViewIndex;
            int smallIndex = mFooterViewIndex < mHeaderViewIndex ? mFooterViewIndex
                    : mHeaderViewIndex;
            if (i >= smallIndex && i < bigIndex - 1) {
                return i + 1;
            }
            if (i >= bigIndex || (i == bigIndex - 1)) {
                return i + 2;
            }
            return i;
        }
    
        /**
         * 创建头布局的容器
         */
        private void createHeaderViewContainer() {
            RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(
                    (int) (mHeaderViewHeight * 0.8),
                    (int) (mHeaderViewHeight * 0.8));
            layoutParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
            layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
            mHeadViewContainer = new HeadViewContainer(getContext());
            mHeadViewContainer.setVisibility(View.GONE);
            defaultProgressView.setVisibility(View.VISIBLE);
            defaultProgressView.setOnDraw(false);
            mHeadViewContainer.addView(defaultProgressView, layoutParams);
            addView(mHeadViewContainer);
        }
    
        /**
         * 添加底部布局
         */
        private void createFooterViewContainer() {
            mFooterViewContainer = new RelativeLayout(getContext());
            mFooterViewContainer.setVisibility(View.GONE);
            addView(mFooterViewContainer);
        }
    
        /**
         * 设置
         *
         * @param listener
         */
        public void setOnPullRefreshListener(OnPullRefreshListener listener) {
            mListener = listener;
        }
    
        public void setHeaderViewBackgroundColor(int color) {
            mHeadViewContainer.setBackgroundColor(color);
        }
    
        /**
         * 设置上拉加载更多的接口
         *
         * @param onPushLoadMoreListener
         */
        public void setOnPushLoadMoreListener(
                OnPushLoadMoreListener onPushLoadMoreListener) {
            this.mOnPushLoadMoreListener = onPushLoadMoreListener;
        }
    
        /**
         * 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 (refreshing && mRefreshing != refreshing) {
                // scale and show
                mRefreshing = refreshing;
                int endTarget = 0;
                if (!mUsingCustomStart) {
                    endTarget = (int) (mSpinnerFinalOffset + mOriginalOffsetTop);
                } else {
                    endTarget = (int) mSpinnerFinalOffset;
                }
                setTargetOffsetTopAndBottom(endTarget - mCurrentTargetOffsetTop,
                        true /* requires update */);
                mNotify = false;
                startScaleUpAnimation(mRefreshListener);
            } else {
                setRefreshing(refreshing, false /* notify */);
                if (usingDefaultHeader) {
                    defaultProgressView.setOnDraw(false);
                }
            }
        }
    
        private void startScaleUpAnimation(AnimationListener listener) {
            mHeadViewContainer.setVisibility(View.VISIBLE);
            mScaleAnimation = new Animation() {
                @Override
                public void applyTransformation(float interpolatedTime,
                                                Transformation t) {
                    setAnimationProgress(interpolatedTime);
                }
            };
            mScaleAnimation.setDuration(mMediumAnimationDuration);
            if (listener != null) {
                mHeadViewContainer.setAnimationListener(listener);
            }
            mHeadViewContainer.clearAnimation();
            mHeadViewContainer.startAnimation(mScaleAnimation);
        }
    
        private void setAnimationProgress(float progress) {
            if (!usingDefaultHeader) {
                progress = 1;
            }
            ViewCompat.setScaleX(mHeadViewContainer, progress);
            ViewCompat.setScaleY(mHeadViewContainer, progress);
        }
    
        private void setRefreshing(boolean refreshing, final boolean notify) {
            if (mRefreshing != refreshing) {
                mNotify = notify;
                ensureTarget();
                mRefreshing = refreshing;
                if (mRefreshing) {
                    animateOffsetToCorrectPosition(mCurrentTargetOffsetTop,
                            mRefreshListener);
                } else {
                    //startScaleDownAnimation(mRefreshListener);
                    animateOffsetToStartPosition(mCurrentTargetOffsetTop, mRefreshListener);
                }
            }
        }
    
        private void startScaleDownAnimation(AnimationListener listener) {
            mScaleDownAnimation = new Animation() {
                @Override
                public void applyTransformation(float interpolatedTime,
                                                Transformation t) {
                    setAnimationProgress(1 - interpolatedTime);
                }
            };
            mScaleDownAnimation.setDuration(SCALE_DOWN_DURATION);
            mHeadViewContainer.setAnimationListener(listener);
            mHeadViewContainer.clearAnimation();
            mHeadViewContainer.startAnimation(mScaleDownAnimation);
        }
    
        public boolean isRefreshing() {
            return mRefreshing;
        }
    
        /**
         * 确保mTarget不为空<br>
         * mTarget一般是可滑动的ScrollView,ListView,RecyclerView等
         */
        private void ensureTarget() {
            if (mTarget == null) {
                for (int i = 0; i < getChildCount(); i++) {
                    View child = getChildAt(i);
                    if (!child.equals(mHeadViewContainer)
                            && !child.equals(mFooterViewContainer)) {
                        mTarget = child;
                        break;
                    }
                }
            }
        }
    
        /**
         * Set the distance to trigger a sync in dips
         *
         * @param distance
         */
        public void setDistanceToTriggerSync(int distance) {
            mTotalDragDistance = distance;
        }
    
        @Override
        protected void onLayout(boolean changed, int left, int top, int right,
                                int bottom) {
            final int width = getMeasuredWidth();
            final int height = getMeasuredHeight();
            if (getChildCount() == 0) {
                return;
            }
            if (mTarget == null) {
                ensureTarget();
            }
            if (mTarget == null) {
                return;
            }
            int distance = mCurrentTargetOffsetTop + mHeadViewContainer.getMeasuredHeight();
            if (!targetScrollWithLayout) {
                // 判断标志位,如果目标View不跟随手指的滑动而滑动,将下拉偏移量设置为0
                distance = 0;
            }
            final View child = mTarget;
            final int childLeft = getPaddingLeft();
            final int childTop = getPaddingTop() + distance - pushDistance;// 根据偏移量distance更新
            final int childWidth = width - getPaddingLeft() - getPaddingRight();
            final int childHeight = height - getPaddingTop() - getPaddingBottom();
    //        Log.d(LOG_TAG, "debug:onLayout childHeight = " + childHeight);
            child.layout(childLeft, childTop, childLeft + childWidth, childTop
                    + childHeight);// 更新目标View的位置
            int headViewWidth = mHeadViewContainer.getMeasuredWidth();
            int headViewHeight = mHeadViewContainer.getMeasuredHeight();
            mHeadViewContainer.layout((width / 2 - headViewWidth / 2),
                    mCurrentTargetOffsetTop, (width / 2 + headViewWidth / 2),
                    mCurrentTargetOffsetTop + headViewHeight);// 更新头布局的位置
            int footViewWidth = mFooterViewContainer.getMeasuredWidth();
            int footViewHeight = mFooterViewContainer.getMeasuredHeight();
            mFooterViewContainer.layout((width / 2 - footViewWidth / 2), height
                    - pushDistance, (width / 2 + footViewWidth / 2), height
                    + footViewHeight - pushDistance);
        }
    
        @Override
        public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            if (mTarget == null) {
                ensureTarget();
            }
            if (mTarget == null) {
                return;
            }
            mTarget.measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth()
                            - getPaddingLeft() - getPaddingRight(), MeasureSpec.EXACTLY),
                    MeasureSpec.makeMeasureSpec(getMeasuredHeight()
                                    - getPaddingTop() - getPaddingBottom(),
                            MeasureSpec.EXACTLY));
            mHeadViewContainer.measure(MeasureSpec.makeMeasureSpec(
                    mHeaderViewWidth, MeasureSpec.EXACTLY), MeasureSpec
                    .makeMeasureSpec(3 * mHeaderViewHeight, MeasureSpec.EXACTLY));
            mFooterViewContainer.measure(MeasureSpec.makeMeasureSpec(
                    mFooterViewWidth, MeasureSpec.EXACTLY), MeasureSpec
                    .makeMeasureSpec(mFooterViewHeight, MeasureSpec.EXACTLY));
            if (!mUsingCustomStart && !mOriginalOffsetCalculated) {
                mOriginalOffsetCalculated = true;
                mCurrentTargetOffsetTop = mOriginalOffsetTop = -mHeadViewContainer
                        .getMeasuredHeight();
                updateListenerCallBack();
            }
            mHeaderViewIndex = -1;
            for (int index = 0; index < getChildCount(); index++) {
                if (getChildAt(index) == mHeadViewContainer) {
                    mHeaderViewIndex = index;
                    break;
                }
            }
            mFooterViewIndex = -1;
            for (int index = 0; index < getChildCount(); index++) {
                if (getChildAt(index) == mFooterViewContainer) {
                    mFooterViewIndex = index;
                    break;
                }
            }
        }
    
        /**
         * 判断目标View是否滑动到顶部-还能否继续滑动
         *
         * @return
         */
        public boolean isChildScrollToTop() {
            if (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);
            }
        }
    
        /**
         * 是否滑动到底部
         *
         * @return
         */
    //    public boolean isChildScrollToBottom() {
    //        if (isChildScrollToTop()) {
    //            return false;
    //        }
    //        if (mTarget instanceof RecyclerView) {
    //            RecyclerView recyclerView = (RecyclerView) mTarget;
    //            LayoutManager layoutManager = recyclerView.getLayoutManager();
    //            int count = recyclerView.getAdapter().getItemCount();
    //            if (layoutManager instanceof LinearLayoutManager && count > 0) {
    //                LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;
    //                if (linearLayoutManager.findLastCompletelyVisibleItemPosition() == count - 1) {
    //                    return true;
    //                }
    //            } else if (layoutManager instanceof StaggeredGridLayoutManager) {
    //                StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;
    //                int[] lastItems = new int[2];
    //                staggeredGridLayoutManager
    //                        .findLastCompletelyVisibleItemPositions(lastItems);
    //                int lastItem = Math.max(lastItems[0], lastItems[1]);
    //                if (lastItem == count - 1) {
    //                    return true;
    //                }
    //            }
    //            return false;
    //        } else if (mTarget instanceof AbsListView) {
    //            final AbsListView absListView = (AbsListView) mTarget;
    //            int count = absListView.getAdapter().getCount();
    //            int fristPos = absListView.getFirstVisiblePosition();
    //            if (fristPos == 0
    //                    && absListView.getChildAt(0).getTop() >= absListView
    //                    .getPaddingTop()) {
    //                return false;
    //            }
    //            int lastPos = absListView.getLastVisiblePosition();
    //            if (lastPos > 0 && count > 0 && lastPos == count - 1) {
    //                return true;
    //            }
    //            return false;
    //        } else if (mTarget instanceof ScrollView) {
    //            ScrollView scrollView = (ScrollView) mTarget;
    //            View view = (View) scrollView
    //                    .getChildAt(scrollView.getChildCount() - 1);
    //            if (view != null) {
    //                int diff = (view.getBottom() - (scrollView.getHeight() + scrollView
    //                        .getScrollY()));
    //                if (diff == 0) {
    //                    return true;
    //                }
    //            }
    //        } else if (mTarget instanceof NestedScrollView) {
    //            NestedScrollView nestedScrollView = (NestedScrollView) mTarget;
    //            View view = (View) nestedScrollView.getChildAt(nestedScrollView.getChildCount() - 1);
    //            if (view != null) {
    //                int diff = (view.getBottom() - (nestedScrollView.getHeight() + nestedScrollView.getScrollY()));
    //                if (diff == 0) {
    //                    return true;
    //                }
    //            }
    //        }
    //        return false;
    //    }
    
        /**
         * 主要判断是否应该拦截子View的事件<br>
         * 如果拦截,则交给自己的OnTouchEvent处理<br>
         * 否者,交给子View处理<br>
         */
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            ensureTarget();
    
            final int action = MotionEventCompat.getActionMasked(ev);
    
            if (mReturningToStart && action == MotionEvent.ACTION_DOWN) {
                mReturningToStart = false;
            }
            if (!isEnabled() || mReturningToStart || mRefreshing || mLoadMore
                    || (!isChildScrollToTop())) {
                // 如果子View可以滑动,不拦截事件,交给子View处理-下拉刷新
                // 或者子View没有滑动到底部不拦截事件-上拉加载更多
                return false;
            }
    
            // 下拉刷新判断
            switch (action) {
                case MotionEvent.ACTION_DOWN:
                    setTargetOffsetTopAndBottom(
                            mOriginalOffsetTop - mHeadViewContainer.getTop(), true);// 恢复HeaderView的初始位置
                    mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
                    mIsBeingDragged = false;
                    final float initialMotionY = getMotionEventY(ev, mActivePointerId);
                    if (initialMotionY == -1) {
                        return false;
                    }
                    mInitialMotionY = initialMotionY;// 记录按下的位置
    
                case MotionEvent.ACTION_MOVE:
                    if (mActivePointerId == INVALID_POINTER) {
    
                        return false;
                    }
    
                    final float y = getMotionEventY(ev, mActivePointerId);
                    if (y == -1) {
                        return false;
                    }
                    float yDiff = 0;
                    if (isChildScrollToTop()) {
                        yDiff = y - mInitialMotionY;// 计算下拉距离
                        if (yDiff > mTouchSlop && !mIsBeingDragged) {// 判断是否下拉的距离足够
                            mIsBeingDragged = true;// 正在下拉
                        }
                    } else {
    
                    }
                    break;
    
                case MotionEventCompat.ACTION_POINTER_UP:
                    onSecondaryPointerUp(ev);
                    break;
    
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:
                    mIsBeingDragged = false;
                    mActivePointerId = INVALID_POINTER;
                    break;
            }
    
            return mIsBeingDragged;// 如果正在拖动,则拦截子View的事件
        }
    
        private float getMotionEventY(MotionEvent ev, int activePointerId) {
            final int index = MotionEventCompat.findPointerIndex(ev,
                    activePointerId);
            if (index < 0) {
                return -1;
            }
            return MotionEventCompat.getY(ev, index);
        }
    
        @Override
        public void requestDisallowInterceptTouchEvent(boolean b) {
            // Nope.
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent ev) {
            final int action = MotionEventCompat.getActionMasked(ev);
    
            if (mReturningToStart && action == MotionEvent.ACTION_DOWN) {
                mReturningToStart = false;
            }
            if (!isEnabled() || mReturningToStart
                    || (!isChildScrollToTop() )) {
                // 如果子View可以滑动,不拦截事件,交给子View处理
                return false;
            }
    
            return handlerPullTouchEvent(ev, action);
        }
    
        private boolean handlerPullTouchEvent(MotionEvent ev, int action) {
            switch (action) {
                case MotionEvent.ACTION_DOWN:
                    mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
                    mIsBeingDragged = false;
                    break;
    
                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 overscrollTop = (y - mInitialMotionY) * DRAG_RATE;
                    if (mIsBeingDragged) {
                        float originalDragPercent = overscrollTop / mTotalDragDistance;
                        if (originalDragPercent < 0) {
                            return false;
                        }
                        float dragPercent = Math.min(1f, Math.abs(originalDragPercent));
                        float extraOS = Math.abs(overscrollTop) - mTotalDragDistance;
                        float slingshotDist = mUsingCustomStart ? mSpinnerFinalOffset
                                - mOriginalOffsetTop : mSpinnerFinalOffset;
                        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 = mOriginalOffsetTop
                                + (int) ((slingshotDist * dragPercent) + extraMove);
                        if (mHeadViewContainer.getVisibility() != View.VISIBLE) {
                            mHeadViewContainer.setVisibility(View.VISIBLE);
                        }
                        if (!mScale) {
                            ViewCompat.setScaleX(mHeadViewContainer, 1f);
                            ViewCompat.setScaleY(mHeadViewContainer, 1f);
                        }
                        if (usingDefaultHeader) {
                            float alpha = overscrollTop / mTotalDragDistance;
                            if (alpha >= 1.0f) {
                                alpha = 1.0f;
                            }
                            ViewCompat.setScaleX(defaultProgressView, alpha);
                            ViewCompat.setScaleY(defaultProgressView, alpha);
                            ViewCompat.setAlpha(defaultProgressView, alpha);
                        }
                        if (overscrollTop < mTotalDragDistance) {
                            if (mScale) {
                                setAnimationProgress(overscrollTop / mTotalDragDistance);
                            }
                            if (mListener != null) {
                                mListener.onPullEnable(false);
                            }
                        } else {
                            if (mListener != null) {
                                mListener.onPullEnable(true);
                            }
                        }
                        setTargetOffsetTopAndBottom(targetY - mCurrentTargetOffsetTop,
                                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) {
                        if (action == MotionEvent.ACTION_UP) {
                        }
                        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 /* notify */);
                    } else {
                        mRefreshing = false;
                        AnimationListener listener = null;
                        if (!mScale) {
                            listener = new AnimationListener() {
    
                                @Override
                                public void onAnimationStart(Animation animation) {
                                }
    
                                @Override
                                public void onAnimationEnd(Animation animation) {
                                    if (!mScale) {
                                        startScaleDownAnimation(null);
                                    }
                                }
    
                                @Override
                                public void onAnimationRepeat(Animation animation) {
                                }
    
                            };
                        }
                        animateOffsetToStartPosition(mCurrentTargetOffsetTop, listener);
                    }
                    mActivePointerId = INVALID_POINTER;
                    return false;
                }
            }
    
            return true;
        }
    
        /**
         * 处理上拉加载更多的Touch事件
         *
         * @param ev
         * @param action
         * @return
         */
        private boolean handlerPushTouchEvent(MotionEvent ev, int action) {
            switch (action) {
                case MotionEvent.ACTION_DOWN:
                    mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
                    mIsBeingDragged = false;
                    break;
                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 overscrollBottom = (mInitialMotionY - y) * DRAG_RATE;
                    if (mIsBeingDragged) {
                        pushDistance = (int) overscrollBottom;
                        updateFooterViewPosition();
                        if (mOnPushLoadMoreListener != null) {
                            mOnPushLoadMoreListener
                                    .onPushEnable(pushDistance >= mFooterViewHeight);
                        }
                    }
                    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) {
                        if (action == MotionEvent.ACTION_UP) {
                        }
                        return false;
                    }
                    final int pointerIndex = MotionEventCompat.findPointerIndex(ev,
                            mActivePointerId);
                    final float y = MotionEventCompat.getY(ev, pointerIndex);
                    final float overscrollBottom = (mInitialMotionY - y) * DRAG_RATE;// 松手是下拉的距离
                    mIsBeingDragged = false;
                    mActivePointerId = INVALID_POINTER;
                    if (overscrollBottom < mFooterViewHeight
                            || mOnPushLoadMoreListener == null) {// 直接取消
                        pushDistance = 0;
                    } else {// 下拉到mFooterViewHeight
                        pushDistance = mFooterViewHeight;
                    }
                    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
                        updateFooterViewPosition();
                        if (pushDistance == mFooterViewHeight
                                && mOnPushLoadMoreListener != null) {
                            mLoadMore = true;
                            mOnPushLoadMoreListener.onLoadMore();
                        }
                    } else {
                        animatorFooterToBottom((int) overscrollBottom, pushDistance);
                    }
                    return false;
                }
            }
            return true;
        }
    
        /**
         * 松手之后,使用动画将Footer从距离start变化到end
         *
         * @param start
         * @param end
         */
        @TargetApi(Build.VERSION_CODES.HONEYCOMB)
        private void animatorFooterToBottom(int start, final int end) {
            ValueAnimator valueAnimator = ValueAnimator.ofInt(start, end);
            valueAnimator.setDuration(150);
            valueAnimator.addUpdateListener(new AnimatorUpdateListener() {
    
                @Override
                public void onAnimationUpdate(ValueAnimator valueAnimator) {
                    // update
                    pushDistance = (Integer) valueAnimator.getAnimatedValue();
                    updateFooterViewPosition();
                }
            });
            valueAnimator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    if (end > 0 && mOnPushLoadMoreListener != null) {
                        // start loading more
                        mLoadMore = true;
                        mOnPushLoadMoreListener.onLoadMore();
                    } else {
                        resetTargetLayout();
                        mLoadMore = false;
                    }
                }
            });
            valueAnimator.setInterpolator(mDecelerateInterpolator);
            valueAnimator.start();
        }
    
        /**
         * 设置停止加载
         *
         * @param loadMore
         */
        public void setLoadMore(boolean loadMore) {
            if (!loadMore && mLoadMore) {// 停止加载
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
                    mLoadMore = false;
                    pushDistance = 0;
                    updateFooterViewPosition();
                } else {
                    animatorFooterToBottom(mFooterViewHeight, 0);
                }
            }
        }
    
        private void animateOffsetToCorrectPosition(int from,
                                                    AnimationListener listener) {
            mFrom = from;
            mAnimateToCorrectPosition.reset();
            mAnimateToCorrectPosition.setDuration(ANIMATE_TO_TRIGGER_DURATION);
            mAnimateToCorrectPosition.setInterpolator(mDecelerateInterpolator);
            if (listener != null) {
                mHeadViewContainer.setAnimationListener(listener);
            }
            mHeadViewContainer.clearAnimation();
            mHeadViewContainer.startAnimation(mAnimateToCorrectPosition);
        }
    
        private void animateOffsetToStartPosition(int from,
                                                  AnimationListener listener) {
            if (mScale) {
                startScaleDownReturnToStartAnimation(from, listener);
            } else {
                mFrom = from;
                mAnimateToStartPosition.reset();
                mAnimateToStartPosition.setDuration(ANIMATE_TO_START_DURATION);
                mAnimateToStartPosition.setInterpolator(mDecelerateInterpolator);
                if (listener != null) {
                    mHeadViewContainer.setAnimationListener(listener);
                }
                mHeadViewContainer.clearAnimation();
                mHeadViewContainer.startAnimation(mAnimateToStartPosition);
            }
            resetTargetLayoutDelay(ANIMATE_TO_START_DURATION);
        }
    
        /**
         * 重置Target位置
         *
         * @param delay
         */
        public void resetTargetLayoutDelay(int delay) {
            new Handler().postDelayed(new Runnable() {
    
                @Override
                public void run() {
                    resetTargetLayout();
                }
            }, delay);
        }
    
        /**
         * 重置Target的位置
         */
        public void resetTargetLayout() {
            final int width = getMeasuredWidth();
            final int height = getMeasuredHeight();
            final View child = mTarget;
            final int childLeft = getPaddingLeft();
            final int childTop = getPaddingTop();
            final int childWidth = child.getWidth() - getPaddingLeft()
                    - getPaddingRight();
            final int childHeight = child.getHeight() - getPaddingTop()
                    - getPaddingBottom();
            child.layout(childLeft, childTop, childLeft + childWidth, childTop
                    + childHeight);
    
            int headViewWidth = mHeadViewContainer.getMeasuredWidth();
            int headViewHeight = mHeadViewContainer.getMeasuredHeight();
            mHeadViewContainer.layout((width / 2 - headViewWidth / 2),
                    -headViewHeight, (width / 2 + headViewWidth / 2), 0);// 更新头布局的位置
            int footViewWidth = mFooterViewContainer.getMeasuredWidth();
            int footViewHeight = mFooterViewContainer.getMeasuredHeight();
            mFooterViewContainer.layout((width / 2 - footViewWidth / 2), height,
                    (width / 2 + footViewWidth / 2), height + footViewHeight);
        }
    
        private final Animation mAnimateToCorrectPosition = new Animation() {
            @Override
            public void applyTransformation(float interpolatedTime, Transformation t) {
                int targetTop = 0;
                int endTarget = 0;
                if (!mUsingCustomStart) {
                    endTarget = (int) (mSpinnerFinalOffset - Math
                            .abs(mOriginalOffsetTop));
                } else {
                    endTarget = (int) mSpinnerFinalOffset;
                }
                targetTop = (mFrom + (int) ((endTarget - mFrom) * interpolatedTime));
                int offset = targetTop - mHeadViewContainer.getTop();
                setTargetOffsetTopAndBottom(offset, false /* requires update */);
            }
    
            @Override
            public void setAnimationListener(AnimationListener listener) {
                super.setAnimationListener(listener);
            }
        };
    
        private void moveToStart(float interpolatedTime) {
            int targetTop = 0;
            targetTop = (mFrom + (int) ((mOriginalOffsetTop - mFrom) * interpolatedTime));
            int offset = targetTop - mHeadViewContainer.getTop();
            setTargetOffsetTopAndBottom(offset, false /* requires update */);
        }
    
        private final Animation mAnimateToStartPosition = new Animation() {
            @Override
            public void applyTransformation(float interpolatedTime, Transformation t) {
                moveToStart(interpolatedTime);
            }
        };
    
        private void startScaleDownReturnToStartAnimation(int from,
                                                          AnimationListener listener) {
            mFrom = from;
            mStartingScale = ViewCompat.getScaleX(mHeadViewContainer);
            mScaleDownToStartAnimation = new Animation() {
                @Override
                public void applyTransformation(float interpolatedTime,
                                                Transformation t) {
                    float targetScale = (mStartingScale + (-mStartingScale * interpolatedTime));
                    setAnimationProgress(targetScale);
                    moveToStart(interpolatedTime);
                }
            };
            mScaleDownToStartAnimation.setDuration(SCALE_DOWN_DURATION);
            if (listener != null) {
                mHeadViewContainer.setAnimationListener(listener);
            }
            mHeadViewContainer.clearAnimation();
            mHeadViewContainer.startAnimation(mScaleDownToStartAnimation);
        }
    
        private void setTargetOffsetTopAndBottom(int offset, boolean requiresUpdate) {
            mHeadViewContainer.bringToFront();
            mHeadViewContainer.offsetTopAndBottom(offset);
            mCurrentTargetOffsetTop = mHeadViewContainer.getTop();
            if (requiresUpdate && Build.VERSION.SDK_INT < 11) {
                invalidate();
            }
            updateListenerCallBack();
        }
    
        /**
         * 修改底部布局的位置-敏感pushDistance
         */
        private void updateFooterViewPosition() {
            mFooterViewContainer.setVisibility(View.VISIBLE);
            mFooterViewContainer.bringToFront();
            //针对4.4及之前版本的兼容
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
                mFooterViewContainer.getParent().requestLayout();
            }
            mFooterViewContainer.offsetTopAndBottom(-pushDistance);
            updatePushDistanceListener();
        }
    
        private void updatePushDistanceListener() {
            if (mOnPushLoadMoreListener != null) {
                mOnPushLoadMoreListener.onPushDistance(pushDistance);
            }
        }
    
        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);
            }
        }
    
        /**
         * @Description 下拉刷新布局头部的容器
         */
        private class HeadViewContainer extends RelativeLayout {
    
            private AnimationListener mListener;
    
            public HeadViewContainer(Context context) {
                super(context);
            }
    
            public void setAnimationListener(AnimationListener listener) {
                mListener = listener;
            }
    
            @Override
            public void onAnimationStart() {
                super.onAnimationStart();
                if (mListener != null) {
                    mListener.onAnimationStart(getAnimation());
                }
            }
    
            @Override
            public void onAnimationEnd() {
                super.onAnimationEnd();
                if (mListener != null) {
                    mListener.onAnimationEnd(getAnimation());
                }
            }
        }
    
        /**
         * 判断子View是否跟随手指的滑动而滑动,默认跟随
         *
         * @return
         */
        public boolean isTargetScrollWithLayout() {
            return targetScrollWithLayout;
        }
    
        /**
         * 设置子View是否跟谁手指的滑动而滑动
         *
         * @param targetScrollWithLayout
         */
        public void setTargetScrollWithLayout(boolean targetScrollWithLayout) {
            this.targetScrollWithLayout = targetScrollWithLayout;
        }
    
        /**
         * 下拉刷新回调
         */
        public interface OnPullRefreshListener {
            public void onRefresh();
    
            public void onPullDistance(int distance);
    
            public void onPullEnable(boolean enable);
        }
    
        /**
         * 上拉加载更多
         */
        public interface OnPushLoadMoreListener {
            public void onLoadMore();
    
            public void onPushDistance(int distance);
    
            public void onPushEnable(boolean enable);
        }
    
        /**
         * Adapter
         */
        public class OnPullRefreshListenerAdapter implements OnPullRefreshListener {
    
            @Override
            public void onRefresh() {
    
            }
    
            @Override
            public void onPullDistance(int distance) {
    
            }
    
            @Override
            public void onPullEnable(boolean enable) {
    
            }
    
        }
    
        public class OnPushLoadMoreListenerAdapter implements
                OnPushLoadMoreListener {
    
            @Override
            public void onLoadMore() {
    
            }
    
            @Override
            public void onPushDistance(int distance) {
    
            }
    
            @Override
            public void onPushEnable(boolean enable) {
    
            }
    
        }
    
        /**
         * 设置默认下拉刷新进度条的颜色
         *
         * @param color
         */
        public void setDefaultCircleProgressColor(int color) {
            if (usingDefaultHeader) {
                defaultProgressView.setProgressColor(color);
            }
        }
    
        /**
         * 设置圆圈的背景色
         *
         * @param color
         */
        public void setDefaultCircleBackgroundColor(int color) {
            if (usingDefaultHeader) {
                defaultProgressView.setCircleBackgroundColor(color);
            }
        }
    
        public void setDefaultCircleShadowColor(int color) {
            if (usingDefaultHeader) {
                defaultProgressView.setShadowColor(color);
            }
        }
    
        /**
         * 默认的下拉刷新样式
         */
        public class CircleProgressView extends View implements Runnable {
    
            private static final int PEROID = 16;// 绘制周期
            private Paint progressPaint;
            private Paint bgPaint;
            private int width;// view的高度
            private int height;// view的宽度
    
            private boolean isOnDraw = false;
            private boolean isRunning = false;
            private int startAngle = 0;
            private int speed = 8;
            private RectF ovalRect = null;
            private RectF bgRect = null;
            private int swipeAngle;
            private int progressColor = 0xffcccccc;
            private int circleBackgroundColor = 0xffffffff;
            private int shadowColor = 0xff999999;
    
            public CircleProgressView(Context context) {
                super(context);
            }
    
            public CircleProgressView(Context context, AttributeSet attrs) {
                super(context, attrs);
            }
    
            public CircleProgressView(Context context, AttributeSet attrs,
                                      int defStyleAttr) {
                super(context, attrs, defStyleAttr);
            }
    
            @Override
            protected void onDraw(Canvas canvas) {
                super.onDraw(canvas);
                canvas.drawArc(getBgRect(), 0, 360, false, createBgPaint());
                int index = startAngle / 360;
                if (index % 2 == 0) {
                    swipeAngle = (startAngle % 720) / 2;
                } else {
                    swipeAngle = 360 - (startAngle % 720) / 2;
                }
                canvas.drawArc(getOvalRect(), startAngle, swipeAngle, false,
                        createPaint());
            }
    
            private RectF getBgRect() {
                width = getWidth();
                height = getHeight();
                if (bgRect == null) {
                    int offset = (int) (density * 2);
                    bgRect = new RectF(offset, offset, width - offset, height
                            - offset);
                }
                return bgRect;
            }
    
            private RectF getOvalRect() {
                width = getWidth();
                height = getHeight();
                if (ovalRect == null) {
                    int offset = (int) (density * 8);
                    ovalRect = new RectF(offset, offset, width - offset, height
                            - offset);
                }
                return ovalRect;
            }
    
            public void setProgressColor(int progressColor) {
                this.progressColor = progressColor;
            }
    
            public void setCircleBackgroundColor(int circleBackgroundColor) {
                this.circleBackgroundColor = circleBackgroundColor;
            }
    
            public void setShadowColor(int shadowColor) {
                this.shadowColor = shadowColor;
            }
    
            /**
             * 根据画笔的颜色,创建画笔
             *
             * @return
             */
            private Paint createPaint() {
                if (this.progressPaint == null) {
                    progressPaint = new Paint();
                    progressPaint.setStrokeWidth((int) (density * 3));
                    progressPaint.setStyle(Paint.Style.STROKE);
                    progressPaint.setAntiAlias(true);
                }
                progressPaint.setColor(progressColor);
                return progressPaint;
            }
    
            private Paint createBgPaint() {
                if (this.bgPaint == null) {
                    bgPaint = new Paint();
                    bgPaint.setColor(circleBackgroundColor);
                    bgPaint.setStyle(Paint.Style.FILL);
                    bgPaint.setAntiAlias(true);
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
                        this.setLayerType(LAYER_TYPE_SOFTWARE, bgPaint);
                    }
                    bgPaint.setShadowLayer(4.0f, 0.0f, 2.0f, shadowColor);
                }
                return bgPaint;
            }
    
            public void setPullDistance(int distance) {
                this.startAngle = distance * 2;
                postInvalidate();
            }
    
            @Override
            public void run() {
                while (isOnDraw) {
                    isRunning = true;
                    long startTime = System.currentTimeMillis();
                    startAngle += speed;
                    postInvalidate();
                    long time = System.currentTimeMillis() - startTime;
                    if (time < PEROID) {
                        try {
                            Thread.sleep(PEROID - time);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
    
            public void setOnDraw(boolean isOnDraw) {
                this.isOnDraw = isOnDraw;
            }
    
            public void setSpeed(int speed) {
                this.speed = speed;
            }
    
            public boolean isRunning() {
                return isRunning;
            }
    
            @Override
            public void onWindowFocusChanged(boolean hasWindowFocus) {
                super.onWindowFocusChanged(hasWindowFocus);
            }
    
            @Override
            protected void onDetachedFromWindow() {
                isOnDraw = false;
                super.onDetachedFromWindow();
            }
    
        }
    
    }
    
    

    相关文章

      网友评论

          本文标题:下拉刷新效果实现

          本文链接:https://www.haomeiwen.com/subject/dkmejxtx.html