RecyclerView的使用你們都很熟悉了,這裏偶帶你們來實現下面這種效果。git
對的,你猜的不錯。這種效果只要操做LayoutManager就能夠實現,而後就這樣github
mRecyclerView.setLayoutManager(new EchelonLayoutManager(getContext()));
複製代碼
完了??? 對,就是so easy.緩存
關於如何自定義LayoutManager,網上有不少文章,能夠去找百度君。bash
自定義LayoutManager步驟通常是:ide
開啓擼碼模式函數
必須實現的方法ui
@Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
return new RecyclerView.LayoutParams(RecyclerView.LayoutParams.WRAP_CONTENT,
RecyclerView.LayoutParams.WRAP_CONTENT);
}
複製代碼
第一次加載的時候,從這裏獲取咱們須要的Item的數據,同時也要把咱們的Item排列好,關鍵函數就是layoutChild(recycler),這個後面講,主要策略是從最後一個Item加載。spa
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
if (state.getItemCount() == 0 || state.isPreLayout()) return;
removeAndRecycleAllViews(recycler);
mItemViewWidth = (int) (getHorizontalSpace() * 0.87f);
mItemViewHeight = (int) (mItemViewWidth * 1.46f);
mItemCount = getItemCount();
//獲取到全部Item的高度和
mScrollOffset = Math.min(Math.max(mItemViewHeight, mScrollOffset), mItemCount * mItemViewHeight);
layoutChild(recycler);
}
複製代碼
這裏說說layoutChild(recycler)裏面作了什麼,看註釋吧,親們code
private void layoutChild(RecyclerView.Recycler recycler) {
if (getItemCount() == 0 ) return;
//獲取到最後一個Item的位置
int bottomItemPosition = (int) Math.floor(mScrollOffset / mItemViewHeight);
//獲取到出去一個完整的Item的高度,還剩餘多少空間
int remainSpace = getVerticalSpace() - mItemViewHeight;
//滑動的時候能夠獲取到最後一個Item在屏幕上還顯示的高度
int bottomItemVisibleHeight = mScrollOffset % mItemViewHeight;
//最後一個Item顯示高度相對於自己的比例
final float offsetPercentRelativeToItemView = bottomItemVisibleHeight * 1.0f / mItemViewHeight;
//把咱們須要的Item添加到這個集合
ArrayList<ItemViewInfo> layoutInfos = new ArrayList<>();
for (int i = bottomItemPosition - 1, j = 1; i >= 0; i--, j++) {
//計算偏移量
double maxOffset = (getVerticalSpace() - mItemViewHeight) / 2 * Math.pow(0.8, j);
//這個Item的top值
int start = (int) (remainSpace - offsetPercentRelativeToItemView * maxOffset);
//這個Item須要縮放的比例
float scaleXY = (float) (Math.pow(mScale, j - 1) * (1 - offsetPercentRelativeToItemView * (1 - mScale)));
float positonOffset = offsetPercentRelativeToItemView;
//Item上面的距離佔RecyclerView可用高度的比例
float layoutPercent = start * 1.0f / getVerticalSpace();
ItemViewInfo info = new ItemViewInfo(start, scaleXY, positonOffset, layoutPercent);
layoutInfos.add(0, info);
remainSpace = (int) (remainSpace - maxOffset);
//在添加Item的同時,計算剩餘空間是否能夠容下下一個Item,若是不能的話,就再也不添加了
if (remainSpace <= 0) {
info.setTop((int) (remainSpace + maxOffset));
info.setPositionOffset(0);
info.setLayoutPercent(info.getTop() / getVerticalSpace());
info.setScaleXY((float) Math.pow(mScale, j - 1)); ;
break;
}
}
if (bottomItemPosition < mItemCount) {
final int start = getVerticalSpace() - bottomItemVisibleHeight;
layoutInfos.add(new ItemViewInfo(start, 1.0f, bottomItemVisibleHeight * 1.0f / mItemViewHeight, start * 1.0f / getVerticalSpace())
.setIsBottom());
} else {
bottomItemPosition = bottomItemPosition - 1;//99
}
//這裏作的是回收處理
int layoutCount = layoutInfos.size();
final int startPos = bottomItemPosition - (layoutCount - 1);
final int endPos = bottomItemPosition;
final int childCount = getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
View childView = getChildAt(i);
int pos = getPosition(childView);
if (pos > endPos || pos < startPos) {
removeAndRecycleView(childView, recycler);
}
}
detachAndScrapAttachedViews(recycler);
//這裏主要是對須要顯示的Item進行排列以及縮放
for (int i = 0; i < layoutCount; i++) {
View view = recycler.getViewForPosition(startPos + i);
ItemViewInfo layoutInfo = layoutInfos.get(i);
addView(view);
measureChildWithExactlySize(view);
int left = (getHorizontalSpace() - mItemViewWidth) / 2;
layoutDecoratedWithMargins(view, left, layoutInfo.getTop(), left + mItemViewWidth, layoutInfo.getTop() + mItemViewHeight);
view.setPivotX(view.getWidth() / 2);
view.setPivotY(0);
view.setScaleX(layoutInfo.getScaleXY());
view.setScaleY(layoutInfo.getScaleXY());
}
}
複製代碼
下面就是滑動的處理,若是想要RecyclerView滑動的話,就要打開這個開關。cdn
@Override
public boolean canScrollVertically() {
return true;
}
複製代碼
返回true,你就能夠在豎直方向上滑動,相對的也有橫向的開關,這裏只關注豎向滑動。 滑動的處理在下面這個函數裏
@Override
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
int pendingScrollOffset = mScrollOffset + dy;
mScrollOffset = Math.min(Math.max(mItemViewHeight, mScrollOffset + dy), mItemCount * mItemViewHeight);
//每次滑動都要對Item進行排列等操做
layoutChild(recycler);
return mScrollOffset - pendingScrollOffset + dy;
}
複製代碼
以上就是主要的邏輯,更多詳細的請移步github : https://github.com/DingMouRen/LayoutManagerGroup