实现炫酷列表动画的最佳姿势
点击上方“鸿洋”,关注本号
优质技术文第一时间送达
本文作者
作者:钉某人
链接:
https://www.jianshu.com/p/20f16a4b4630
本文由作者授权发布。
RecyclerView的使用大家都很熟悉了,这里偶带大家来实现下面这种效果。
SkidRightLayoutManager
EchelonLayoutManager
对的,你猜的不错。这种效果只要操作LayoutManager就可以实现,然后就这样
mRecyclerView.setLayoutManager(new EchelonLayoutManager(getContext()));
完了??? 对,就是so easy.
关于如何自定义LayoutManager,网上有很多文章,可以去找百度君。
自定义LayoutManager步骤一般是:
指定默认的LayoutParams,一般返回 return new RecyclerView.LayoutParams(RecyclerView.LayoutParams.WRAP_CONTENT,
RecyclerView.LayoutParams.WRAP_CONTENT); 就可以了测量Item的信息
处理Item回收,使用RecyclerView的二级缓存机制就可以,多方便
滑动处理逻辑
必须实现的方法
@Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
return new RecyclerView.LayoutParams(RecyclerView.LayoutParams.WRAP_CONTENT,
RecyclerView.LayoutParams.WRAP_CONTENT);
}
第一次加载的时候,从这里获取我们需要的Item的数据,同时也要把我们的Item排列好,关键函数就是layoutChild(recycler),这个后面讲,主要策略是从最后一个Item加载。
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)里面做了什么,看注释吧,亲们
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滑动的话,就要打开这个开关。
public boolean canScrollVertically() {
return true;
}
返回true,你就可以在竖直方向上滑动,相对的也有横向的开关,这里只关注竖向滑动。
滑动的处理在下面这个函数里
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
推荐阅读:
扫一扫 关注我的公众号
如果你想要跟大家分享你的文章,欢迎投稿~
┏(^0^)┛明天见!