美文网首页
IRcyclerView从会用到会写(二)

IRcyclerView从会用到会写(二)

作者: JokerHerry | 来源:发表于2017-12-23 01:13 被阅读0次

    IRcyclerView从会用到会写系列的第二篇,上一篇我们讲了怎么使用IRrecyclerView,这一节我们来看看,IRecyclerView到底是怎样实现效果的。

    首先,你得要有一个猜想

    首先,我们会很疑惑,感觉IRecyclerView用起来和recyclerview没什么区别,就只是setAdapter变成了setIAdapter。那么,我们就从setIAdapter开始看起。

    点进去,我们看见的setIAdapter的方法,以及现在可以看到IRecyclerView是继承自RecyclerView的,所以拥有recyclerView的所有方法。

        public void setIAdapter(Adapter adapter) {
            this.ensureRefreshHeaderContainer();
            this.ensureHeaderViewContainer();
            this.ensureFooterViewContainer();
            this.ensureLoadMoreFooterContainer();
            this.setAdapter(new WrapperAdapter(adapter, this.mRefreshHeaderContainer, this.mHeaderViewContainer, this.mFooterViewContainer, this.mLoadMoreFooterContainer));
        }
    

    噫!!上面的都是些什么鬼!最后还是调用了setAdapter,只不过却传了一个我们不知道的参数。而且上面的名字很熟悉,明显就是我们的刷新item,头结点,尾节点,和加载item。
    嘛嘛,我们开始猜想,他是不是其实已经早就已经将我们所需要的四个动态item早就加载了list里面,但是返回给我们的就只是我们设置的参数。

    我们随便点进去一个会发现,噫!这不就是一个空的viewlayout嘛。

        private void ensureRefreshHeaderContainer() {
            if(this.mRefreshHeaderContainer == null) {
                this.mRefreshHeaderContainer = new RefreshHeaderLayout(this.getContext());
                this.mRefreshHeaderContainer.setLayoutParams(new LayoutParams(-1, 0));
            }
        }
    

    再看看他们的定义

        private RefreshHeaderLayout mRefreshHeaderContainer;
        private FrameLayout mLoadMoreFooterContainer;
        private LinearLayout mHeaderViewContainer;
        private LinearLayout mFooterViewContainer;
    

    就更加坚定了我们的之前的猜想,这些就是viewLayout。但是他们却传给了一个不知姓名的WrapperAdapter,所以为什么我们设置adapter的时候,却和往常的一样,没有因为多加了几个item而是item的顺序发生了错乱, 所以 ,这个WrapperAdapter一定有鬼,而且他最终肯定也是一个adapter,所噶,那我们进去看看这个WrapperAdapter到底是什么东西。

        public WrapperAdapter(Adapter adapter, RefreshHeaderLayout refreshHeaderContainer, LinearLayout headerContainer, LinearLayout footerContainer, FrameLayout loadMoreFooterContainer) {
            this.mAdapter = adapter;
            this.mRefreshHeaderContainer = refreshHeaderContainer;
            this.mHeaderContainer = headerContainer;
            this.mFooterContainer = footerContainer;
            this.mLoadMoreFooterContainer = loadMoreFooterContainer;
            this.mAdapter.registerAdapterDataObserver(this.mObserver);
        }
    

    点进去我们就看见了WrapperAdapter的构造函数, 前面5行不用解释了吧,就是对象变量的初始化。重点是最后一个,我滴天!!!registerAdapterDataObserver()这个是什么东东,而且还传入了一个自定义的mOberserver。

    好了!到了这里我们就又要科普一下了!这个registerAdapterDataObserver是个什么东东。让我们看一下官方的说法:
    Register a new observer to listen for data changes.
    嘛嘛!什么意思呢!就是说注册一个新的监听者,去监听data的变化。我们大家都知道,当我们使用recyclerview的过程中,如果数据变化了,我们就要使用notifydatachange去改变recycleview中的item的数量以及顺序。而具体要怎么变,其实也就是通过adapter中的observer接收到的消息,然后具体在改变。
    OK!科普结束。

    那我们就去看看mObserver是怎样响应这个data变化的消息事件。


    image.png

    OK,我们可以看见,我们新建的adapterdataobserver里面重写了所有的方法。
    这里我们打开几个重点的,有代表性的函数。

            public void onItemRangeChanged(int positionStart, int itemCount) {
                WrapperAdapter.this.notifyItemRangeChanged(positionStart + 2, itemCount);
            }
    
            public void onItemRangeInserted(int positionStart, int itemCount) {
                WrapperAdapter.this.notifyItemRangeInserted(positionStart + 2, itemCount);
            }
    

    我们发现,噫!!为什么在更新数据的时候,要向后移动两个位置呢??咦咦!!!这部又进一步证明我们的猜想嘛,因为那两个是refreshview和handView,而他们本来就已经先建好了的,只是需要动态的设置才能显示出来。

    然后我们继续往下翻,哇塞,心里的石头落地,这不就是印证了我们的猜想。根据不同的类型,创建不同的itemview。

    image.png
        public void onBindViewHolder(ViewHolder holder, int position) {
            if(1 < position && position < this.mAdapter.getItemCount() + 2) {
                this.mAdapter.onBindViewHolder(holder, position - 2);
            }
        }
    

    所以我们的猜想是正确的,irecyclerview内部自己实现了adapter,已经将我们可能需要到的四个view已经加了进去。只是暂时让我们看不见而已。既然我们已经知道这一点了,那么,开始下一步,我什么我们看不见他们!

    为什么我们看不见他们

    让我们回到IRecyclerView文件中,看看那itemlayout都是怎么定义的。

        private void ensureRefreshHeaderContainer() {
            if (mRefreshHeaderContainer == null) {
                mRefreshHeaderContainer = new RefreshHeaderLayout(getContext());
                mRefreshHeaderContainer.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0));
            }
        }
    
        private void ensureLoadMoreFooterContainer() {
            if (mLoadMoreFooterContainer == null) {
                mLoadMoreFooterContainer = new FrameLayout(getContext());
                mLoadMoreFooterContainer.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
            }
        }
    
    

    看到了吧,在初始化他的时候,我们给他的高度为0,或者,我们给他们的高度为自适应,而开始的时候,他们里面都是没有子view的,所以我们看不见他们,他们开始时候的高度为0。

    itemView的动态改变?不存在的

    其实到这里,我们已经能够知道了头节点和尾节点的实现方式,然而我们现在关心的是下拉刷新的效果以及,上拉加载更多的页面的变化。

    首先,让我们想一下,如果是你,想要实现下拉刷新,你会怎么做,一般的想法就是当我们的listview已经处于在顶端的时候,你还在继续下拉的话,那么,我们的refreshView是不是就应该出现了,所以说,你肯定需要检测到我们下拉的手势,所以,我们需要重写onTouchEvent方法。

    所以,继续看源码,我们果然发现了他在确实是重写了onTouchEvent方法。并且,里面的东西还挺多,我们挑重点的来说。

    case MotionEvent.ACTION_MOVE: {
                    ...
                    final int dx = x - mLastTouchX;
                    final int dy = y - mLastTouchY;
    
                    //保证 存在 开启 存在刷新页面 手指滑动 保证在最头上
                    final boolean triggerCondition = isEnabled() && mRefreshEnabled && mRefreshHeaderView != null && isFingerDragging() && canTriggerRefresh();
                    if (triggerCondition) {
                        ...
                        //下拉
                        if (dy > 0 && mStatus == STATUS_DEFAULT) {
                          ...
                        }//上拉
                        else if (dy < 0) {
                            ...
                        }
    
                        //下拉或者是放松状态
                        if (mStatus == STATUS_SWIPING_TO_REFRESH || mStatus == STATUS_RELEASE_TO_REFRESH) {
                            ...
                            fingerMove(dy);//变化的Y值
                            ...
                        }
                    }
                }
                break;
    

    在move事件中,看见,通过每次对比dy,得到是否是上划还是下划,然后添加不同的状态,最后调用fingerMove。还有一个重点就是triggerCondition,他是决定该事件能否开始我们下拉刷新的操作。我们看看最后两个条件的函数。

        //判断当前是否是 拖 这个状态
        private boolean isFingerDragging() {
            return getScrollState() == SCROLL_STATE_DRAGGING;
        }
    
        //当前是否已经到达recyclerView的顶部
        public boolean canTriggerRefresh() {
            final Adapter adapter = getAdapter();
            if (adapter == null || adapter.getItemCount() <= 0) {
                return true;
            }
            View firstChild = getChildAt(0);
            int position = getChildLayoutPosition(firstChild);
            if (position == 0) {
                if (firstChild.getTop() == mRefreshHeaderContainer.getTop()) {
                    return true;
                }
            }
            return false;
        }
    

    秒懂对不对!!!再来看看fingerMove是什么操作。因为fingerMov最终调用了move。move如下:

        private void move(int dy) {
            if (dy != 0) {
                int height = mRefreshHeaderContainer.getMeasuredHeight() + dy;
                setRefreshHeaderContainerHeight(height);
                mRefreshTrigger.onMove(false, false, height);
            }
        }
    

    好的 我相信你已经也懂了对不对。
    最后是setRefreshHeaderContainerHeight,他就动态改变了refreshView的高度。也就是我们下拉时候,把refreshView拉出来的操作。

        private void setRefreshHeaderContainerHeight(int height) {
            mRefreshHeaderContainer.getLayoutParams().height = height;
            mRefreshHeaderContainer.requestLayout();
        }
    

    最后回到我们的ontouchEvent,它里面还有两个事件:

                case MotionEvent.ACTION_UP: {
                    onFingerUpStartAnimating();
                }
                break;
    
                case MotionEvent.ACTION_CANCEL: {
                    onFingerUpStartAnimating();
                }
    

    当触摸事件终止和触摸事件手指抬起的时候,开始动画效果。
    嘛嘛!我们已经知道,在我们拉完了之后,页面向上弹起,其实也就是动画效果实现了。

        private void onFingerUpStartAnimating() {
            if (mStatus == STATUS_RELEASE_TO_REFRESH) {
                startScrollReleaseStatusToRefreshingStatus();
            } else if (mStatus == STATUS_SWIPING_TO_REFRESH) {
                startScrollSwipingToRefreshStatusToDefaultStatus();
            }
        }
    

    到这里 ,我想大家也已经明白了IRecyclerView大致的道理。
    1.重写了recyclerView,将其多增加了四个节点,作为我们后期动态改变的itemview
    2.在touchEvent中,定了我们特定的事件,实现下拉效果的refreshView的动态改变
    3.在手指抬起,或者触摸事件终止的时候,通过动画,实现页面的反弹回去。

    好的!!!接下来我们开始重复造轮子,自己仿照这IRecyclerView的方式,自己写一个IRecyclerViewCopy,并在内部增加几个常见的示例,不用每次使用的时候,还要自己写页面动画效果。那么今天就这样咯!!hhhhhhhh

    相关文章

      网友评论

          本文标题:IRcyclerView从会用到会写(二)

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