IRcyclerView從會用到會寫系列的第二篇,上一篇我們講了怎么使用IRrecyclerView,這一節(jié)我們來看看,IRecyclerView到底是怎樣實現(xiàn)效果的。
首先,你得要有一個猜想
首先,我們會很疑惑,感覺IRecyclerView用起來和recyclerview沒什么區(qū)別,就只是setAdapter變成了setIAdapter。那么,我們就從setIAdapter開始看起。
點進去,我們看見的setIAdapter的方法,以及現(xiàn)在可以看到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));
}
噫!!上面的都是些什么鬼!最后還是調(diào)用了setAdapter,只不過卻傳了一個我們不知道的參數(shù)。而且上面的名字很熟悉,明顯就是我們的刷新item,頭結(jié)點,尾節(jié)點,和加載item。
嘛嘛,我們開始猜想,他是不是其實已經(jīng)早就已經(jīng)將我們所需要的四個動態(tài)item早就加載了list里面,但是返回給我們的就只是我們設(shè)置的參數(shù)。
我們隨便點進去一個會發(fā)現(xiàn),噫!這不就是一個空的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,所以為什么我們設(shè)置adapter的時候,卻和往常的一樣,沒有因為多加了幾個item而是item的順序發(fā)生了錯亂, 所以 ,這個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的構(gòu)造函數(shù), 前面5行不用解釋了吧,就是對象變量的初始化。重點是最后一個,我滴天?。?!registerAdapterDataObserver()這個是什么東東,而且還傳入了一個自定義的mOberserver。
好了!到了這里我們就又要科普一下了!這個registerAdapterDataObserver是個什么東東。讓我們看一下官方的說法:
Register a new observer to listen for data changes.
嘛嘛!什么意思呢!就是說注冊一個新的監(jiān)聽者,去監(jiān)聽data的變化。我們大家都知道,當(dāng)我們使用recyclerview的過程中,如果數(shù)據(jù)變化了,我們就要使用notifydatachange去改變recycleview中的item的數(shù)量以及順序。而具體要怎么變,其實也就是通過adapter中的observer接收到的消息,然后具體在改變。
OK!科普結(jié)束。
那我們就去看看mObserver是怎樣響應(yīng)這個data變化的消息事件。

OK,我們可以看見,我們新建的adapterdataobserver里面重寫了所有的方法。
這里我們打開幾個重點的,有代表性的函數(shù)。
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);
}
我們發(fā)現(xiàn),噫?。槭裁丛诟聰?shù)據(jù)的時候,要向后移動兩個位置呢??咦咦?。?!這部又進一步證明我們的猜想嘛,因為那兩個是refreshview和handView,而他們本來就已經(jīng)先建好了的,只是需要動態(tài)的設(shè)置才能顯示出來。
然后我們繼續(xù)往下翻,哇塞,心里的石頭落地,這不就是印證了我們的猜想。根據(jù)不同的類型,創(chuàng)建不同的itemview。

public void onBindViewHolder(ViewHolder holder, int position) {
if(1 < position && position < this.mAdapter.getItemCount() + 2) {
this.mAdapter.onBindViewHolder(holder, position - 2);
}
}
所以我們的猜想是正確的,irecyclerview內(nèi)部自己實現(xiàn)了adapter,已經(jīng)將我們可能需要到的四個view已經(jīng)加了進去。只是暫時讓我們看不見而已。既然我們已經(jīng)知道這一點了,那么,開始下一步,我什么我們看不見他們!
為什么我們看不見他們
讓我們回到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,或者,我們給他們的高度為自適應(yīng),而開始的時候,他們里面都是沒有子view的,所以我們看不見他們,他們開始時候的高度為0。
itemView的動態(tài)改變?不存在的
其實到這里,我們已經(jīng)能夠知道了頭節(jié)點和尾節(jié)點的實現(xiàn)方式,然而我們現(xiàn)在關(guān)心的是下拉刷新的效果以及,上拉加載更多的頁面的變化。
首先,讓我們想一下,如果是你,想要實現(xiàn)下拉刷新,你會怎么做,一般的想法就是當(dāng)我們的listview已經(jīng)處于在頂端的時候,你還在繼續(xù)下拉的話,那么,我們的refreshView是不是就應(yīng)該出現(xiàn)了,所以說,你肯定需要檢測到我們下拉的手勢,所以,我們需要重寫onTouchEvent方法。
所以,繼續(xù)看源碼,我們果然發(fā)現(xiàn)了他在確實是重寫了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) {
...
}
//下拉或者是放松狀態(tài)
if (mStatus == STATUS_SWIPING_TO_REFRESH || mStatus == STATUS_RELEASE_TO_REFRESH) {
...
fingerMove(dy);//變化的Y值
...
}
}
}
break;
在move事件中,看見,通過每次對比dy,得到是否是上劃還是下劃,然后添加不同的狀態(tài),最后調(diào)用fingerMove。還有一個重點就是triggerCondition,他是決定該事件能否開始我們下拉刷新的操作。我們看看最后兩個條件的函數(shù)。
//判斷當(dāng)前是否是 拖 這個狀態(tài)
private boolean isFingerDragging() {
return getScrollState() == SCROLL_STATE_DRAGGING;
}
//當(dāng)前是否已經(jīng)到達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最終調(diào)用了move。move如下:
private void move(int dy) {
if (dy != 0) {
int height = mRefreshHeaderContainer.getMeasuredHeight() + dy;
setRefreshHeaderContainerHeight(height);
mRefreshTrigger.onMove(false, false, height);
}
}
好的 我相信你已經(jīng)也懂了對不對。
最后是setRefreshHeaderContainerHeight,他就動態(tài)改變了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();
}
當(dāng)觸摸事件終止和觸摸事件手指抬起的時候,開始動畫效果。
嘛嘛!我們已經(jīng)知道,在我們拉完了之后,頁面向上彈起,其實也就是動畫效果實現(xiàn)了。
private void onFingerUpStartAnimating() {
if (mStatus == STATUS_RELEASE_TO_REFRESH) {
startScrollReleaseStatusToRefreshingStatus();
} else if (mStatus == STATUS_SWIPING_TO_REFRESH) {
startScrollSwipingToRefreshStatusToDefaultStatus();
}
}
到這里 ,我想大家也已經(jīng)明白了IRecyclerView大致的道理。
1.重寫了recyclerView,將其多增加了四個節(jié)點,作為我們后期動態(tài)改變的itemview
2.在touchEvent中,定了我們特定的事件,實現(xiàn)下拉效果的refreshView的動態(tài)改變
3.在手指抬起,或者觸摸事件終止的時候,通過動畫,實現(xiàn)頁面的反彈回去。
好的?。?!接下來我們開始重復(fù)造輪子,自己仿照這IRecyclerView的方式,自己寫一個IRecyclerViewCopy,并在內(nèi)部增加幾個常見的示例,不用每次使用的時候,還要自己寫頁面動畫效果。那么今天就這樣咯?。hhhhhhh