RecyclerView 之自定义LayoutManager实现探探效果

简介

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));

        }


    }

最终效果如下:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值