RecycleView自定義LayoutManager實現Gallery效果

image
老規矩,先上圖:

image
這個效果其實就是在上篇的HorizontalLayoutmanager的基礎上進行修改
1、可以看到條目的起始位置是從螢幕的一半的地方再減去item寬度的一半的位置開始,而且每個item都是疊在上一個item寬度一般的位置
mStartX = getWidth()/2 - getItemShowWidth(); for (int i = 0; i < getItemCount(); i++) {Rect rect = new Rect(mStartX + temp, 0, mStartX + temp + mItemWidth, itemHeight);mSparseArray.put(i, rect);mBooleanArray.put(i, false);temp += getItemShowWidth(); } private int getItemShowWidth() {return mItemWidth / 2;}

image
2、我們可以看到所有的item都重疊起來了,畫廊的效果是中間的item完全顯示,而其它地方都只顯示了一半,這裡我們要重寫Recycleview中的方法,來改變item繪製的順序,我們想要畫廊的效果是中間顯示其它顯示一半,所以我們中間的item最後來繪製,這裡介紹下Recycleview中的這個方法
@Overrideprotected int getChildDrawingOrder(int childCount, int i) {if (mChildDrawingOrderCallback == null) {return super.getChildDrawingOrder(childCount, i);} else {return mChildDrawingOrderCallback.onGetChildDrawingOrder(childCount, i);}}
childCount表示螢幕中可見的item個數,i表示要繪製的條目位置,i越小越先繪製,如下所示:

image
前三個條目的位置和索引一樣不用變,中間的為childCount-1,倒數第二個的繪圖順序是center,倒數第二個的繪圖順序是center+1,倒數第三個的繪圖順序是center+2,所以後三個對應的為center + childCount- 1 - i
public class GalleryRecycleview extends RecyclerView {public GalleryRecycleview(Context context) {this(context,null);}public GalleryRecycleview(Context context, @Nullable AttributeSet attrs) {super(context, attrs);setChildrenDrawingOrderEnabled(true);}@Overrideprotected int getChildDrawingOrder(int childCount, int i) {int center = getlayoutManager().getCenterPos()- getlayoutManager().getFistvisiblePos(); //計算正在顯示的所有Item的中間位置int order;if (i == center) {order = childCount - 1;} else if (i > center) {order = center + childCount - 1 - i;} else {order = i;}return order;}
在layoutmanager中獲取第一個可見item和中間item的位置
public int getCenterPos() {int pos = (mTotalMoveX / getItemShowWidth());int more = (mTotalMoveX % getItemShowWidth());if (more > getItemShowWidth() * 0.5f) pos++;return pos;}public int getFistvisiblePos() {if(getItemCount()==0){return 0;}return getPosition(getChildAt(0));}

image
可以看到已經是最後才繪製中間的item了
3、接著我們在layoutChild的時候加上動畫
private void handleChildView(View child, int moveX) {float radio = computeScale(moveX);float rotation = computeRotationY(moveX);child.setScaleX(radio);child.setScaleY(radio);child.setRotationY(rotation);}private float computeScale(int x) {float scale = 1 - Math.abs(x * 1.0f / (6f * getItemShowWidth()));if (scale < 0) scale = 0;if (scale > 1) scale = 1;return scale;}/*** 最大Y軸旋轉度數*/private float M_MAX_ROTATION_Y = 30.0f;private float computeRotationY(int x) {float rotationY;rotationY = -M_MAX_ROTATION_Y * x / getItemShowWidth();if (Math.abs(rotationY) > M_MAX_ROTATION_Y) {if (rotationY > 0) {rotationY = M_MAX_ROTATION_Y;} else {rotationY = -M_MAX_ROTATION_Y;}}return rotationY;}
就和最上面的效果一樣了
【附】相關架構及資料

image
資料領取:
關注+點贊+加群:185873940 免費獲取!
領取獲取往期Android高階架構資料、原始碼、筆記、視訊。高階UI、效能優化、架構師課程、NDK、混合式開發(ReactNative+Weex)微信小程式、Flutter全方面的Android進階實踐技術