97

用RecyclerView打造一个轮播图(进阶版)

 6 years ago
source link: https://juejin.im/post/5a13a28c51882512a860ee6a
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

用RecyclerView打造一个轮播图(进阶版) - 掘金

用RecyclerView打造一个轮播图(进阶版)

2017年11月22日 10:02 ·  阅读 27819
用RecyclerView打造一个轮播图(进阶版)

前几天写了篇《用RecyclerView打造一个轮播图》(以下简称基础版),看到有读者评论说相比Viewpager,用RecyclerView看起来没什么特别的优势。究其原因,目前只用到了RecyclerView最基础的一部分功能。其实相比Viewpager实现的轮播图,RecyclerView版的最大优势就在于它的灵活多变性,可定制性高。本篇文章将通过利用LayoutManger、SnapHelper等RecyclerView的辅助类来实现一系列更为炫酷的轮播图。

初试:竖直轮播图

基础版中,RecyclerView设置了默认横向的LinearLayoutManager: LinearLayoutManager indicatorLayoutManager = new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false); 那么我们再来加个属性:

 <attr name="orientation" format="enum">
            <enum name="horizontal" value="0"/>
            <enum name="vertical" value="1" />
 </attr>
复制代码

同时在布局文件中设置app:orientation="vertical",让LinearLayoutManager的布局方向变为竖直(为了保持统一,标示点布局方向跟随滑动方向),就是这么简单,一款竖直滑动的无限轮播图就打造完成了!

15fdcdf5db932200~tplv-t2oaga2asx-zoom-in-crop-mark:3024:0:0:0.awebp

实战:仿魅族轮播图

之前有篇文章ViewPager系列之 仿魅族应用的广告BannerView,不过这是用ViewPager实现的,那我们就来个RecyclerView版的,而实现重点的就在于自定义LayoutManger(如果不太了解这部分的知识,请先移步学习下(╯︵╰))。 这次先上成果图,再慢慢分析:

15fe6b7aa2e25572~tplv-t2oaga2asx-zoom-in-crop-mark:3024:0:0:0.awebp

以上的效果仅仅是换了一个LayoutManger和一个itemview(为了显示效果,imageview外面套了cardview)。 首先我们做准备工作,定义几个常量:

private  float SCALE_RATE = 1.2f;当前图片放大比例
private  int mOrientation = HORIZONTAL;布局方向(HORIZONTAL or VERTICAL)
private  int itemSpace = 0;图片之间的间距

复制代码

自定layoutmanager第一步当然是实现唯一必须要实现的方法:

  @Override
    public RecyclerView.LayoutParams generateDefaultLayoutParams() {
        return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
    }
复制代码

然而并没有什么用,99%的自定义LayoutManager都是这么写的,因为我们没有把view添加到 RecyclerView中。所以接下来就是重写onLayoutChildren()来进行布局,这个方法的作用相当于普通Viewgroup中的onLayout()方法,在RecyclerView滚动,数据改变等情况都会调用此方法来重新布局。

 @Override
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
        if (state.getItemCount() == 0) {
            removeAndRecycleAllViews(recycler);
            mOffset = 0;
            return;
        }//没有Itemitemview,不做处理

        ensureLayoutState();

        if (getChildCount() == 0) {//没有可见的itemview,初始化需要用到的一些参数
            View scrap = recycler.getViewForPosition(0);
            measureChildWithMargins(scrap, 0, 0);
            mDecoratedMeasurement = mOrientationHelper.getDecoratedMeasurement(scrap);
            mDecoratedMeasurementInOther = mOrientationHelper.getDecoratedMeasurementInOther(scrap);
            mSpaceMain = (mOrientationHelper.getTotalSpace() - mDecoratedMeasurement) / 2;
            mSpaceInOther = (mOrientationHelper.getTotalSpaceInOther() - mDecoratedMeasurementInOther) / 2;
            mInterval = setInterval();
            setUp();
            mLeftItems = (int) Math.abs(minRemoveOffset() / mInterval) + 1;
            mRightItems = (int) Math.abs(maxRemoveOffset() / mInterval) + 1;
        }

        if (mPendingScrollPosition != NO_POSITION) {
            mOffset = mReverseLayout ?
                    mPendingScrollPosition * -mInterval : mPendingScrollPosition * mInterval;
        }
        //开始布局
        detachAndScrapAttachedViews(recycler);
        layoutItems(recycler);
    }
    ```
    
  

上面只是做一些初始化工作,接下来是`layoutItems`方法,就贴一些重要代码:
```java
取当前可见的view进行放置,遍历计算位置
final int currentPos = mReverseLayout ?
                -getCurrentPositionOffset() : getCurrentPositionOffset();
        int start = currentPos - mLeftItems;
        int end = currentPos + mRightItems;
for (int i = start; i < end; i++) {
    if (findViewByPosition(adapterPosition) == null) {
         final View scrap = recycler.getViewForPosition(adapterPosition);
         measureChildWithMargins(scrap, 0, 0);
         resetViewProperty(scrap);
        final float targetOffset = getProperty(i) - mOffset;
        layoutScrap(scrap, targetOffset);

复制代码

具体的布局方法,主要就是:回收不可见的itemview,遍历可见的itemview进行位置计算并放置:

private void layoutScrap(View scrap, float targetOffset) {
       final int left = calItemLeft(scrap, targetOffset);
       final int top = calItemTop(scrap, targetOffset);
       if (mOrientation == VERTICAL) {
           layoutDecorated(scrap, mSpaceInOther + left, mSpaceMain + top,
                   mSpaceInOther + left + mDecoratedMeasurementInOther, mSpaceMain + top + mDecoratedMeasurement);
       } else {
           layoutDecorated(scrap, mSpaceMain + left, mSpaceInOther + top,
                   mSpaceMain + left + mDecoratedMeasurement, mSpaceInOther + top + mDecoratedMeasurementInOther);
       }
       setItemViewProperty(scrap, targetOffset);
   }
   //在滚动时根据距离动态缩放itemView(在这里你可以自定义滑动动画,改变 itemView的属性,透明度,大小,角度等等)
    private void setItemViewProperty(View itemView, float targetOffset) {
       float scale = calculateScale(targetOffset + mSpaceMain);
       itemView.setScaleX(scale);
       itemView.setScaleY(scale);
   }
复制代码

接下来是处理滚动,让recyclerview可以滚动起来:

//设置允许横向或竖向滚动
  @Override
    public boolean canScrollHorizontally() {
        return mOrientation == HORIZONTAL;
    }

    @Override
    public boolean canScrollVertically() {
        return mOrientation == VERTICAL;
    }
复制代码
//处理横向或竖向滚动
 @Override
    public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
        //位移0、没有子View 当然不移动
       if (getChildCount() == 0 || dy == 0) {
            return 0;
        }

          int willScroll = dy;
          float realDx = dy / getDistanceRatio();//真正滑动的距离
       // 重新布局可见的view
        for (int i = 0; i < getChildCount(); i++) {
            final View scrap = getChildAt(i);
            final float delta = propertyChangeWhenScroll(scrap) - realDx;
            layoutScrap(scrap, delta);
        }
    }
复制代码

因为在我们在布局和滚动时考虑了横向和竖向的情况,所以设置竖直的无限轮播图也很简单: new BannerLayoutManager(BannerLayoutManager.VERTICAL, Util.dp2px(10)); (方向竖直,图片间距10dp,默认放大1.2倍)

看到这,我想大家都能看到用RecyclerView实现无限轮播图的强大的之处了吧: adapter可以处理itemview布局和无限轮播;LayoutManager可以处理整体布局和滑动动画;SnapHelper可以让itemview滑动起来像viewpager一样(一般用自带的PagerSnapHelper就行了)。 而上面的所有动画效果仅仅都是通过改动LayoutManager,然后再通过设置不同itemview,就可以做出各种不同效果。最后感谢以下文章提供LayoutManager的实现思路。

github地址

blog.csdn.net/zxt0601/art… www.jianshu.com/p/7bb7556bb…


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK