1

I am new to android and hence RV and I am trying to achieve the layout where the first and last card are not centered and instead show more of the cards after and before them. Maybe at In this case I can see 16dp for the second cards and same thing for the penultimate card which makes the first and last card not centered. But 8dp each for the rest of the cards so the intermediate cards appear centered. Maybe using itemDecoration somehow for the 2nd and the penultimate card somehow.

enter image description here

I was able to achieve showing parts of next and prev cards by following what is suggested here, but that only centers all the cards uniformly : How to show part of next/previous card RecyclerView

I tried overriding getItemOffsets but it gets triggered everytime I scroll to the first or the last card and moves the 2nd and 2nd to last card incorrectly and also doesn't center them correctly when I scroll to them.

  public static class MyItemDecoration extends RecyclerView.ItemDecoration {

    @Override
    public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
      super.getItemOffsets(outRect, view, parent, state);

      final int itemPosition = parent.getChildAdapterPosition(view);
      if (itemPosition == RecyclerView.NO_POSITION) {
        return;
      }

      final int itemCount = state.getItemCount();
      if (itemCount > 0 && itemPosition == 1) {
        outRect.left -= 16;
        outRect.right -= 16;
      }

      else if (itemCount > 0 && itemPosition == itemCount - 1) {
        outRect.left += 16;
        outRect.right += 16;
      }
    }
  }

RV Setup

 SnapHelper snapHelper = new PagerSnapHelper();
        RecyclerView rv = getBinding().rv;
        rv.setOnFlingListener(null);
        snapHelper.attachToRecyclerView(rv);
RamPrasadBismil
  • 579
  • 2
  • 10
  • 30

1 Answers1

5

PagerSnapHelper centers the RecyclerView items including the decorations, so, unless the decoration widths are balanced, they won't always be centered. This may be what you are seeing.

Try the following for the decoration. This code applies the full-width decoration to the start of the first item and the end of the last item; otherwise, a half decoration width is used. By setting up the decorations this way, you are centering items that have balanced left and right decorations.

DividerItemDecoration decoration =
        new DividerItemDecoration(getApplicationContext(), HORIZONTAL) {
            private int mDecorationWidth = (int) (getResources().getDisplayMetrics().density * 8);

            @Override
            public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
                                       RecyclerView.State state) {
                final int pos = parent.getChildAdapterPosition(view);
                if (pos == RecyclerView.NO_POSITION) {
                    return;
                }
                if (pos == 0) {
                    outRect.set(mDecorationWidth, 0, mDecorationWidth / 2, 0);
                } else if (pos == parent.getAdapter().getItemCount() - 1) {
                    outRect.set(mDecorationWidth / 2, 0, mDecorationWidth, 0);
                } else {
                    outRect.set(mDecorationWidth / 2, 0, mDecorationWidth / 2, 0);
                }
            }
        };

Here is a video showing the results with gray vertical dividers.

enter image description here

If you already have the decorations working to your satisfaction, you can override calculateDistanceToFinalSnap() in PagerSnapHelper to center all views except the first and last view as follows. See calculatedistancetofinalsnap(). Once the PageSnapHelper identifies a target view to snap to, calculatedistancetofinalsnap() is called to determine how many pixels to move to perform the snap. Here, we are moving just enough pixels to center the view (without decorations) in the RecyclerView. PageSnapHelper does the right thing for the first and last items, so we just call the super for these.

PagerSnapHelper pagerSnapHelper = new PagerSnapHelper() {  

    @Override  
  public int[] calculateDistanceToFinalSnap(@NonNull RecyclerView.LayoutManager layoutManager,  
                                              @NonNull View targetView) {  
        LinearLayoutManager lm = (LinearLayoutManager) layoutManager;  
        int pos = mRecycler.getChildAdapterPosition(targetView);  
        // If first or last view, the default implementation works.  
  if (pos == 0 || pos == lm.getItemCount() - 1) {  
            return super.calculateDistanceToFinalSnap(layoutManager, targetView);  
        }  
        // Force centering in the view without its decorations. 
        // targetCenter is the location of the center of the view we want to center. 
        int targetCenter = targetView.getLeft() + targetView.getWidth() / 2;  
        // Distance is the number of pixels to move the target so that its center
        // lines up with the center of the RecyclerView (mRecycler.getWidth() / 2)       
        int distance = targetCenter - mRecycler.getWidth() / 2;  
        return new int[]{distance, 0};  
    }  
};

Either way will work.

Cheticamp
  • 61,413
  • 10
  • 78
  • 131
  • I am not actually looking for balanced left and right decorations using dividers. I am actually looking for how to have variable margins for the items based on position. But I see the idea and let me see if this works. – RamPrasadBismil May 29 '19 at 17:37
  • @RamPrasadBismil Take a look at the video I posted in the answer. If it isn't what you describe, what is different? – Cheticamp May 30 '19 at 02:49
  • Can you explain your solution a little more. I feel like this is changing the dividers width for the different position but that needs to be constant, it is the 2nd card that needs to be moved more to the left. Coz with this current solution I am not able to see as much of the 2nd card as i want to still. – RamPrasadBismil May 30 '19 at 19:46
  • @RamPrasadBismil Sure. The first card lines up with the left side of the _RecyclerView_ (actually its left-side decoration lines up). The second view abuts the right decoration of the first view. That fills the width of the _RecyclerView_. If the second view is to slide to the left so it shows more what will give up space? (We can also get rid of the 1/2 width decorations and replace them with full-width ones with a little different logic.) – Cheticamp May 30 '19 at 20:37
  • So I updated the image and lets say its a little trickier. So the padding before first card is 16dp and same after the last card. Margin between cards is constant 12dp. Now the 2nd card should show 32dp and same for the 2nd to last card and rest of the cards all show 16dp and the card in between is centered. Now for such case we would need to move the 2nd and 2nd to last card left and right respectively and we would need to make sure that on scroll they appear centered. @Cheticamp – RamPrasadBismil May 30 '19 at 21:16
  • if anything the card is a fixed width and for the first and last we have 16padding + 12dp margin + 32dp peeking = 60dp vs for the middle card 16 peeking on left + 12 margin on left + 12 margin on right + 16 peeking on right = 56 dp – RamPrasadBismil May 30 '19 at 21:30
  • I see what you are saying but it doesn't add up. If all the views are the same width (not including the decoration), then the _RecyclerVIew_ (if that's the brown part) in the first image is 16dp + x + 12dp + 32dp = x + 60dp wide where 'x' is the width of the view and 16dp + 12dp + x + 12dp + 16dp = x + 56dp wide in the second image. There is a 4dp difference. Should the 32dp part be 28dp in the first image? If that is the case, we will have the solution. @RamPrasadBismil – Cheticamp May 30 '19 at 23:03
  • Ya 28 Makes sense – RamPrasadBismil May 31 '19 at 07:17
  • Check out update. Sample app is in this [gist](https://gist.github.com/Cheticamp/c9b8d7115f83a14a4b453db634daf0ce). @RamPrasadBismil – Cheticamp May 31 '19 at 16:48
  • Can you example your solution a little the calculateDistanceToFinalSnap part – RamPrasadBismil May 31 '19 at 20:49
  • @RamPrasadBismil Added some additional explanation for the calculateDistanceToFinalSnap part. Also, check out the gist I mentioned in a previous comment. – Cheticamp May 31 '19 at 20:59
  • Do you by any chance know how I can set max width on the card? – RamPrasadBismil Jun 06 '19 at 21:51