這RecyclerView的特效炸了!
大家好,我是劉望舒,騰訊最具價值專家,著有三本業(yè)內(nèi)知名暢銷書,連續(xù)五年蟬聯(lián)電子工業(yè)出版社年度優(yōu)秀作者,百度百科收錄的資深技術(shù)專家。
前華為面試官、獨角獸公司技術(shù)總監(jiān)。
想要加入?BATcoder技術(shù)群,公號回復BAT?即可。
作者:android超級兵?
https://blog.csdn.net/weixin_44819566
前言
還是老套路,先來看看實現(xiàn)的效果!

在寫這個效果之前,需要熟悉Rv的回收復用機制,因為實現(xiàn)這個效果,需要自定義LayoutManager()…
眾所周知,RecyclerView 是一個可滑動的View,那么他的回收/復用入口一定是在onTouchEvent()事件中
滑動過程中響應的是MotionEvent.ACTION_MOVE事件,所以直接來這里找找看!!
緩存機制
onTouchEvent()入口
#RecyclerView.java
?@Override
public?boolean?onTouchEvent(MotionEvent?e)?{
????final?int?action?=?e.getActionMasked();
?????switch?(action)?{
????????????........................................
???????????????........只展示代碼思路,細節(jié)請自行查看........
???????????????........................................
????????????case?MotionEvent.ACTION_MOVE:?{
????????????if?(mScrollState?==?SCROLL_STATE_DRAGGING)?{
????????????????????mLastTouchX?=?x?-?mScrollOffset[0];
????????????????????mLastTouchY?=?y?-?mScrollOffset[1];
????????????????????//?關(guān)鍵代碼1
????????????????????if?(scrollByInternal(
????????????????????????????canScrollHorizontally???dx?:?0,
????????????????????????????canScrollVertically???dy?:?0,
????????????????????????????vtev))?{
????????????????????????getParent().requestDisallowInterceptTouchEvent(true);
????????????????????}
????????????????????if?(mGapWorker?!=?null?&&?(dx?!=?0?||?dy?!=?0))?{
????????????????????????mGapWorker.postFromTraversal(this,?dx,?dy);
????????????????????}
????????????????}
????????????}
????????????break;
????}
}
接著找scrollByInternal(int x, int y, MotionEvent ev)方法
#RecyclerView.java
boolean?scrollByInternal(int?x,?int?y,?MotionEvent?ev)?{
?????if?(mAdapter?!=?null)?{
????????????........................................
???????????????........只展示代碼思路,細節(jié)請自行查看........
???????????????........................................
????????????if?(x?!=?0)?{
????????????????//?關(guān)鍵代碼2?去到?LinearLayoutManager?執(zhí)行fill方法
????????????????consumedX?=?mLayout.scrollHorizontallyBy(x,?mRecycler,?mState);
????????????????unconsumedX?=?x?-?consumedX;
????????????}
????????????if?(y?!=?0)?{
????????????????//?關(guān)鍵代碼2?去到LinearLayoutManager?執(zhí)行fill方法
????????????????consumedY?=?mLayout.scrollVerticallyBy(y,?mRecycler,?mState);
????????????????unconsumedY?=?y?-?consumedY;
????????????}
????????}
????????....
}
現(xiàn)在走到了mLayout.scrollHorizontallyBy(x, mRecycler, mState);
接著去LinearLayoutManager()?中去找scrollHorizontallyBy()?方法
#LinearLayoutManager.java
????@Override
????public?int?scrollVerticallyBy(int?dy,?RecyclerView.Recycler?recycler,
??????????????????????????????????RecyclerView.State?state)?{
????????if?(mOrientation?==?HORIZONTAL)?{
????????????return?0;
????????}
????????//?關(guān)鍵代碼3
????????return?scrollBy(dy,?recycler,?state);
????}
scrollBy()->
#LinearLayoutManager.java
?int?scrollBy(int?dy,?RecyclerView.Recycler?recycler,?RecyclerView.State?state)?{
???????........................................
?????........只展示代碼思路,細節(jié)請自行查看........
?????........................................
?????final?int?consumed?=?mLayoutState.mScrollingOffset
????????????????//?關(guān)鍵代碼4
????????????????+?fill(recycler,?mLayoutState,?state,?false);
}
接著找到fill()方法
#LinearLayoutManager.java
int?fill(RecyclerView.Recycler?recycler,?LayoutState?layoutState,
?????????????RecyclerView.State?state,?boolean?stopOnFocusable)?{
????????if?(layoutState.mScrollingOffset?!=?LayoutState.SCROLLING_OFFSET_NaN)?{
????????????//?關(guān)鍵代碼19?緩存ViewHolder
????????????recycleByLayoutState(recycler,?layoutState);
????????}
????????//?循環(huán)調(diào)用
????????while?((layoutState.mInfinite?||?remainingSpace?>?0)?&&?layoutState.hasMore(state))?{
???????????//?關(guān)鍵代碼5?[用來4級復用]
????????????layoutChunk(recycler,?state,?layoutState,?layoutChunkResult);
??????????????????........................................
????????????????????........只展示代碼思路,細節(jié)請自行查看........
??????????????????........................................
????????}
????}
看到這里只需要記住以下兩點即可:
recycleByLayoutState(recycler, layoutState); 緩存ViewHolder layoutChunk(recycler, state, layoutState, layoutChunkResult); 四級復用
#LinearLayoutManager.java
?private?void?recycleByLayoutState(RecyclerView.Recycler?recycler,?LayoutState?layoutState)?{
????????if?(layoutState.mLayoutDirection?==?LayoutState.LAYOUT_START)?{
????????????//?關(guān)鍵代碼21?緩存底部
????????????recycleViewsFromEnd(recycler,?layoutState.mScrollingOffset);
????????}?else?{
????????????//?關(guān)鍵代碼20?緩存頭部
????????????recycleViewsFromStart(recycler,?layoutState.mScrollingOffset);
????????}
????}
#LinearLayoutManager.java
?private?void?recycleViewsFromStart(RecyclerView.Recycler?recycler,?int?dt)?{
????????if?(mShouldReverseLayout)?{
????????????for?(int?i?=?childCount?-?1;?i?>=?0;?i--)?{
????????????...
????????????????????//?關(guān)鍵代碼22
????????????????????recycleChildren(recycler,?childCount?-?1,?i);
????????????????????return;
????????????}
????????}?else?{
????????????for?(int?i?=?0;?i?????????????...
????????????????????//?關(guān)鍵代碼23
????????????????????recycleChildren(recycler,?0,?i);
????????????????????return;
????????????}
????????}
????}
#LinearLayoutManager.java
private?void?recycleChildren(RecyclerView.Recycler?recycler,?int?startIndex,?int?endIndex)?{
????????if?(startIndex?==?endIndex)?{
????????????return;
????????}
????????if?(endIndex?>?startIndex)?{
????????????for?(int?i?=?endIndex?-?1;?i?>=?startIndex;?i--)?{
????????????????//?移除View??關(guān)鍵代碼23?[執(zhí)行到RecyclerView.removeAndRecycleViewAt()]
????????????????removeAndRecycleViewAt(i,?recycler);
????????????}
????????}?else?{
????????????for?(int?i?=?startIndex;?i?>?endIndex;?i--)?{
????????????????removeAndRecycleViewAt(i,?recycler);
????????????}
????????}
????}
#RecyclerView.java
????????//?關(guān)鍵代碼24
????????public?void?removeAndRecycleViewAt(int?index,?Recycler?recycler)?{
????????????final?View?view?=?getChildAt(index);
????????????removeViewAt(index);
????????????//?關(guān)鍵代碼25
????????????recycler.recycleView(view);
????????}
#RecyclerView.java
?public?void?recycleView(View?view)?{
????????????.......
????????????ViewHolder?holder?=?getChildViewHolderInt(view);
????????????//?緩存
????????????recycleViewHolderInternal(holder);
????????}
#RecyclerView.java
void?recycleViewHolderInternal(ViewHolder?holder)?{
????????????........................................
????????????........只展示代碼思路,細節(jié)請自行查看........
????????????........................................
?????????????boolean?cached?=?false;
????????????if?(forceRecycle?||?holder.isRecyclable())?{
????????????????//?mViewCacheMax?=?緩存的最大值?
????????????????//?mViewCacheMax?=?2
????????????????//?如果viewHolder是無效、未被移除、未被標記的
????????????????if?(mViewCacheMax?>?0
????????????????????????&&?!holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
????????????????????????|?ViewHolder.FLAG_REMOVED
????????????????????????|?ViewHolder.FLAG_UPDATE
????????????????????????|?ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN))?{
????????????????????int?cachedViewSize?=?mCachedViews.size();
????????????????????//?關(guān)鍵代碼24
????????????????????//?mViewCacheMax?=?2
????????????????????if?(cachedViewSize?>=?mViewCacheMax?&&?cachedViewSize?>?0)?{
????????????????????????//?如果viewholder存滿2個則移除第0個位置?
????????????????????????//?保證mCachedViews?最多能緩存2個ViewHolder
????????????????????????recycleCachedViewAt(0);
????????????????????????cachedViewSize--;
????????????????????}
????????????????????....
????????????????????//?保存ViewHolder數(shù)據(jù)?[mCachedViews數(shù)據(jù)不會超過2個]
????????????????????mCachedViews.add(targetCacheIndex,?holder);
????????????????????cached?=?true;
????????????????}
?????????????????if?(!cached)?{
????????????????????//?當ViewHolder不改變時候(只有一個ViewHolder)?就會直接存到緩存池中
????????????????????addViewHolderToRecycledViewPool(holder,?true);
????????????????????recycled?=?true;
????????????????}
????????????????........................................
???????????????????........只展示代碼思路,細節(jié)請自行查看........
????????????????........................................
????????}
#RecyclerView.java
????void?recycleCachedViewAt(int?cachedViewIndex)?{
????????????...
????????????ViewHolder?viewHolder?=?mCachedViews.get(cachedViewIndex);
????????????//?關(guān)鍵代碼25
????????????//?添加到ViewPool到緩存里面取
????????????addViewHolderToRecycledViewPool(viewHolder,?true);
????????????//?將第0個ViewHolder移除
????????????mCachedViews.remove(cachedViewIndex);
????????}
#RecyclerView.java
void?addViewHolderToRecycledViewPool(ViewHolder?holder,?boolean?dispatchRecycled)?{
????????????.....
????????????//?向緩存池中?保存ViewHolder?關(guān)鍵代碼28
????????????getRecycledViewPool().putRecycledView(holder);
????????}
#RecyclerView.java
//?SparseArray?類似與?HashMap
//?特點:?key相同會保留最后一個,
//??????會根據(jù)key的int值排序(從小到大)
SparseArray?mScrap?=?new?SparseArray<>();
?public?void?putRecycledView(ViewHolder?scrap)?{
???????//?獲取ViewHolder布局類型
??????final?int?viewType?=?scrap.getItemViewType();
??????//?根據(jù)布局類型來獲取ViewHolder
???????final?ArrayList?scrapHeap?=?getScrapDataForType(viewType).mScrapHeap;
???????//?判斷緩存池的大小
???????//?mScrap.get(viewType).mMaxScrap?默認為?5
???????//?同一種ViewType?只保存5個ViewHolder
????????if?(mScrap.get(viewType).mMaxScrap?<=?scrapHeap.size())?{
???????????return;
????????}
???????//?清空ViewHolder記錄
????????scrap.resetInternal();
????????//add
????????scrapHeap.add(scrap);
}
?//?清空ViewHolder記錄
?void?resetInternal()?{
????????????mFlags?=?0;
????????????mPosition?=?NO_POSITION;
????????????mOldPosition?=?NO_POSITION;
????????????mItemId?=?NO_ID;
????????????mPreLayoutPosition?=?NO_POSITION;
????????????mIsRecyclableCount?=?0;
????????????mShadowedHolder?=?null;
????????????mShadowingHolder?=?null;
????????????clearPayload();
????????????mWasImportantForAccessibilityBeforeHidden?=?View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
????????????mPendingAccessibilityState?=?PENDING_ACCESSIBILITY_STATE_NOT_SET;
????????????clearNestedRecyclerViewIfNotNested(this);
????????}
//?根據(jù)不同viewType?獲取ViewHolder
?private?ScrapData?getScrapDataForType(int?viewType)?{
????????????ScrapData?scrapData?=?mScrap.get(viewType);
????????????if?(scrapData?==?null)?{
????????????????scrapData?=?new?ScrapData();
????????????????mScrap.put(viewType,?scrapData);
????????????}
????????????return?scrapData;
????????}

小結(jié)



onLayout()入口
????#RecyclerView.java
????@Override
????protected?void?onLayout(boolean?changed,?int?l,?int?t,?int?r,?int?b)?{
????????//?入口
????????dispatchLayout();
????}
#RecyclerView.java
void?dispatchLayout()?{
???????.....
???????dispatchLayoutStep2();
???????......
}
#RecyclerView.java
private?void?dispatchLayoutStep2()?{
??????????......
????????//?在這里先緩存
????????mLayout.onLayoutChildren(mRecycler,?mState);
????????.....
}
#LinearLayoutManager.java
?@Override
public?void?onLayoutChildren(RecyclerView.Recycler?recycler,?RecyclerView.State?state)?{
????????....
????????//會執(zhí)行到:?RecyclerView.detachAndScrapAttachedViews()
?????????detachAndScrapAttachedViews(recycler);
????????......
}
#RecyclerView.java
public?void?detachAndScrapAttachedViews(Recycler?recycler)?{
????????????final?int?childCount?=?getChildCount();
????????????for?(int?i?=?childCount?-?1;?i?>=?0;?i--)?{
????????????????final?View?v?=?getChildAt(i);
????????????????//?回收機制關(guān)鍵代碼1
????????????????scrapOrRecycleView(recycler,?i,?v);
????????????}
}
#RecyclerView.java
private?void?scrapOrRecycleView(Recycler?recycler,?int?index,?View?view)?{
????????????final?ViewHolder?viewHolder?=?getChildViewHolderInt(view);
???????????...
????????????if?(viewHolder.isInvalid()?&&?!viewHolder.isRemoved()
????????????????????&&?!mRecyclerView.mAdapter.hasStableIds())?{
????????????????removeViewAt(index);
????????????????//?緩存機制關(guān)鍵代碼2?主要用來處理?cacheView?,RecyclerViewPool的緩存
????????????????recycler.recycleViewHolderInternal(viewHolder);
????????????}?else?{
????????????????detachViewAt(index);
????????????????//?緩存機制關(guān)鍵代碼3
????????????????recycler.scrapView(view);
????????????}
}
緩存機制關(guān)鍵代碼2 主要用來處理 cacheView ,RecyclerViewPool的緩存recycler.recycleViewHolderInternal(viewHolder); // 這個關(guān)鍵點上面已經(jīng)分析過了!!,忘記的ctrl+F搜索看看看一看 recycler.scrapView(view); // 緩存屏幕內(nèi)的ViewHolder
?void?scrapView(View?view)?{
????????????final?ViewHolder?holder?=?getChildViewHolderInt(view);
????????????//?如果標記沒有移除,或者失效等清空?就會緩存
????????????if?(holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED?|?ViewHolder.FLAG_INVALID)
????????????????????||?!holder.isUpdated()?||?canReuseUpdatedViewHolder(holder))?{
????????????????holder.setScrapContainer(this,?false);
????????????????//?一級緩存位置點1
????????????????mAttachedScrap.add(holder);
????????????}?else?{
????????????????if?(mChangedScrap?==?null)?{
????????????????????mChangedScrap?=?new?ArrayList();
????????????????}
????????????????holder.setScrapContainer(this,?true);
????????????????//?一級緩存位置點2
????????????????mChangedScrap.add(holder);
????????????}
}

復用機制
#LinearLayoutManager.java
int?fill(RecyclerView.Recycler?recycler,?LayoutState?layoutState,
?????????????RecyclerView.State?state,?boolean?stopOnFocusable)?{
????????final?int?start?=?layoutState.mAvailable;
????????if?(layoutState.mScrollingOffset?!=?LayoutState.SCROLLING_OFFSET_NaN)?{
????????????.....
????????????//?關(guān)鍵代碼19?[用來4級緩存]
????????????recycleByLayoutState(recycler,?layoutState);
????????}
?????????....
????????//?循環(huán)調(diào)用
????????while?((layoutState.mInfinite?||?remainingSpace?>?0)?&&?layoutState.hasMore(state))?{
???????????//?關(guān)鍵代碼5?[用來4級復用]
????????????layoutChunk(recycler,?state,?layoutState,?layoutChunkResult);
??????????????????........................................
????????????????????........只展示代碼思路,細節(jié)請自行查看........
??????????????????........................................
????????}
}
#LinearLayoutManager.java
void?layoutChunk(RecyclerView.Recycler?recycler,?RecyclerView.State?state,
?????????????????????LayoutState?layoutState,?LayoutChunkResult?result)?{
????????//?獲取當前view
????????//?關(guān)鍵代碼6
????????View?view?=?layoutState.next(recycler);
????????//?測量View
????????measureChildWithMargins(view,?0,?0);
????????.....
}
#LinearLayoutManager.java
View?next(RecyclerView.Recycler?recycler)?{
???????????.....
????????????//?關(guān)鍵代碼7?[復用機制入口]
????????????final?View?view?=?recycler.getViewForPosition(mCurrentPosition);
????????????return?view;
}
#RecyclerView.java
?public?View?getViewForPosition(int?position)?{
????????????//?關(guān)鍵代碼8
????????????return?getViewForPosition(position,?false);
}
View?getViewForPosition(int?position,?boolean?dryRun)?{
?????//?關(guān)鍵代碼10?所有的復用都在這里
??????return?tryGetViewHolderForPositionByDeadline(position,?dryRun,?FOREVER_NS).itemView;
}
#RecyclerView.java
??ViewHolder?tryGetViewHolderForPositionByDeadline(int?position,
?????????????????????????????????????????????????????????boolean?dryRun,?long?deadlineNs)?{
?????????????ViewHolder?holder?=?null;
????????????//?一級別復用?[mChangedScrap]
????????????if?(mState.isPreLayout())?{
????????????????//?關(guān)鍵代碼11
????????????????holder?=?getChangedScrapViewForPosition(position);
????????????????fromScrapOrHiddenOrCache?=?holder?!=?null;
????????????}
????????????//?一級復用?[mAttachedScrap]
????????????if?(holder?==?null)?{
????????????????//?通過位置
????????????????//?關(guān)鍵代碼12
????????????????holder?=?getScrapOrHiddenOrCachedHolderForPosition(position,?dryRun);
????????????}
????????????//?二級復用?[mCachedViews]
?????????????if?(holder?==?null)?{
????????????????????//?獲取布局類型
????????????????????final?int?type?=?mAdapter.getItemViewType(offsetPosition);
????????????????????//?2)?Find?from?scrap/cache?via?stable?ids,?if?exists
????????????????????//?2)?通過穩(wěn)定ID從廢料/緩存中查找(如果存在)
????????????????????if?(mAdapter.hasStableIds())?{
????????????????????//?關(guān)鍵代碼13?根據(jù)Id來復用
????????????????????holder?=?getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
????????????????????????????type,?dryRun);
????????????????????}
?????????????}
?????????????//?三級復用?【自定義復用】
????????????????if?(holder?==?null?&&?mViewCacheExtension?!=?null)?{
????????????????????//?關(guān)鍵代碼14
????????????????????//?自定義復用
????????????????????final?View?view?=?mViewCacheExtension
????????????????????????????.getViewForPositionAndType(this,?position,?type);
????????????????????if?(view?!=?null)?{
????????????????????????holder?=?getChildViewHolder(view);
?????????????????????}
????????????????}
????????????//?四級復用?[mRecyclerPool(緩存池復用)]
??????????????if?(holder?==?null)?{
????????????????????//?關(guān)鍵代碼15?從緩存池獲取viewHolder
????????????????????holder?=?getRecycledViewPool().getRecycledView(type);
????????????????}
????????????//?最終,如果走到這里,holder?==?0,表示沒有緩存,那么則創(chuàng)建ViewHolder
????????????if?(holder?==?null)?{
????????????????????//?如果四級緩存都是?null,?那么就由適配器創(chuàng)建?ViewHolder
????????????????????holder?=?mAdapter.createViewHolder(RecyclerView.this,?type);
????????????}
????????????//?走到這了的時候,ViewHolder?!=?null
????????????//?綁定布局
????????????if?(mState.isPreLayout()?&&?holder.isBound())?{
???????????????.....
????????????}?else?if?(!holder.isBound()?||?holder.needsUpdate()?||?holder.isInvalid())?{
?????????????????......
????????????????//?關(guān)鍵代碼17
????????????????//?在這里調(diào)?onBindViewHolder()?綁定數(shù)據(jù)
????????????????bound?=?tryBindViewHolderByDeadline(holder,?offsetPosition,?position,?deadlineNs);
????????????????......
????????????}
????????????......
}
private?boolean?tryBindViewHolderByDeadline(ViewHolder?holder,?int?offsetPosition,
????????????????????????????????????????????????????int?position,?long?deadlineNs)?{
???????????....
???????????//?最終綁定位置
????????????mAdapter.bindViewHolder(holder,?offsetPosition);
???????????...
}

探探效果實戰(zhàn)


public?class?CardStack3LayoutManager?extends?RecyclerView.LayoutManager?{
????@Override
????public?RecyclerView.LayoutParams?generateDefaultLayoutParams()?{
?????????return?new?RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
????????????????ViewGroup.LayoutParams.WRAP_CONTENT);
????}
?????//?必須重寫?在?RecyclerView->OnLayout()時候調(diào)用,用來擺放?Item位置
?????@Override
????public?void?onLayoutChildren(RecyclerView.Recycler?recycler,?RecyclerView.State?state)?{
????????super.onLayoutChildren(recycler,?state);
????}
}

#CardStack2LayoutManager.java
?//?最開始顯示個數(shù)
?public?static?final?int?MAX_SHOW_COUNT?=?4;
????@Override
????public?void?onLayoutChildren(RecyclerView.Recycler?recycler,?RecyclerView.State?state)?{
????????super.onLayoutChildren(recycler,?state);
?????????//?調(diào)用RecyclerView的緩存機制?緩存?ViewHolder
????????detachAndScrapAttachedViews(recycler);
????????//?最下面圖片下標
????????int?bottomPosition?=?0;
????????//?獲取所有圖片
????????int?itemCount?=?getItemCount();
????????if?(itemCount?>?MAX_SHOW_COUNT)?{
????????????//?獲取到從第幾張開始
????????????bottomPosition?=?itemCount?-?MAX_SHOW_COUNT;
????????}
?????????for?(int?i?=?bottomPosition;?i?????????????//?獲取當前view寬高
????????????View?view?=?recycler.getViewForPosition(i);
????????????addView(view);
????????????//?測量
????????????measureChildWithMargins(view,?0,?0);
//????????????getWidth()?RecyclerView?寬
//????????????getDecoratedMeasuredWidth()?View的寬
????????????int?widthSpace?=?getWidth()?-?getDecoratedMeasuredWidth(view);
????????????int?heightSpace?=?getHeight()?-?getDecoratedMeasuredHeight(view);
????????????//?LinearLayoutManager#layoutChunk#layoutDecoratedWithMargins
????????????//?繪制布局
????????????layoutDecoratedWithMargins(view,?widthSpace?/?2,
????????????????????heightSpace?/?2,
????????????????????widthSpace?/?2?+?getDecoratedMeasuredWidth(view),
????????????????????heightSpace?/?2?+?getDecoratedMeasuredHeight(view));
????????????}
}


#CardStack2LayoutManager.java
?//?最開始顯示個數(shù)
????public?static?final?int?MAX_SHOW_COUNT?=?4;
????//?item?平移Y軸距
????public?static?final?int?TRANSLATION_Y?=?20;
????//?縮放的大小
????public?static?final?float?SCALE?=?0.05f;
@Override
????public?void?onLayoutChildren(RecyclerView.Recycler?recycler,?RecyclerView.State?state)?{
????????super.onLayoutChildren(recycler,?state);
????????//?緩存?ViewHolder
????????detachAndScrapAttachedViews(recycler);
????????//?最下面圖片下標
????????int?bottomPosition?=?0;
????????//?獲取所有圖片
????????int?itemCount?=?getItemCount();
????????//如果所有圖片?>?顯示的圖片
????????if?(itemCount?>?MAX_SHOW_COUNT)?{
????????????//?獲取到從第幾張開始
????????????bottomPosition?=?itemCount?-?MAX_SHOW_COUNT;
????????}
????????for?(int?i?=?bottomPosition;?i?????????????//?獲取當前view寬高
????????????View?view?=?recycler.getViewForPosition(i);
????????????addView(view);
????????????//?測量
????????????measureChildWithMargins(view,?0,?0);
//????????????getWidth()?RecyclerView?寬
//????????????getDecoratedMeasuredWidth()?View的寬
????????????int?widthSpace?=?getWidth()?-?getDecoratedMeasuredWidth(view);
????????????int?heightSpace?=?getHeight()?-?getDecoratedMeasuredHeight(view);
????????????//?LinearLayoutManager#layoutChunk#layoutDecoratedWithMargins
????????????//?繪制布局
????????????layoutDecoratedWithMargins(view,?widthSpace?/?2,
????????????????????heightSpace?/?2,
????????????????????widthSpace?/?2?+?getDecoratedMeasuredWidth(view),
????????????????????heightSpace?/?2?+?getDecoratedMeasuredHeight(view));
????????????/*
?????????????*?作者:android?超級兵
?????????????*?TODO?itemCount?-?1??=?最后一個元素
????????????????????最后一個元素?-?i?=?倒數(shù)的元素
?????????????*/
????????????int?level?=?itemCount?-?1?-?i;
????????????if?(level?>?0)?{
????????????????int?value?=?toDip(view.getContext(),?TRANSLATION_Y);
????????????????//?如果不是最后一個才縮放
????????????????if?(level?1)?{
????????????????????//?平移
????????????????????view.setTranslationY(value?*?level);
????????????????????//?縮放
????????????????????view.setScaleX(1?-?SCALE?*?level);
????????????????????view.setScaleY(1?-?SCALE?*?level);
????????????????}?else?{
????????????????????//?最下面的View?和前一個View布局一樣(level?-?1)
????????????????????view.setTranslationY(value?*?(level?-?1));
????????????????????view.setScaleX(1?-?SCALE?*?(level?-?1));
????????????????????view.setScaleY(1?-?SCALE?*?(level?-?1));
????????????????}
????????????}
????????}
????}
????private?int?toDip(Context?context,?float?value)?{
????????return?(int)?TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,?value,?context.getResources().getDisplayMetrics());
????}

public?class?SlideCardStackCallBack2<T>?extends?ItemTouchHelper.SimpleCallback?{
????private?final?CardStackAdapter?mAdapter;
????public?SlideCardStackCallBack2(CardStackAdapter?mAdapter) ?{
????????super(0,?15);
????????this.mAdapter?=?mAdapter;
????}
????//?拖拽使用,不用管
????@Override
????public?boolean?onMove(@NonNull?RecyclerView?recyclerView,?@NonNull?RecyclerView.ViewHolder?viewHolder,?@NonNull?RecyclerView.ViewHolder?target)?{
????????return?false;
????}
???//?滑動結(jié)束后的處理
????@Override
????public?void?onSwiped(@NonNull?RecyclerView.ViewHolder?viewHolder,?int?direction)?{
????}
}
參數(shù)一:dragDirs 拖拽 參數(shù)二:swipeDirs 滑動
#ItemTouchHelper.java
/**
?????*?Up?direction,?used?for?swipe?&?drag?control.
?????*/
????public?static?final?int?UP?=?1;????//1
????/**
?????*?Down?direction,?used?for?swipe?&?drag?control.
?????*/
????public?static?final?int?DOWN?=?1?<1;?//2?
????/**
?????*?Left?direction,?used?for?swipe?&?drag?control.
?????*/
????public?static?final?int?LEFT?=?1?<2;?//4
????/**
?????*?Right?direction,?used?for?swipe?&?drag?control.
?????*/
????public?static?final?int?RIGHT?=?1?<3;?//8?
如果需要上下滑動 那么就是 UP+DOWN = 1+2 = 3 如果是上下左滑動就是 UP + DOWN + LEFT = 1 + 2 + 4 = 7 那么如果是上下左右滑動就是 UP + DOWN + LEFT + RIGHT = 15
#SlideCardStackCallBack2.java
@Override
????public?void?onSwiped(@NonNull?RecyclerView.ViewHolder?viewHolder,?int?direction)?{
????????//?當前滑動的View下標
????????int?layoutPosition?=?viewHolder.getLayoutPosition();
????????//?刪除當前滑動的元素
????????CardStackBean?bean?=?mAdapter.getData().remove(layoutPosition);
????????//?添加到集合第0個位置?造成循環(huán)滑動的效果
????????mAdapter.addData(0,?bean);
????????mAdapter.notifyDataSetChanged();
}

#SlideCardStackCallBack2.java
@Override
????public?void?onChildDraw(@NonNull?Canvas?c,?@NonNull?RecyclerView?recyclerView,?@NonNull?RecyclerView.ViewHolder?viewHolder,?float?dX,?float?dY,?int?actionState,?boolean?isCurrentlyActive)?{
????????super.onChildDraw(c,?recyclerView,?viewHolder,?dX,?dY,?actionState,?isCurrentlyActive);
????????int?maxDistance?=?recyclerView.getWidth()?/?2;
????????????//?dx?=?當前滑動x位置
????????????//?dy?=?當前滑動y位置
????????//sqrt?開根號
????????double?sqrt?=?Math.sqrt((dX?*?dX?+?dY?*?dY));
????????//?放大系數(shù)
????????double?scaleRatio?=?sqrt?/?maxDistance;
????????//?系數(shù)最大為1?
????????if?(scaleRatio?>?1.0)?{
????????????scaleRatio?=?1.0;
????????}
????????int?childCount?=?recyclerView.getChildCount();
????????//?循環(huán)所有數(shù)據(jù)
????????for?(int?i?=?0;?i?????????????View?view?=?recyclerView.getChildAt(i);
????????????int?valueDip?=?toDip(view.getContext(),?20f);
????????????/*
?????????????*?作者:android?超級兵
?????????????*?TODO
?????????????*???childCount?-?1?=??itemView總個數(shù)
?????????????*????childCount?-?1?-?i?=?itemView總個數(shù)?-?i?=?從最后一個開始
?????????????*
?????????????*?假設?childCount?-?1?=?7
?????????????*?????i累加
?????????????*?????那么level?=?childCount?-?1?-?0?=?7
?????????????*?????那么level?=?childCount?-?1?-?1?=?6
?????????????*?????那么level?=?childCount?-?1?-?2?=?5
?????????????*?????那么level?=?childCount?-?1?-?3?=?4
?????????????*?????那么level?=?childCount?-?1?-?4?=?3
?????????????*??????。。。。
?????????????*/
????????????int?level?=?childCount?-?1?-?i;
????????????if?(level?>?0)?{
????????????????//?最大顯示疊加個數(shù):CardStack2LayoutManager.MAX_SHOW_COUNT?=?4
????????????????if?(level?1)?{
????????????????????//?縮放比例:?CardStack2LayoutManager.SCALE?=?0.05
????????????????????float?scale?=?CardStack2LayoutManager.SCALE;
????????????????????//?valueDip?*?level??=?原始平移距離
????????????????????//?scaleRatio?*?valueDip?=?平移系數(shù)
????????????????????//?valueDip?*?level?-?scaleRatio?*?valueDip?=?手指滑動過程中的Y軸平移距離
????????????????????//?因為是Y軸,所以向上平移是?-?號
????????????????????view.setTranslationY((float)?(valueDip?*?level?-?scaleRatio?*?valueDip));
????????????????????//?1?-?scale?*?level?=?原始縮放大小
????????????????????//?scaleRatio?*?scale?=?縮放系數(shù)
????????????????????//?因為是需要放大,所以這里是?+?號
????????????????????view.setScaleX((float)?((1?-?scale?*?level)?+?scaleRatio?*?scale));
????????????????????view.setScaleY((float)?((1?-?scale?*?level)?+?scaleRatio?*?scale));
????????????????}
????????????}
????????}
????}
????private?int?toDip(Context?context,?float?value)?{
????????return?(int)?TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,?value,?context.getResources().getDisplayMetrics());
????}

????//?創(chuàng)建拖拽
????????val?slideCardStackCallBack?=?SlideCardStackCallBack2(cardStackAdapter)
????????val?itemTouchHelper?=?ItemTouchHelper(slideCardStackCallBack)
????????//?綁定拖拽
????????itemTouchHelper.attachToRecyclerView(rootRecyclerView)

?//?設置回彈距離
????@Override
????public?float?getSwipeThreshold(@NonNull?RecyclerView.ViewHolder?viewHolder)?{
????????return?0.3f;
????}
????//?設置回彈時間
????@Override
????public?long?getAnimationDuration(@NonNull?RecyclerView?recyclerView,?int?animationType,?float?animateDx,?float?animateDy)?{
????????return?3000;
????}


? 耗時2年,Android進階三部曲第三部《Android進階指北》出版!
為了防止失聯(lián),歡迎關(guān)注我的小號
??微信改了推送機制,真愛請星標本公號??
