RecyclerView实现Gallery效果

概述

最近看到一款常用的app上头图的滑动效果比较不错,于是想自己动手实现下

效果预览

先上图后说话
在这里插入图片描述

实现

这里我使用RecyclerView实现这个效果,其中重点和难点在于实现分页效果滑动动画
使用RecyclerView代码如下(从百度随意找了几张图片作为填充数据,如果涉及版权问题请联系删除):

public class MainActivity extends AppCompatActivity {
    private String[] pics = {"http://gss0.baidu.com/7Po3dSag_xI4khGko9WTAnF6hhy/zhidao/pic/item/bd315c6034a85edfca479e4f48540923dc547596.jpg",
            "http://ww2.sinaimg.cn/large/005ImClmjw1ern9ybe83yj30zk0k01cm.jpg",
            "http://b-ssl.duitang.com/uploads/item/201802/13/20180213202427_vcykg.jpg",
            "http://i0.hdslb.com/bfs/article/3de475241c65c721c242f4f3f854fbd115fc0dd0.jpg",
            "http://b-ssl.duitang.com/uploads/item/201410/25/20141025181818_SSvPY.jpeg"};
    private RecyclerView recyclerView;
    private CustomGalleryAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        recyclerView = findViewById(R.id.recyclerView);
        //设置横滑
        recyclerView.setLayoutManager(new LinearLayoutManager(this, RecyclerView.HORIZONTAL, false));
        //填充数据
        adapter = new CustomGalleryAdapter(getData(), this);
        recyclerView.setAdapter(adapter);
        //分页滑动效果
        recyclerView.setOnFlingListener(null);
        new CustomPagerSnapHelper().attachToRecyclerView(recyclerView);
        //滑动动画
        recyclerView.addOnScrollListener(new GalleryOnScrollListener());
    }

    private List<GalleryItemBean> getData() {
        List<GalleryItemBean> list = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            GalleryItemBean itemBean = new GalleryItemBean();
            itemBean.setImage(pics[i]);
            list.add(itemBean);
        }
        return list;
    }
}

分页滑动效果

我从网上搜了一下,有使用OnFlingListener和OnScrollListener实现该效果的,但是我实现起来总是有缺陷,不知道是不是我使用姿势不对,后来无意间发现了PagerSnapHelper这个类,完美解决了我的问题
看下api文档中对PagerSnapHelper的介绍
在这里插入图片描述
文档中告诉我们PagerSnapHelper可以帮助实现类似ViewPager的效果,这正是我想要的,而且文档里也说明了它的使用方法attachToRecyclerView(RecyclerView),这个方法使用时会让item居中显示,但是我想要的效果是让item靠屏幕左侧显示,通过PagerSnapHelper的源码可以看出
在这里插入图片描述
在这里插入图片描述
distanceToCenter这个方法是用来计算最终显示在屏幕的View(就是item)中心和容器(这里就是RecyclerView)中心之间的距离,calculateDistanceToFinalSnap方法得到的也就是item中心距RecyclerView中心横纵坐标的偏移量,那么就可以重写这个方法改为计算起始位置的偏移量就好了

public class CustomPagerSnapHelper extends PagerSnapHelper {
    @Nullable
    private OrientationHelper mVerticalHelper;
    @Nullable
    private OrientationHelper mHorizontalHelper;

    @Nullable
    @Override
    public int[] calculateDistanceToFinalSnap(@NonNull RecyclerView.LayoutManager layoutManager, @NonNull View targetView) {
        int[] out = new int[2];
        if (layoutManager.canScrollHorizontally()) {
            out[0] = distanceToStart(targetView, getHorizontalHelper(layoutManager));
        } else {
            out[0] = 0;
        }

        if (layoutManager.canScrollVertically()) {
            out[1] = distanceToStart(targetView, getVerticalHelper(layoutManager));
        } else {
            out[1] = 0;
        }
        return out;
    }

    private int distanceToStart(@NonNull View targetView, OrientationHelper helper) {
        //targetView的起始位置
        int childStart = helper.getDecoratedStart(targetView);
        //容器可使用范围的起始位置
        int containerStart = helper.getStartAfterPadding();
        return childStart - containerStart;
    }

    @NonNull
    private OrientationHelper getVerticalHelper(@NonNull RecyclerView.LayoutManager layoutManager) {
        if (mVerticalHelper == null || mVerticalHelper.getLayoutManager() != layoutManager) {
            mVerticalHelper = OrientationHelper.createVerticalHelper(layoutManager);
        }
        return mVerticalHelper;
    }

    @NonNull
    private OrientationHelper getHorizontalHelper(
            @NonNull RecyclerView.LayoutManager layoutManager) {
        if (mHorizontalHelper == null || mHorizontalHelper.getLayoutManager() != layoutManager) {
            mHorizontalHelper = OrientationHelper.createHorizontalHelper(layoutManager);
        }
        return mHorizontalHelper;
    }
}

这里就实现了item滑动时自动对齐RecyclerView左边的效果

滑动动画

这里滑动动画的实现也是个比较棘手的问题,由于动画效果是和滑动有关所以在OnScrollListener中实现动画效果是比较合理的地方,我这里采用了一个比较取巧的方式,因为屏幕中只能同时显示两个item,所以我直接取第二个可见的item进行变化,实现滑入的动画效果

public class GalleryOnScrollListener extends RecyclerView.OnScrollListener {
    @Override
    public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
        super.onScrollStateChanged(recyclerView, newState);
    }

    @Override
    public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        slideInAnimation(recyclerView);
    }

    /**
     * 滑入时的动画
     */
    private void slideInAnimation(RecyclerView recyclerView) {
        float percent = getScrollPercent(recyclerView);
        if (percent > 0 && percent < 1) {
            //屏幕中只能同时显示两个item,所以直接找到第二个可见item就是需要实现动画效果的item
            View targetView = getTargetView(recyclerView, 1);
            if (targetView == null) {
                return;
            }
            //根据滑动的距离改变item的大小
            ViewGroup.MarginLayoutParams lp = ((ViewGroup.MarginLayoutParams) targetView.getLayoutParams());
            lp.width = (int) (DisplayUtils.getItemWidth(targetView.getContext()) * (0.85 + 0.15 * percent));
            lp.height = (int) (DisplayUtils.getItemHeight(targetView.getContext()) * (0.85 + 0.15 * percent));
            targetView.setLayoutParams(lp);
        }
    }

    /**
     * 计算滑动的百分比
     */
    private float getScrollPercent(RecyclerView recyclerView) {
        View firstItem = recyclerView.getChildAt(0);
        if (firstItem == null) {
            return 0;
        }
        RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
        int scrollDistance = firstItem.getWidth() - layoutManager.getDecoratedRight(firstItem);
        return scrollDistance * 1.0f / firstItem.getWidth();
    }

    private View getTargetView(RecyclerView recyclerView, int index) {
        View view = recyclerView.getChildAt(index);
        if (view == null) {
            return null;
        }
        return view.findViewById(R.id.itemContent);
    }
}

到了这里滑动的效果就实现完成了,完整代码见github

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值