zoukankan      html  css  js  c++  java
  • RecyclerView下拉刷新和载入很多其它

    之前一直写的是ListVIew下拉刷新,可是好多朋友都说要RecycleView的下拉刷新和滑动载入。事实上,这个原理都是几乎相同。抽出时间,我就写了下RecycleView的下拉刷新和滑动载入很多其它。因此,这才写到博客里,记录一下。


    在大家阅读这篇博客前。大家须要了解的知识

    1.Scroller。实现弹性滑动的类,这个是经经常使用到的,不懂的请自觉先学习Scroller的知识。

    2.事件分发机制。

    事件是以ACTION_DOWN開始到ACTION_UP货ACTION_CANCEL结束的一个序列,期间事件分发,能够通过onInterceptTouchEvent方法和dispatchTouchEvent进行事件的阻止和消费。

    3.RecyclerView的基本使用。

    比方怎样加入一个Decoration.

    4.onSizeChange的触发时机。onSizeChange()在View的layout中触发,它运行在全部控件的onMeasure()之后,因此能够直接获取到控件的測量长和宽。
           总体的思路:採用的是LinearLayout+RecyclerView的组合。在LinearLayout中加入HeaderView和FooterView。

    当RecyclerView滑动到了最顶部,则能够触发下拉事件;当RecyclerView滑动到了底部,则能够触发滑动载入很多其它的事件。然后在通过事件分发。进行滑动事件的处理。

           先看一下效果:

       以下是自己定义View的代码,后面会逐一分析代码块:
    package com.mjc.recyclerviewdemo.refresh;
    
    import android.content.Context;
    import android.graphics.Color;
    import android.support.v7.widget.LinearLayoutManager;
    import android.support.v7.widget.RecyclerView;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.MotionEvent;
    import android.view.ViewConfiguration;
    import android.widget.LinearLayout;
    import android.widget.Scroller;
    
    import com.mjc.recyclerviewdemo.CustomItemDecoration;
    
    /**
     * Created by mjc on 2016/3/11.
     */
    public class PullToRefreshRecycleView extends LinearLayout {
    
        //头部
        private IHeaderView mHeaderView;
        private int mHeaderHeight;
    
        //尾部
        private CustomFooterView mFooterView;
        private int mFooterHeight;
    
        //阻尼系数,越大,阻力越大
        public static final float RATIO = 0.5f;
        //当前是否阻止事件
        private boolean isIntercept;
        //是否正在刷新
        private boolean isRefreshing;
        //滑动类
        private Scroller mScroller;
        //刷新的View
        private RecyclerView mRefreshView;
    
        private Context mContext;
    
        private int mMaxScrollHeight;
    
        private boolean isFirst = true;
    
        public static final int NORMAL = 0;
        public static final int PULL_TO_REFRESH = 1;
        public static final int RELEASE_TO_REFRESH = 2;
        public static final int REFRESING = 3;
        private int mCurrentState;
        private int mTouchSlop;
    
        private OnRefreshListener listener;
    
        private boolean isPullDownMotion;
        private int lastVisible;
    
        public PullToRefreshRecycleView(Context context) {
            super(context);
            init(context);
        }
    
        public PullToRefreshRecycleView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init(context);
        }
    
    
        private void init(Context context) {
            mContext = context;
            this.setOrientation(VERTICAL);
            mRefreshView = getRefreshView();
            mRefreshView.setBackgroundColor(Color.WHITE);
            LayoutParams listParams = new LayoutParams(-1, -1);
            mRefreshView.setLayoutParams(listParams);
            addView(mRefreshView);
            //加入HeaderView
            mHeaderView = new CustomHeaderView(context);
            LayoutParams params = new LayoutParams(-1, -2);
            mHeaderView.setLayoutParams(params);
            addView(mHeaderView, 0);
            //加入FooterView
            mFooterView = new CustomFooterView(context);
            LayoutParams fParams = new LayoutParams(-1, 200);
            mFooterView.setLayoutParams(fParams);
            addView(mFooterView, -1);
            //弹性滑动实现
            mScroller = new Scroller(context);
            mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            //第一次获取相关參数,并隐藏HeaderView,FooterView
            if (isFirst) {
                mHeaderHeight = mHeaderView.getMeasuredHeight();
                mMaxScrollHeight = mHeaderHeight * 3;
                resetHeaderLayout(-mHeaderHeight);
    
                mFooterHeight = mFooterView.getMeasuredHeight();
                resetFooterLayout(-mFooterHeight);
                Log.v("@mHeaderHeight", mHeaderHeight + "");
                Log.v("@mFooterHeight", mFooterHeight + "");
                isFirst = false;
            }
        }
    
        private void resetHeaderLayout(int offset) {
            LayoutParams params = (LayoutParams) mHeaderView.getLayoutParams();
            params.topMargin = offset;
            mHeaderView.requestLayout();
        }
    
        private void resetFooterLayout(int offset) {
            LayoutParams params = (LayoutParams) mFooterView.getLayoutParams();
            params.bottomMargin = offset;
            mFooterView.requestLayout();
        }
    
        //按下时的位置,当事件被阻止时,第一次ActionDown事件,onTouchEvent无法获取这个位置
        //须要在onInterceptTouchEvent获取
        private float downY;
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            //假设当前是正在刷新而且是下拉状态,则当前视图处理事件
            if (isRefreshing && mScrollY < 0) {
                return true;
            }
            //假设当前是刷新状态,而且处于上拉状态,则视图不可进入下拉状态
            if (mScrollY >= 0 && isRefreshing)
                return false;
            boolean isIntercept = false;
            int action = ev.getAction();
            switch (action) {
                case MotionEvent.ACTION_DOWN:
                    downY = ev.getY();
                    break;
                case MotionEvent.ACTION_MOVE:
    
                    //假设达到了滑动条件
                    if (Math.abs(ev.getY() - downY) >= mTouchSlop) {
                        if (ev.getY() - downY > 0) {//下拉
                            isIntercept = isEnablePullDown();
                            if (isIntercept)//设置下拉还是上滑的状态,true表示下拉动作
                                isPullDownMotion = true;
    
                        } else {//上滑
                            isIntercept = isEnableLoadMore();
                            if (isIntercept)//false表示上滑状态
                                isPullDownMotion = false;
                        }
                    } else {
                        isIntercept = false;
                    }
    
                    break;
                case MotionEvent.ACTION_CANCEL:
                    //假设返回true,子视图假设包括点击事件。则无法进行处理
                    isIntercept = false;
                    break;
                case MotionEvent.ACTION_UP:
                    isIntercept = false;
                    break;
            }
            return isIntercept;
        }
    
        //记录当前滑动的位置
        private int mScrollY;
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            int action = event.getAction();
            switch (action) {
                case MotionEvent.ACTION_DOWN:
                    //第一次推断时,downY仅仅能从intercept中获取,之后从这里获取
                    downY = event.getY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    float dY = event.getY() - downY;
                    if (isPullDownMotion)//下拉
                        doPullDownMoveEvent(dY);
                    else {//自己主动载入很多其它
                        doLoadMoreEvent(dY);
                    }
                    break;
                case MotionEvent.ACTION_UP:
    
                    if (isPullDownMotion) {
                        //处理下拉结果
                        doPullDownResult();
                    } else {
                        //处理滑动载入很多其它结果
                        doLoadMoreResult();
                    }
    
                    break;
                case MotionEvent.ACTION_CANCEL:
                    //同ACTION_UP
                    if (isPullDownMotion) {
                        doPullDownResult();
                    } else {
                        doLoadMoreResult();
    
                    }
    
                    break;
            }
            return true;
        }
    
        /**
         * 处理载入很多其它
         */
        private void doLoadMoreResult() {
            //手指松开时,假设FooterView,没有全然滑动出来。自己主动滑动出来
            scrollTo(0, mFooterHeight);
            mScrollY = getScrollY();
            if (!isRefreshing) {
                isRefreshing = true;
                if (listener != null)
                    listener.onLoadMore();
            }
    
        }
    
        /**
         * 载入很多其它完毕后调用
         */
        public void completeLoadMore() {
            scrollTo(0, 0);
            mScrollY = 0;
            isRefreshing = false;
            LinearLayoutManager manager = (LinearLayoutManager) mRefreshView.getLayoutManager();
            int count = manager.getItemCount();
            if (count > lastVisible + 1)//载入了很多其它数据
                mRefreshView.scrollToPosition(lastVisible + 1);
        }
    
        //处理载入很多其它
        private void doLoadMoreEvent(float y) {
            int scrollY = (int) (mScrollY - y);
            if (scrollY < 0) {
                scrollY = 0;
            }
    
    
            if (scrollY > mFooterHeight) {
                scrollY = mFooterHeight;
            }
            Log.v("@scrollY", scrollY + "");
            scrollTo(0, scrollY);
        }
    
        /**
         * 处理释放后的操作
         */
        private void doPullDownResult() {
            //先获取如今滑动到的位置
            mScrollY = getScrollY();
            switch (mCurrentState) {
                case PULL_TO_REFRESH:
                    mCurrentState = NORMAL;
                    mHeaderView.onNormal();
                    smoothScrollTo(0);
                    break;
                case RELEASE_TO_REFRESH:
                    //松开时,假设是释放刷新。则開始进行刷新动作
                    if (!isRefreshing) {
                        //滑动到指定位置
                        smoothScrollTo(-mHeaderHeight);
    
                        mHeaderView.onRefreshing();
                        isRefreshing = true;
                        if (listener != null) {
                            //运行刷新回调
                            listener.onPullDownRefresh();
    
                        }
                        //假设当前滑动位置太靠下,则滑动到指定刷新位置
                    } else if (mScrollY < -mHeaderHeight) {
                        smoothScrollTo(-mHeaderHeight);
                    }
                    break;
    
            }
        }
    
        /**
         * 获取到数据后,调用
         */
        public void completeRefresh() {
            isRefreshing = false;
            mCurrentState = NORMAL;
            smoothScrollTo(0);
        }
    
        private void doPullDownMoveEvent(float y) {
            int scrollY = (int) (mScrollY - y * RATIO);
            if (scrollY > 0) {
                scrollY = 0;
            }
            if (scrollY < -mMaxScrollHeight) {
                scrollY = -mMaxScrollHeight;
            }
            scrollTo(0, scrollY);
            if (isRefreshing)
                return;
            //设置对应的状态
            if (scrollY == 0) {
                mCurrentState = NORMAL;
                mHeaderView.onNormal();
            } else if (scrollY <= 0 && scrollY > -mHeaderHeight) {
                mCurrentState = PULL_TO_REFRESH;
                mHeaderView.onPullToRefresh(Math.abs(scrollY));
            } else if (scrollY <= -mHeaderHeight && scrollY >= -mMaxScrollHeight) {
                mCurrentState = RELEASE_TO_REFRESH;
                mHeaderView.onReleaseToRefresh(Math.abs(scrollY));
            }
        }
    
        /**
         * 从当前位置滑动到指定位置
         * @param y 滑动到的位置
         */
        private void smoothScrollTo(int y) {
            int dY = y - mScrollY;
            mScroller.startScroll(0, mScrollY, 0, dY, 500);
            invalidate();
    
        }
    
        private RecyclerView getRefreshView() {
            mRefreshView = new RecyclerView(mContext);
            mRefreshView.setLayoutManager(new LinearLayoutManager(mContext, LinearLayoutManager.VERTICAL, false));
            mRefreshView.addItemDecoration(new CustomItemDecoration(mContext, CustomItemDecoration.VERTICAL_LIST));
            return mRefreshView;
        }
    
        public void setAdapter(RecyclerView.Adapter adapter) {
            mRefreshView.setAdapter(adapter);
        }
    
        /**
         * 推断列表是否在最顶端
         * @return
         */
        private boolean isEnablePullDown() {
            LinearLayoutManager manager = (LinearLayoutManager) mRefreshView.getLayoutManager();
            int firstVisible = manager.findFirstVisibleItemPosition();
            //当前还没有数据,能够进行下拉
            if(manager.getItemCount()==0)
                return true;
            return firstVisible == 0 && manager.getChildAt(0).getTop() == 0;
        }
    
        /**
         * 推断列表是否滑动到了最底部
         * @return
         */
        private boolean isEnableLoadMore() {
            LinearLayoutManager manager = (LinearLayoutManager) mRefreshView.getLayoutManager();
            lastVisible = manager.findLastVisibleItemPosition();
            int totalCount = manager.getItemCount();
            //假设没有数据,仅仅能下拉刷新
            if (totalCount == 0)
                return false;
            int bottom = manager.findViewByPosition(lastVisible).getBottom();
            int decorHeight = manager.getBottomDecorationHeight(mRefreshView.getChildAt(0));
            //最后一个child的底部位置在当前视图的上面
            return totalCount == lastVisible + 1 && bottom + decorHeight <= getMeasuredHeight();
        }
    
        @Override
        public void computeScroll() {
            if (mScroller.computeScrollOffset()) {
                scrollTo(0, mScroller.getCurrY());
                mScrollY = mScroller.getCurrY();
                invalidate();
            }
        }
    
        /**
         * 设置Footer的内容
         */
        public void setFooterViewState(boolean hasMoreData){
            if(hasMoreData){
                mFooterView.onRefreshing();
            }else{
                mFooterView.onNoData();
            }
        }
        public interface OnRefreshListener {
            void onPullDownRefresh();
    
            void onLoadMore();
        }
    
        public void setOnRefreshListener(OnRefreshListener listener) {
    
            this.listener = listener;
        }
    }
    


        接下来一步一步的进行分析。
        首先,我们在构造方法中。调用了init(Context)方法,例如以下:
      private void init(Context context) {
            mContext = context;
            this.setOrientation(VERTICAL);
            mRefreshView = getRefreshView();
            mRefreshView.setBackgroundColor(Color.WHITE);
            LayoutParams listParams = new LayoutParams(-1, -1);
            mRefreshView.setLayoutParams(listParams);
            addView(mRefreshView);
            //加入HeaderView
            mHeaderView = new CustomHeaderView(context);
            LayoutParams params = new LayoutParams(-1, -2);
            mHeaderView.setLayoutParams(params);
            addView(mHeaderView, 0);
            //加入FooterView
            mFooterView = new CustomFooterView(context);
            LayoutParams fParams = new LayoutParams(-1, 200);
            mFooterView.setLayoutParams(fParams);
            addView(mFooterView, -1);
            //弹性滑动实现
            mScroller = new Scroller(context);
            mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
        }


    方法中。我们构造了HeaderView。RecyclerView以及FooterView。HeaderView和FooterView是简单的自己定义View,RecyclerView是直接构造的。而且在init()方法中。构造了Scroller,用于后面的弹性滑动须要。

    接着,后面会运行onSizeChange方法:
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            //第一次获取相关參数,并隐藏HeaderView。FooterView
            if (isFirst) {
                mHeaderHeight = mHeaderView.getMeasuredHeight();
                mMaxScrollHeight = mHeaderHeight * 3;
                resetHeaderLayout(-mHeaderHeight);
    
                mFooterHeight = mFooterView.getMeasuredHeight();
                resetFooterLayout(-mFooterHeight);
                Log.v("@mHeaderHeight", mHeaderHeight + "");
                Log.v("@mFooterHeight", mFooterHeight + "");
                isFirst = false;
            }
        }
    设置了一个isFirst变量。防止反复设置里面的代码。

    在这种方法里面,我们获取了HeaderView,FooterView的測量高。而且,我们设置了HeaderView。FooterView的margin值,隐藏了头部和尾部。


        再接着,就是与用户的交互过程,即用户的触摸事件。这个实现过程。分成两块,一块是下拉刷新,一块是滑动究竟部自己主动载入。这里我们一起分析。

     //按下时的位置,当事件被阻止时。第一次ActionDown事件,onTouchEvent无法获取这个位置
        //须要在onInterceptTouchEvent获取
        private float downY;
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            //假设当前是正在刷新而且是下拉状态,则当前视图处理事件
            if (isRefreshing && mScrollY < 0) {
                return true;
            }
            //假设当前是刷新状态。而且处于上拉状态。则视图不可进入下拉状态
            if (mScrollY >= 0 && isRefreshing)
                return false;
            boolean isIntercept = false;
            int action = ev.getAction();
            switch (action) {
                case MotionEvent.ACTION_DOWN:
                    downY = ev.getY();
                    break;
                case MotionEvent.ACTION_MOVE:
    
                    //假设达到了滑动条件
                    if (Math.abs(ev.getY() - downY) >= mTouchSlop) {
                        if (ev.getY() - downY > 0) {//下拉
                            isIntercept = isEnablePullDown();
                            if (isIntercept)//设置下拉还是上滑的状态,true表示下拉动作
                                isPullDownMotion = true;
    
                        } else {//上滑
                            isIntercept = isEnableLoadMore();
                            if (isIntercept)//false表示上滑状态
                                isPullDownMotion = false;
                        }
                    } else {
                        isIntercept = false;
                    }
    
                    break;
                case MotionEvent.ACTION_CANCEL:
                    //假设返回true,子视图假设包括点击事件,则无法进行处理
                    isIntercept = false;
                    break;
                case MotionEvent.ACTION_UP:
                    isIntercept = false;
                    break;
            }
            return isIntercept;
        }
    


    onInterceptTouchEvent的作用,假设返回值为true,表示拦截事件。则事件交个当前控件进行处理,子View无法接收到事件;否则事件交给子View处理。  我们要知道,一般,一个事件序列,仅仅能由一个控件处理,也就是说。假设这个控件消费了ACTION_DOWN事件,那么,后面的ACTION_MOVE等都会交给他处理。可是。假设他的parentView在ACTION_MOVE中,拦截了事件,事件将会转交给ParentView的onTouchEvent处理。

     然后,開始分析代码,
      //假设当前是正在刷新而且是下拉状态,则当前视图处理事件
            if (isRefreshing && mScrollY < 0) {
                return true;
            }
            //假设当前是刷新状态,而且处于上拉状态。则视图不可进入下拉状态
            if (mScrollY >= 0 && isRefreshing)
                return false;

    假设当前为下拉而且在刷新状态,则返回true,表示拦截事件,RecyclerView不可滑动。假设当前是滑动载入很多其它。而且刷新状态。则不拦截,由于后面我想在滑动载入很多其它时,RecyclerView能够滑动。  截止后面。在ACTION_DOWN事件中,我们记录下按下的y轴位置。然后是ACTION_MOVE;
         //假设达到了滑动条件
                    if (Math.abs(ev.getY() - downY) >= mTouchSlop) {
                        if (ev.getY() - downY > 0) {//下拉
                            isIntercept = isEnablePullDown();
                            if (isIntercept)//设置下拉还是上滑的状态,true表示下拉动作
                                isPullDownMotion = true;
    
                        } else {//上滑
                            isIntercept = isEnableLoadMore();
                            if (isIntercept)//false表示上滑状态
                                isPullDownMotion = false;
                        }
                    } else {
                        isIntercept = false;
                    }
    


    mTouchSlop是滑动的最小值。假设小于这个值,我们觉得没有滑动。大于这个值才算滑动。

    假设当前滑动,大于这个值,继续走里面的if推断,假设当前是下拉状态,而且是能够下拉。那么拦截事件,否则进行滑动载入很多其它。假设满足滑动载入很多其它的条件,那么能够向上滑动。而且整个过程,用isPullDownMotion记录下了是向上还是向下的动作。后面在onTouchEvent中须要使用。最后,ACTION_UP和ACTION_CANCEL不拦截。假设拦截,会影响到子View的点击事件。


        最后是onTouchEvent
      //记录当前滑动的位置
        private int mScrollY;
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            int action = event.getAction();
            switch (action) {
                case MotionEvent.ACTION_DOWN:
                    //第一次推断时,downY仅仅能从intercept中获取。之后从这里获取
                    downY = event.getY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    float dY = event.getY() - downY;
                    if (isPullDownMotion)//下拉
                        doPullDownMoveEvent(dY);
                    else {//自己主动载入很多其它
                        doLoadMoreEvent(dY);
                    }
                    break;
                case MotionEvent.ACTION_UP:
    
                    if (isPullDownMotion) {
                        //处理下拉结果
                        doPullDownResult();
                    } else {
                        //处理滑动载入很多其它结果
                        doLoadMoreResult();
                    }
    
                    break;
                case MotionEvent.ACTION_CANCEL:
                    //同ACTION_UP
                    if (isPullDownMotion) {
                        doPullDownResult();
                    } else {
                        doLoadMoreResult();
    
                    }
    
                    break;
            }
            return true;
        }



    看下拉环节(滑动载入很多其它相似,不再介绍),下拉过程ACTION_MOVE中先调用doPullDownMoveEvent,然后在ACTION_UP中调用了doPullDownResult。先看duPullDownMoveEvent
     private void doPullDownMoveEvent(float y) {
            int scrollY = (int) (mScrollY - y * RATIO);
            if (scrollY > 0) {
                scrollY = 0;
            }
            if (scrollY < -mMaxScrollHeight) {
                scrollY = -mMaxScrollHeight;
            }
            scrollTo(0, scrollY);
            if (isRefreshing)
                return;
            //设置对应的状态
            if (scrollY == 0) {
                mCurrentState = NORMAL;
                mHeaderView.onNormal();
            } else if (scrollY <= 0 && scrollY > -mHeaderHeight) {
                mCurrentState = PULL_TO_REFRESH;
                mHeaderView.onPullToRefresh(Math.abs(scrollY));
            } else if (scrollY <= -mHeaderHeight && scrollY >= -mMaxScrollHeight) {
                mCurrentState = RELEASE_TO_REFRESH;
                mHeaderView.onReleaseToRefresh(Math.abs(scrollY));
            }
        }


    先计算滑动的位置,把滑动的位置限制在-mMaxScrollHeight和0之间,这样就不会滑动到其它地方。然后调用View的scrollTo方法,滑动到对应位置。

    这样就完毕了触摸滑动。    后面。我们在通过滑动的位置,设置对应的状态。并回调HeaderView的各个状态的方法。


    然后再看doPullDownResult
    /**
         * 处理释放后的操作
         */
        private void doPullDownResult() {
            //先获取如今滑动到的位置
            mScrollY = getScrollY();
            switch (mCurrentState) {
                case PULL_TO_REFRESH:
                    mCurrentState = NORMAL;
                    mHeaderView.onNormal();
                    smoothScrollTo(0);
                    break;
                case RELEASE_TO_REFRESH:
                    //松开时。假设是释放刷新,则開始进行刷新动作
                    if (!isRefreshing) {
                        //滑动到指定位置
                        smoothScrollTo(-mHeaderHeight);
    
                        mHeaderView.onRefreshing();
                        isRefreshing = true;
                        if (listener != null) {
                            //运行刷新回调
                            listener.onPullDownRefresh();
    
                        }
                        //假设当前滑动位置太靠下,则滑动到指定刷新位置
                    } else if (mScrollY < -mHeaderHeight) {
                        smoothScrollTo(-mHeaderHeight);
                    }
                    break;
    
            }
        }
    


    这种方法,就是手指松开屏幕时触发。然后推断移动过程中的状态。假设是下拉刷新状态,则又一次恢复到下拉之前,调用smoothScrollTo(后面分析详细实现)。弹性滑动到初始位置。并设置状态为NORMAL状态。    假设松开时,是释放刷新状态。那么。先弹性滑动到刷新位置,并运行回调方法。


        如今分析。弹性滑动 smoothScrollTo
       /**
         * 从当前位置滑动到指定位置
         * @param y 滑动到的位置
         */
        private void smoothScrollTo(int y) {
            int dY = y - mScrollY;
            mScroller.startScroll(0, mScrollY, 0, dY, 500);
            invalidate();
    
        }



    这种方法,必须要配合computeScroll使用。不然是没有效果的。详细的原因,须要查看View的绘制流程,这里我就不详细分析。

        @Override
        public void computeScroll() {
            if (mScroller.computeScrollOffset()) {
                scrollTo(0, mScroller.getCurrY());
                mScrollY = mScroller.getCurrY();
                invalidate();
            }
        }
    



    这个过程,是从Scroller的startScroll方法開始的,这种方法,调用后。Scroller的computeScrollOffset仅仅要动作没有运行完,就会一直返回true。调用了startScroll方法。须要调用invalide()来引起computeScroll方法的调用,而里面scrollTo方法。才是真正实现位移的原因。里面再调用invalidate又又一次引起了computeScroll方法,直到Scroller的computeOffset方法返回false。

       这样,每次都移动一小段位置,就实现了平滑滑动的效果。



    用法。布局文件
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <com.mjc.recyclerviewdemo.refresh.PullToRefreshRecycleView
            android:id="@+id/prrv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    </LinearLayout>
    



    Activity中
      mPRRV = (PullToRefreshRecycleView) findViewById(R.id.prrv);
            mPRRV.setOnRefreshListener(new PullToRefreshRecycleView.OnRefreshListener() {
                @Override
                public void onPullDownRefresh() {
                    mHandler.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            datas.add(0, "add");
                            mAdapter.notifyDataSetChanged();
                            mPRRV.completeRefresh();
                        }
                    }, 2000);
    
                }
    
                @Override
                public void onLoadMore() {
                    mHandler.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            datas.add("李四");
                            datas.add("王五");
                            datas.add("张三");
                            datas.add("李四");
                            datas.add("王五");
                            datas.add("张三");
    
                            mAdapter.notifyDataSetChanged();
                            mPRRV.completeLoadMore();
                        }
                    }, 1000);
    
                }
            });









    附:源代码


  • 相关阅读:
    RobotFrameWork-Python简易环境搭建
    Oracle常用函数
    Oracle多表连接查询Join
    测试用例的常用设计方法
    App自动化测试之Monkey使用场景及常用命令
    App自动化测试之Adb基础命令使用
    App弱网测试
    前端基础(二)
    py自动化框架(二)
    html基础js
  • 原文地址:https://www.cnblogs.com/zhchoutai/p/8412565.html
Copyright © 2011-2022 走看看