RecyclerView实现Gallery画廊效果

使用RecyclerView实现一个画廊效果,主要是使用support库中最新加入的PagerSnapHelper类,通过计算滑动偏移来计算scale的值。

585087-20170923123107821-1219561243.gif

基本实现

首先需要为RecyclerView添加一个滚动监听,然后为RecyclerView的第一个与最后一个itemView添加一个ItemDecoration,使位于第一个与最后一个itemView的位置居中对齐。

 
  public void attachToRecyclerView(final RecyclerView recyclerView) {
        this.recyclerView = recyclerView;
        snapHelper.attachToRecyclerView(recyclerView);
        recyclerView.addOnScrollListener(scrollListener);
        recyclerView.addItemDecoration(new ScalableCardItemDecoration());
        recyclerView.post(new Runnable() {
            @Override
            public void run() {
                pageScrolled();
            }
        });
    }

ScalableCardItemDecoration用于计算itemView左右剩余空间,然后为第一个itemView与最后一个itemView添加偏移量。


 private static class ScalableCardItemDecoration extends RecyclerView.ItemDecoration {


        @Override
        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
            RecyclerView.ViewHolder holder = parent.getChildViewHolder(view);
            int position = holder.getAdapterPosition() == RecyclerView.NO_POSITION ? holder.getOldPosition() : holder.getAdapterPosition();
            RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
            int itemCount = layoutManager.getItemCount();

            if(position != 0 && position != itemCount - 1){
                return;
            }

            int peekWidth = getPeekWidth(parent, view);
            boolean isVertical = layoutManager.canScrollVertically();
            //移除item时adapter position为-1。

            if (isVertical) {
                if (position == 0) {
                    outRect.set(0, peekWidth, 0, 0);
                } else if (position == itemCount - 1) {
                    outRect.set(0, 0, 0, peekWidth);
                } else {
                    outRect.set(0, 0, 0, 0);
                }
            } else {
                if (position == 0) {
                    outRect.set(peekWidth, 0, 0, 0);
                } else if (position == itemCount - 1) {
                    outRect.set(0, 0, peekWidth, 0);
                } else {
                    outRect.set(0, 0, 0, 0);
                }
            }
        }
    }

在为item添加ItemDecoration时,需要计算两边的空间。这里需要手动测量itemView的宽高, 然后计算第一个itemView左边的偏移与最后一个itemView右边的偏移。这里碰到个问题,如果LayoutMangaer的方面是垂直或水平的且RecyclerView对应的的高度或宽度设为wrap_content时,这里计算的值不会正确。

 public static int getPeekWidth(RecyclerView recyclerView, View itemView) {
        RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
        boolean isVertical = layoutManager.canScrollVertically();
        int position = recyclerView.getChildAdapterPosition(itemView);
        //TODO RecyclerView使用wrap_content时,获取的宽度可能会是0。
        int parentWidth = recyclerView.getMeasuredWidth();
        int parentHeight = recyclerView.getMeasuredHeight(); //有时会拿到0
        parentWidth = parentWidth == 0 ? recyclerView.getWidth() : parentWidth;
        parentHeight = parentHeight == 0 ? recyclerView.getHeight() : parentHeight;
        int parentEnd = isVertical ? parentHeight : parentWidth;
        int parentCenter = parentEnd / 2;

        int itemSize = isVertical ? itemView.getMeasuredHeight() : itemView.getMeasuredWidth();

        if (itemSize == 0) {

            ViewGroup.LayoutParams layoutParams = itemView.getLayoutParams();
            int widthMeasureSpec =
                    RecyclerView.LayoutManager.getChildMeasureSpec(parentWidth,
                            layoutManager.getWidthMode(),
                            recyclerView.getPaddingLeft() + recyclerView.getPaddingRight(),
                            layoutParams.width, layoutManager.canScrollHorizontally());

            int heightMeasureSpec =
                    RecyclerView.LayoutManager.getChildMeasureSpec(parentHeight,
                            layoutManager.getHeightMode(),
                            recyclerView.getPaddingTop() + recyclerView.getPaddingBottom(),
                            layoutParams.height, layoutManager.canScrollVertically());


            itemView.measure(widthMeasureSpec, heightMeasureSpec);
            itemSize = isVertical ? itemView.getMeasuredHeight() : itemView.getMeasuredWidth();
        }


        /*
            计算ItemDecoration的大小,确保插入的大小正好使view的start + itemSize / 2等于parentCenter。
         */
        int startOffset = parentCenter - itemSize / 2;
        int endOffset = parentEnd - (startOffset + itemSize);

        return position == 0 ? startOffset : endOffset;
    }

添加完ItemDecoration后,我们需要在RecyclerView每次滚动的时候计算左、中、右3个itemView的缩放比例。


 private void pageScrolled() {
        if (recyclerView == null || recyclerView.getChildCount() == 0)
            return;

        RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();

        View snapingView = snapHelper.findSnapView(layoutManager);
        int snapingViewPosition = recyclerView.getChildAdapterPosition(snapingView);
        View leftSnapingView = layoutManager.findViewByPosition(snapingViewPosition - 1);
        View rightSnapingView = layoutManager.findViewByPosition(snapingViewPosition + 1);


        float leftSnapingOffset = calculateOffset(recyclerView, leftSnapingView);
        float rightSnapingOffset = calculateOffset(recyclerView, rightSnapingView);
        float currentSnapingOffset = calculateOffset(recyclerView, snapingView);

        if (snapingView != null) {
            snapingView.setScaleX(currentSnapingOffset);
            snapingView.setScaleY(currentSnapingOffset);
        }

        if (leftSnapingView != null) {
            leftSnapingView.setScaleX(leftSnapingOffset);
            leftSnapingView.setScaleY(leftSnapingOffset);
        }

        if (rightSnapingView != null) {
            rightSnapingView.setScaleX(rightSnapingOffset);
            rightSnapingView.setScaleY(rightSnapingOffset);
        }


        if(snapingView != null && currentSnapingOffset >= 1){
            OnPageChangeListener listener = pageChangeListenerRef != null ? pageChangeListenerRef.get(): null;
            if(listener != null)
                listener.onPageSelected(snapingViewPosition);
        }

        Log.d(TAG, String.format("left: %f, right: %f, current: %f", leftSnapingOffset, rightSnapingOffset, currentSnapingOffset));
    }
   

计算缩放比例时是根据左、中、右三个itemView的中间点与RecyclerView中间点的距离来计算的。


 private float calculateOffset(RecyclerView recyclerView, View view) {
        if (view == null)
            return -1;


        RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
        boolean isVertical = layoutManager.canScrollVertically();
        int viewStart = isVertical ? view.getTop() : view.getLeft();
        int viewEnd = isVertical ? view.getBottom() : view.getRight();
        int centerX = isVertical ? recyclerView.getHeight() / 2 : recyclerView.getWidth() / 2;
        int childCenter = (viewStart + viewEnd) / 2;
        int distance =   Math.abs(childCenter - centerX);

        if (distance > centerX)
            return STAY_SCALE;

        float offset = 1.f - (distance / (float) centerX);
        return (1.f - STAY_SCALE) * offset + STAY_SCALE;
    }

项目地址: https://github.com/yjwfn/recyclerview-gallery

转载于:https://www.cnblogs.com/xwgblog/p/7580812.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值