日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android仿豆瓣书影音频道推荐表单堆叠列表RecyclerView-LayoutManager

發布時間:2024/1/1 Android 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android仿豆瓣书影音频道推荐表单堆叠列表RecyclerView-LayoutManager 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Android仿豆瓣書影音頻道推薦表單堆疊列表RecyclerView-LayoutManager


項目地址:https://github.com/CCY0122/FocusLayoutManager


效果預覽


截圖:


GIF:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-dq1PKEIw-1589702037735)(https://github.com/CCY0122/FocusLayoutManager/blob/master/pic/gif_hor_2.gif?raw=true)]


可自己監聽滾動編寫效果,如修改成仿MacOS文件瀏覽:

使用

focusLayoutManager =new FocusLayoutManager.Builder().layerPadding(dp2px(this, 14)).normalViewGap(dp2px(this, 14)).focusOrientation(FocusLayoutManager.FOCUS_LEFT).isAutoSelect(true).maxLayerCount(3).setOnFocusChangeListener(new FocusLayoutManager.OnFocusChangeListener() {@Overridepublic void onFocusChanged(int focusdPosition, int lastFocusdPosition) {}}).build(); recyclerView.setLayoutManager(focusLayoutManager);

各屬性意義見圖:




注意:因為item在不同區域隨著滑動會有不同的縮放,所以實際layerPadding、normalViewGap會被縮放計算。

調整動畫效果:

new FocusLayoutManager.Builder().......setSimpleTrasitionListener(new FocusLayoutManager.SimpleTrasitionListener() {@Overridepublic float getLayerViewMaxAlpha(int maxLayerCount) {return super.getLayerViewMaxAlpha(maxLayerCount);}@Overridepublic float getLayerViewMinAlpha(int maxLayerCount) {return super.getLayerViewMinAlpha(maxLayerCount);}@Overridepublic float getLayerChangeRangePercent() {return super.getLayerChangeRangePercent();}//and more//更多可重寫方法和釋義見接口聲明}).build();

自定義動畫/滾動監聽:

如果你想在滑動時不僅僅改變item的大小、透明度,你有更多的想法,可以監聽TrasitionListener,該監聽暴露了很多關鍵布局數據,

.......setSimpleTrasitionListener(null) //如果默認動畫不想要,移除之。or use removeTrasitionlistener(XXX) .addTrasitionListener(new FocusLayoutManager.TrasitionListener() {@Overridepublic void handleLayerView(FocusLayoutManager focusLayoutManager,View view, int viewLayer,int maxLayerCount, int position,float fraction, float offset) {}@Overridepublic void handleFocusingView(FocusLayoutManager focusLayoutManager,View view, int position,float fraction, float offset) {}@Overridepublic void handleNormalView(FocusLayoutManager focusLayoutManager, View view, int position, float fraction, float offset) {}})

各參數意義見接口注釋。
實際上SimpleTrasitionListener內部就會被轉為TrasitionListener

解析

自定義LayoutManager基礎知識

自備。
這個項目就我學習LayoutManager的實戰項目。(斷斷續續學習過很多次,還是得實際編碼才能掌握)
推薦幾篇我覺得好的自定義LayoutManager文章:
1、 張旭童的掌握自定義LayoutManager(一) 系列開篇 常見誤區、問題、注意事項,常用API
2、張旭童的掌握自定義LayoutManager(二) 實現流式布局
3、陳小緣的自定義LayoutManager第十一式之飛龍在天

自定義LayoutManager的注意事項

上面張旭童的文章里有指出很多自定義LayoutManager的誤區、注意事項,我補充幾點:

1、不要遍歷ItemCount

這個真的,是我認為最關鍵的一個注意事項。getItemCount獲取到的是什么?是列表的總item數量,它可能有幾千條幾萬條,甚至某些情況使用者會特意重寫getItemCount將其返回為Integer.MAX_VALUE(比如為了實現無限循環輪播)。你之所以自定義LayoutManager而不自定義ViewGroup,就是為了不管itemCount多少你都能hold住。所以你不應該在布局相關代碼中遍歷ItemCount!!誠然,遍歷它往往可以獲取很多有用的數據,對后續的布局的計算、子View是否在屏幕內等判斷非常有用,但請盡量不要遍歷它(除非你的LM夠特殊)。
張旭童說的沒錯,很多文章都存在誤導,我還看到過有篇”喜歡“數很多的文章里有類似這么一段代碼:

for (int i = 0; i < getItemCount(); i++) {View view = recycler.getViewForPosition(i);addView(view);......

???
對于初次布局,這不就是有多少item就onCreateViewHolder多少次了么。緩存池總數 = item總數?之后的回收復用操作也沒意義了。

2、注意調用getChildCount時機

在列表滾動時,一般都要判斷子View是否還在屏幕內,若不在了則回收。那么獲取子View的邏輯應該在detachAndScrapAttachedViews(or detachAndScrapView等)之前。見下面代碼的打印:

//分離全部的view,放入臨時緩存log("before。child count = " + getChildCount() + ";scrap count = " + recycler.getScrapList().size());detachAndScrapAttachedViews(recycler);log("after。child count = " + getChildCount() + ";scrap count = " + recycler.getScrapList().size()); //打印結果://before。child count = 5;scrap count = 0//after。child count = 0;scrap count = 5

另外,不用多說,recycler.getViewForPosition應在detachAndScrapAttachedViews之后

3、回收子View小技巧

這是在陳小緣那篇文章里學到的:
可以直接把Recycler里面的mAttachedScrap全部放進mRecyclerPool中,因為我們在一開始就已經調用了detachAndScrapAttachedViews方法將當前屏幕中有效的ViewHolder全部放進了mAttachedScrap,而在重新布局的時候,有用的Holder已經被重用了,也就是拿出去了,這個mAttachedScrap中剩下的Holder,都是不需要layout的,所以可以把它們都回收進mRecyclerPool中
實用哦。
(不知道對預布局是否有影響,但我代碼中并沒有判斷過isPreLayout,也測試過notifyItemRemoved,動畫正常)

布局實現

先把上面的細節圖重新貼一下

首先無視掉view的縮放、透明度變化。那么布局其實就這樣:

我們稱一個view從”普通view“滾動到”焦點view“為一次完整的聚焦滑動所需要移動的距離,定義其為onceCompleteScrollLength。

在普通view移動了一個onceCompleteScrollLength,堆疊View只移動了一個layerPadding。核心邏輯就這一句。

我們在scrollHorizontallyBy中記錄偏移量dx,保存一個累計偏移量mHorizontalOffset,然后用該偏移量除以onceCompleteScrollLength,就知道當前已經滾動了多少個item了,換句話說就是屏幕內第一個可見view的position知道了。同時能計算出一個onceCompleteScrollLength已經滾動了的百分比fraction,再用這個百分比換算出堆疊區域和普通區域布局起始位置的偏移量,然后可以開始布局了,對于堆疊區域的view,彼此之間距離一個layerPadding,對于普通區域view,彼此之間距離一個onceCompleteScrollLength。
見代碼:

@Overridepublic int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler,RecyclerView.State state) {//手指從右向左滑動,dx > 0; 手指從左向右滑動,dx < 0;//位移0、沒有子View 當然不移動if (dx == 0 || getChildCount() == 0) {return 0;}mHorizontalOffset += dx;//累加實際滑動距離dx = fill(recycler, state, dx);return dx;}/*** @param recycler* @param state* @param delta*/private int fill(RecyclerView.Recycler recycler, RecyclerView.State state, int delta) {int resultDelta = delta;//省略resultDelta = fillHorizontalLeft(recycler, state, delta);//省略return resultDelta;} /*** 水平滾動、向左堆疊布局** @param recycler* @param state* @param dx 偏移量。手指從右向左滑動,dx > 0; 手指從左向右滑動,dx < 0;*/private int fillHorizontalLeft(RecyclerView.Recycler recycler, RecyclerView.State state,int dx) {//----------------1、邊界檢測-----------------if (dx < 0) {//已達左邊界if (mHorizontalOffset < 0) {mHorizontalOffset = dx = 0;}}if (dx > 0) {//滑動到只剩堆疊view,沒有普通view了,說明已經到達右邊界了if (mLastVisiPos - mFirstVisiPos <= maxLayerCount - 1) {//因為scrollHorizontallyBy里加了一次dx,現在減回去mHorizontalOffset -= dx;dx = 0;}}//分離全部的view,放入臨時緩存detachAndScrapAttachedViews(recycler);//----------------2、初始化布局數據-----------------float startX = getPaddingLeft() - layerPadding;View tempView = null;int tempPosition = -1;if (onceCompleteScrollLength == -1) {//因為mFirstVisiPos在下面可能會被改變,所以用tempPosition暫存一下。tempPosition = mFirstVisiPos;tempView = recycler.getViewForPosition(tempPosition);measureChildWithMargins(tempView, 0, 0);onceCompleteScrollLength = getDecoratedMeasurementHorizontal(tempView) + normalViewGap;}//當前"一次完整的聚焦滑動"所在的進度百分比.百分比增加方向為向著堆疊移動的方向(即如果為FOCUS_LEFT,從右向左移動fraction將從0%到100%)float fraction =(Math.abs(mHorizontalOffset) % onceCompleteScrollLength) / (onceCompleteScrollLength * 1.0f);//堆疊區域view偏移量。在一次完整的聚焦滑動期間,其總偏移量是一個layerPadding的距離float layerViewOffset = layerPadding * fraction;//普通區域view偏移量。在一次完整的聚焦滑動期間,其總位移量是一個onceCompleteScrollLengthfloat normalViewOffset = onceCompleteScrollLength * fraction;boolean isLayerViewOffsetSetted = false;boolean isNormalViewOffsetSetted = false;//修正第一個可見的view:mFirstVisiPos。已經滑動了多少個完整的onceCompleteScrollLength就代表滑動了多少個itemmFirstVisiPos = (int) Math.floor(Math.abs(mHorizontalOffset) / onceCompleteScrollLength); //向下取整//臨時將mLastVisiPos賦值為getItemCount() - 1,放心,下面遍歷時會判斷view是否已溢出屏幕,并及時修正該值并結束布局mLastVisiPos = getItemCount() - 1;//...省略監聽回調//----------------3、開始布局-----------------for (int i = mFirstVisiPos; i <= mLastVisiPos; i++) {//屬于堆疊區域if (i - mFirstVisiPos < maxLayerCount) {View item;if (i == tempPosition && tempView != null) {//如果初始化數據時已經取了一個臨時view,可別浪費了!item = tempView;} else {item = recycler.getViewForPosition(i);}addView(item);measureChildWithMargins(item, 0, 0);startX += layerPadding;if (!isLayerViewOffsetSetted) {startX -= layerViewOffset;isLayerViewOffsetSetted = true;}//...省略監聽回調int l, t, r, b;l = (int) startX;t = getPaddingTop();r = (int) (startX + getDecoratedMeasurementHorizontal(item));b = getPaddingTop() + getDecoratedMeasurementVertical(item);layoutDecoratedWithMargins(item, l, t, r, b);} else {//屬于普通區域View item = recycler.getViewForPosition(i);addView(item);measureChildWithMargins(item, 0, 0);startX += onceCompleteScrollLength;if (!isNormalViewOffsetSetted) {startX += layerViewOffset;startX -= normalViewOffset;isNormalViewOffsetSetted = true;}//...省略監聽回調int l, t, r, b;l = (int) startX;t = getPaddingTop();r = (int) (startX + getDecoratedMeasurementHorizontal(item));b = getPaddingTop() + getDecoratedMeasurementVertical(item);layoutDecoratedWithMargins(item, l, t, r, b);//判斷下一個view的布局位置是不是已經超出屏幕了,若超出,修正mLastVisiPos并跳出遍歷if (startX + onceCompleteScrollLength > getWidth() - getPaddingRight()) {mLastVisiPos = i;break;}}}return dx;}

因為measure、layout調用的都是考慮了margin的api,所以布局時也要考慮到margin:

/*** 獲取某個childView在水平方向所占的空間,將margin考慮進去** @param view* @return*/public int getDecoratedMeasurementHorizontal(View view) {final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)view.getLayoutParams();return getDecoratedMeasuredWidth(view) + params.leftMargin+ params.rightMargin;}/*** 獲取某個childView在豎直方向所占的空間,將margin考慮進去** @param view* @return*/public int getDecoratedMeasurementVertical(View view) {final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)view.getLayoutParams();return getDecoratedMeasuredHeight(view) + params.topMargin+ params.bottomMargin;}

回收復用

用上面講的回收技巧:

/*** @param recycler* @param state* @param delta*/private int fill(RecyclerView.Recycler recycler, RecyclerView.State state, int delta) {int resultDelta = delta;//。。。省略recycleChildren(recycler);log("childCount= [" + getChildCount() + "]" + ",[recycler.getScrapList().size():" + recycler.getScrapList().size());return resultDelta;}/*** 回收需回收的Item。*/private void recycleChildren(RecyclerView.Recycler recycler) {List<RecyclerView.ViewHolder> scrapList = recycler.getScrapList();for (int i = 0; i < scrapList.size(); i++) {RecyclerView.ViewHolder holder = scrapList.get(i);removeAndRecycleView(holder.itemView, recycler);}}

接下來驗證下。

  • 驗證1
    張旭童:通過getChildCount()和recycler.getScrapList().size() 查看當前屏幕上的Item數量 和 scrapCache緩存區域的Item數量,合格的LayoutManager,childCount數量不應大于屏幕上顯示的Item數量,而scrapCache緩存區域的Item數量應該是0.
    編寫log并打印:
childCount= [5],[recycler.getScrapList().size():0 childCount= [6],[recycler.getScrapList().size():0 childCount= [6],[recycler.getScrapList().size():0 childCount= [6],[recycler.getScrapList().size():0 childCount= [6],[recycler.getScrapList().size():0 childCount= [6],[recycler.getScrapList().size():0 childCount= [6],[recycler.getScrapList().size():0 childCount= [5],[recycler.getScrapList().size():0 childCount= [6],[recycler.getScrapList().size():0 childCount= [6],[recycler.getScrapList().size():0 childCount= [6],[recycler.getScrapList().size():0 childCount= [6],[recycler.getScrapList().size():0 childCount= [6],[recycler.getScrapList().size():0

合格。

  • 驗證2
    用最直接的方法,打印onCreateViewHolder、onBindViewHolder看看到底復用了沒:
@NonNull@Overridepublic ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) {View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_card,viewGroup, false);view.setTag(++index);Log.d("ccy", "onCreateViewHolder = " + index);return new ViewHolder(view);}@Overridepublic void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) {Log.d("ccy", "onBindViewHolder,index = " + (int) (viewHolder.itemView.getTag()));}

在onCreateViewHolder創建view時,給他一個tag,然后onBindViewHolder中打印這個tag,以此查看是不用復用了view。打印如下

onCreateViewHolder = 1 onBindViewHolder,index = 1 onCreateViewHolder = 2 onBindViewHolder,index = 2 onCreateViewHolder = 3 onBindViewHolder,index = 3 onCreateViewHolder = 4 onBindViewHolder,index = 4 onCreateViewHolder = 5 onBindViewHolder,index = 5 onCreateViewHolder = 6 onBindViewHolder,index = 6 onCreateViewHolder = 7 onBindViewHolder,index = 7 onCreateViewHolder = 8 onBindViewHolder,index = 8 onBindViewHolder,index = 1 onBindViewHolder,index = 2 onBindViewHolder,index = 3 onBindViewHolder,index = 4 onBindViewHolder,index = 5 onBindViewHolder,index = 6 onBindViewHolder,index = 7 onBindViewHolder,index = 8 onCreateViewHolder = 9 onBindViewHolder,index = 9 onBindViewHolder,index = 2 onBindViewHolder,index = 3 onBindViewHolder,index = 1 onBindViewHolder,index = 4 onBindViewHolder,index = 5 onBindViewHolder,index = 6

我測試時手機一屏內最多可見約6個,從打印中可見它最多調用了9次onCreateViewHolder,這個次數完全可以接受。并且onBindViewHolder也在復用view。完全ojbk沒得問題

動畫效果

我做的動畫,就是在滑動期間漸變view的縮放比例、透明度,使得view看上去像一層一層堆疊上去的樣子。其實就是各種y = kx + b之類的計算,因為fill系列方法中已經計算出很多有用的數據了。
我的做法是,暴露出這么個接口:

/*** 滾動過程中view的變換監聽接口。屬于高級定制,暴露了很多關鍵布局數據。若定制要求不高,考慮使用{@link SimpleTrasitionListener}*/public interface TrasitionListener {/*** 處理在堆疊里的view。** @param focusLayoutManager* @param view view對象。請僅在方法體范圍內對view做操作,不要外部強引用它,view是要被回收復用的* @param viewLayer 當前層級,0表示底層,maxLayerCount-1表示頂層* @param maxLayerCount 最大層級* @param position item所在的position* @param fraction "一次完整的聚焦滑動"所在的進度百分比.百分比增加方向為向著堆疊移動的方向(即如果為FOCUS_LEFT* ,從右向左移動fraction將從0%到100%)* @param offset 當次滑動偏移量*/void handleLayerView(FocusLayoutManager focusLayoutManager, View view, int viewLayer,int maxLayerCount, int position, float fraction, float offset);/*** 處理正聚焦的那個View(即正處在從普通位置滾向聚焦位置時的那個view,即堆疊頂層view)** @param focusLayoutManager* @param view view對象。請僅在方法體范圍內對view做操作,不要外部強引用它,view是要被回收復用的* @param position item所在的position* @param fraction "一次完整的聚焦滑動"所在的進度百分比.百分比增加方向為向著堆疊移動的方向(即如果為FOCUS_LEFT* ,從右向左移動fraction將從0%到100%)* @param offset 當次滑動偏移量*/void handleFocusingView(FocusLayoutManager focusLayoutManager, View view, int position,float fraction, float offset);/*** 處理不在堆疊里的普通view(正在聚焦的那個view除外)** @param focusLayoutManager* @param view view對象。請僅在方法體范圍內對view做操作,不要外部強引用它,view是要被回收復用的* @param position item所在的position* @param fraction "一次完整的聚焦滑動"所在的進度百分比.百分比增加方向為向著堆疊移動的方向(即如果為FOCUS_LEFT* ,從右向左移動fraction將從0%到100%)* @param offset 當次滑動偏移量*/void handleNormalView(FocusLayoutManager focusLayoutManager, View view, int position,float fraction, float offset);}

然后在fill系列方法的對應位置回調該接口即可:

/*** 變換監聽接口。*/private List<TrasitionListener> trasitionListeners; /*** 水平滾動、向左堆疊布局** @param recycler* @param state* @param dx 偏移量。手指從右向左滑動,dx > 0; 手指從左向右滑動,dx < 0;*/private int fillHorizontalLeft(RecyclerView.Recycler recycler, RecyclerView.State state,int dx) {//省略。。。。。 //----------------3、開始布局-----------------for (int i = mFirstVisiPos; i <= mLastVisiPos; i++) {//屬于堆疊區域if (i - mFirstVisiPos < maxLayerCount) {//省略。。。。。 if (trasitionListeners != null && !trasitionListeners.isEmpty()) {for (TrasitionListener trasitionListener : trasitionListeners) {trasitionListener.handleLayerView(this, item, i - mFirstVisiPos,maxLayerCount, i, fraction, dx);}}} else {//屬于普通區域//省略。。。。。 if (trasitionListeners != null && !trasitionListeners.isEmpty()) {for (TrasitionListener trasitionListener : trasitionListeners) {if (i - mFirstVisiPos == maxLayerCount) {trasitionListener.handleFocusingView(this, item, i, fraction, dx);} else {trasitionListener.handleNormalView(this, item, i, fraction, dx);}}}}}return dx;}

然后使用者可以自己注冊該接口,天馬行空。

那么我這個項目默認的動畫具體實現是怎么樣的呢?
先這樣,再那樣,效果就出來啦:

@Overridepublic void handleLayerView(FocusLayoutManager focusLayoutManager, View view,int viewLayer, int maxLayerCount, int position,float fraction, float offset) {/*** 期望效果:從0%開始到{@link SimpleTrasitionListener#getLayerChangeRangePercent()} 期間* view均勻完成漸變,之后一直保持不變*///轉換為真實的漸變變化百分比float realFraction;if (fraction <= stl.getLayerChangeRangePercent()) {realFraction = fraction / stl.getLayerChangeRangePercent();} else {realFraction = 1.0f;}float minScale = stl.getLayerViewMinScale(maxLayerCount);float maxScale = stl.getLayerViewMaxScale(maxLayerCount);float scaleDelta = maxScale - minScale; //總縮放差float currentLayerMaxScale =minScale + scaleDelta * (viewLayer + 1) / (maxLayerCount * 1.0f);float currentLayerMinScale = minScale + scaleDelta * viewLayer / (maxLayerCount * 1.0f);float realScale =currentLayerMaxScale - (currentLayerMaxScale - currentLayerMinScale) * realFraction;float minAlpha = stl.getLayerViewMinAlpha(maxLayerCount);float maxAlpha = stl.getLayerViewMaxAlpha(maxLayerCount);float alphaDelta = maxAlpha - minAlpha; //總透明度差float currentLayerMaxAlpha =minAlpha + alphaDelta * (viewLayer + 1) / (maxLayerCount * 1.0f);float currentLayerMinAlpha = minAlpha + alphaDelta * viewLayer / (maxLayerCount * 1.0f);float realAlpha =currentLayerMaxAlpha - (currentLayerMaxAlpha - currentLayerMinAlpha) * realFraction;// log("layer =" + viewLayer + ";alpha = " + realAlpha + ";fraction = " + fraction);view.setScaleX(realScale);view.setScaleY(realScale);view.setAlpha(realAlpha);}

哈哈哈。代碼中stl 存儲著堆疊區域view、焦點view、普通view的最大和最小縮放比、透明度,然后利用fraction計算出當前位置真實的縮放比、透明度設置之。
上面只貼了堆疊區域view的實現,完整實現見源碼中的TrasitionListenerConvert

自動選中

1、滾動停止后自動選中

我的實現方式是這樣的:監聽onScrollStateChanged,在滾動停止時計算出應當停留的position,再計算出停留時的mHorizontalOffset值,播放屬性動畫將當前mHorizontalOffset不斷更新至最終值即可。具體代碼參考源碼中的onScrollStateChanged和smoothScrollToPosition。
(思考:能通過自定義SnapHelper實現么?)

2、點擊非焦點view自動將其選中為焦點view

已經實現了setFocusdPosition方法。內部邏輯就是計算出實際position并調用smoothScrollToPosition或scrollToPosition 。示例代碼:

public ViewHolder(@NonNull final View itemView) {super(itemView);itemView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {int pos = getAdapterPosition();if (pos == focusLayoutManager.getFocusdPosition()) {//是焦點view} else {focusLayoutManager.setFocusdPosition(pos, true);}}});}

無限循環滾動

因為FocusLayoutManager內部沒有遍歷itemCount這種bad操作,你可以自己通過重寫getItemCount返回Integer.MAX_VALUE實現偽無限循環。示例代碼:

public void initView(){recyclerView.post(new Runnable() {@Overridepublic void run() {focusLayoutManager.scrollToPosition(1000); //差不多大行了,畢竟mHorizontalOffset是會一直累加的}});}public class Adapter extends RecyclerView.Adapter<Adapter.ViewHolder> {@Overridepublic void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) {int realPosition = position % datas.size();Bean bean = datas.get(realPosition);//...}@Overridepublic int getItemCount() {return Integer.MAX_VALUE;}}

讓開頭(堆疊數-1)個View可見

按目前布局邏輯,開頭的position = 0 到position = maxLayerCount - 1個view永遠只能在堆疊區域,沒法拉出來到焦點view。解決方式也簡單,給你的源數據開頭插入maxLayerCount - 1個假數據,然后當adapter中識別到假數據時讓其布局不可見即可

結束

剩下的三個堆疊方向的實現就是加加減減的變化,不用貼出來了。

思考:按目前代碼邏輯,onceCompleteScrollLength賦值后是固定的,即“普通區域”的view之間的距離是一樣的,這在所有item寬度(若是垂直滾動則指的是item高度)一樣的情況下沒什么問題。但如果item的寬度是不固定的,那么實際效果就不盡人意了。
那 onceCompleteScrollLength如果動態計算呢?我思考過。有很多難點。比如屏幕第一個可見view的position計算難度大大增加。。以后再說吧(逃)

給個贊唄~
給個star唄~
項目地址:https://github.com/CCY0122/FocusLayoutManager

阿里內推(長期)

可幫阿里內推。將想投的阿里招聘官網職位鏈接+簡歷簡介+備注“csdn#1”(用于讓我知道你是在哪里看到我的)發我郵箱671518768@qq.com。

tips:

1.簡歷絕對真實,背調階段查出誠信問題會被阿里拉黑。

總結

以上是生活随笔為你收集整理的Android仿豆瓣书影音频道推荐表单堆叠列表RecyclerView-LayoutManager的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。