简介
RecyclerView通过添加自定义LayoutManager实现类似探探首页的效果。 RecyclerView的宽度设置为屏幕宽度。 即 android:layout_width="match_parent" 。
val linearLayoutManager = SliperLayoutManager()
mBinding.recycleView.layoutManager = linearLayoutManager
val adapter = SliperAdapter()
mBinding.recycleView.adapter = adapter
添加LayoutManager
LayoutManager源码分析:
androidx.recyclerview.widget.RecyclerVie#LayoutManager。先看需要使用LayoutManager里面的涉及的两个方法。
public abstract static class LayoutManager {
//设置RecyclerView的宽和高
public abstract LayoutParams generateDefaultLayoutParams();
//设置RecyclerView的item布局位置,必须自己实现
public void onLayoutChildren(Recycler recycler, State state) {
Log.e(TAG, "You must override onLayoutChildren(Recycler recycler, State state) ");
}
}
通过注释我们知道自定义LayoutManager必须自己实现generateDefaultLayoutParams和onLayoutChildren这两个方法。
自定义LayoutManager
public class SliperLayoutManager extends RecyclerView.LayoutManager {
@Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
detachAndScrapAttachedViews(recycler);
//获取item个数
int count = getItemCount();
//记录最后一个item位置
int bottomPosition ;
//默认展示4个item
if (count<4){
bottomPosition = 0;
}else {
bottomPosition = count- 4;
}
//重新布局item位置
for (int i = bottomPosition; i < count; i++) {
View view = recycler.getViewForPosition(i);
addView(view);
measureChildWithMargins(view,0,0);
int itemWidth = getWidth() - getDecoratedMeasuredWidth(view);
int itemHeight = getHeight() - getDecoratedMeasuredHeight(view);
layoutDecoratedWithMargins(view,itemWidth/2,itemHeight/2,
itemWidth/2+getDecoratedMeasuredWidth(view), itemHeight/2+getDecoratedMeasuredHeight(view) );
}
}
}
先看效果:
此时已经将所有RecyclerView的Item堆叠在一起排放,接下来我们需要将最后四张图片错位排放。
实现思路:由于RecyclerView是堆叠在一起,最上面这个图片是RecyclerView的最后一个Item。需要将RecyclerView的最后四张图错开排放。只需要移动最后四张图在y轴方向的偏移量即可。同时为了突出变化,加上适当缩放效果。
view.setTranslationY((float) (dp2px(view.getContext(), 10)*level));
view.setScaleX((float) (1-level*0.05));
view.setScaleY((float) (1-level*0.05));
实现效果如下:
添加手势滑动
通过自定义LayoutManager实现了图片的布局效果,最后加上手势滑动效果就行了,当图片被滑到屏幕边缘时,图片消失,下一个图片置顶。为了实现手势滑动,需要用到 ItemTouchHelper.SimpleCallback辅助类。
ItemTouchHelper.SimpleCallback源码分析
public abstract static class SimpleCallback extends Callback {
//移动效果
public boolean onMove(@NonNull @NotNull RecyclerView recyclerView, @NonNull @NotNull RecyclerView.ViewHolder viewHolder, @NonNull @NotNull RecyclerView.ViewHolder target) {
return false;
}
//滑动效果
public void onSwiped(@NonNull @NotNull RecyclerView.ViewHolder viewHolder, int direction) {
}
//绘制Child
public void onChildDraw(@NonNull @NotNull Canvas c, @NonNull @NotNull RecyclerView recyclerView, @NonNull @NotNull RecyclerView.ViewHolder viewHolder,
}
}
这里需要实现滑动效果,只需要实现 onSwiped,onChildDraw这两个方法即可。
自定义ItemTouchHelpe.SimpleCallback
public class SliperItemTouchHelperCallBack extends ItemTouchHelper.SimpleCallback {
private SliperAdapter adapter; //适配器
private List<String > mData; //图片链接集合
public SliperItemTouchHelperCallBack(SliperAdapter adapter, List<String > mData) {
//设置滑动方向
super(0, 15);
this.adapter = adapter;
this.mData = mData;
}
@Override
public boolean onMove(@NonNull @NotNull RecyclerView recyclerView, @NonNull @NotNull RecyclerView.ViewHolder viewHolder, @NonNull @NotNull RecyclerView.ViewHolder target) {
return false;
}
@Override
public void onSwiped(@NonNull @NotNull RecyclerView.ViewHolder viewHolder, int direction) {
String s = adapter.getMData().remove(viewHolder.getLayoutPosition());
//将移除的图片重新添加到RecyclerView的头部
adapter.getMData().add(0,s);
//刷新适配器
adapter.notifyDataSetChanged();
}
@Override
public void onChildDraw(@NonNull @NotNull Canvas c, @NonNull @NotNull RecyclerView recyclerView, @NonNull @NotNull RecyclerView.ViewHolder viewHolder,
float dX, float dY, int actionState, boolean isCurrentlyActive) {
super.onChildDraw(c,recyclerView,viewHolder,dX,dY,actionState,isCurrentlyActive);
//计算手势移动图片的偏移量
double dis = Math.sqrt(dX * dX + dY * dY);
int max = recyclerView.getWidth() / 2;
//计算偏移量与屏幕一半的比值系数
double scale = dis / max;
if (scale>1){
scale = 1;
}
//布局子view
int childCount = recyclerView.getChildCount();
for (int i = 0; i <childCount ; i++) {
View view = recyclerView.getChildAt(i);
int level = childCount-i-1;
view.setTranslationY((float) (dp2px(view.getContext(), 10)*(level-scale)));
view.setScaleX((float) (1-level*0.05+scale*0.05));
view.setScaleY((float) (1-level*0.05+scale*0.05));
}
}
最终效果如下: