IRcyclerView从会用到会写(二)

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,980评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,422评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,130评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,553评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,408评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,326评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,720评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,373评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,678评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,722评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,486评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,335评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,738评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,283评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,692评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,893评论 2 335

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,137评论 25 707
  • 雨葵_5264阅读 970评论 0 0
  • 多久都没有旧地重游了 午夜梦回的思绪 每每在一棵树下打断 我停下来冥想的时候 树又老去了三载 写在叶子上的年轮 断...
    T骑士阅读 184评论 2 4
  • 360云盘宣布个人用户服务要下台,目前说是2017年4月或12月,这再次引起我们对云盘的关注。云时代里,云盘能发挥...
    小斌PPT阅读 2,094评论 6 4