39

Im a newbie in android. Im not sure why I cant center my item in RecyclerView.

What I want is like below image :-

Actual

What android render is like below image :-

Reality

Is there a way to push items in RecyclerView to center? So it will look like this :-

i want like this

I also provide the layout files as below :-

recycler_item.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content" android:layout_height="wrap_content"
    android:id="@+id/calendar_itemContainer">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="April"
        android:id="@+id/calendar_txtMonth"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="true"
        android:textColor="#ff58636d"
        android:textSize="16sp"
        android:gravity="center|center_vertical|center_horizontal"
        android:paddingTop="8dp"
        android:paddingLeft="12dp"
        android:paddingRight="12dp" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="21"
        android:id="@+id/calendar_txtDay"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_below="@+id/calendar_txtMonth"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="true"
        android:textColor="#ff58636d"
        android:textSize="40sp"
        android:layout_marginTop="-10dp"
        android:paddingLeft="10dp"
        android:paddingRight="10dp"
        android:paddingBottom="5dp"
        android:gravity="center|center_vertical|center_horizontal" />
</RelativeLayout>

fragment_calendar.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/calendar_recycler_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:scrollbars="horizontal"
        android:background="#ff2c3e50" />
</RelativeLayout>

and the java codes :-

CalendarAdapter mAdapter = new CalendarAdapter(mDataset);
mLayoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.HORIZONTAL, false);
mRecyclerView.setLayoutManager(mLayoutManager);
mAdapter.setCalendarCallbacks(this);
mRecyclerView.setAdapter(mAdapter);
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
selectItem(mCurrentSelectedPosition);
skycrew
  • 918
  • 1
  • 15
  • 30

10 Answers10

28

set recyclerview to:

android:layout_width="wrap_content"
android:layout_gravity="center_horizontal"

fixed it for me

Maxi
  • 979
  • 10
  • 16
21

I was trying to do what I think is the same thing you're asking when I came across this post. Here's the solution I ended up using.

Create your own subclass of LinearLayoutManager and override getPaddingLeft() and getPaddingRight() like so.

public class CustomLayoutManager extends LinearLayoutManager {
    private int mParentWidth;
    private int mItemWidth;

    public CustomLayoutManager(Context context, int parentWidth, int itemWidth) {
        super(context);
        mParentWidth = parentWidth;
        mItemWidth = itemWidth;
    }

    @Override
    public int getPaddingLeft() {
        return Math.round(mParentWidth / 2f - mItemWidth / 2f);
    }

    @Override
    public int getPaddingRight() {
        return getPaddingLeft();
    }
}

parentWidth should be the width of the RecyclerView and itemWidth should be the width of each of your child views, both in pixels. Obviously, this only works if you know that all your child views will be the same width.

Then, just use this layout manager with your RecyclerView

terencey
  • 3,282
  • 4
  • 32
  • 40
majormajors
  • 2,351
  • 1
  • 14
  • 7
9

This worked for me:

    LinearSnapHelper snapHelper = new LinearSnapHelper();
    snapHelper.attachToRecyclerView(mRecyclerView);

    //This is used to center first and last item on screen
    mRecyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
        @Override
        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
            int position = parent.getChildViewHolder(view).getAdapterPosition();

            if (position == 0 || position == state.getItemCount() - 1) {

                int elementWidth = (int)getResources().getDimension(R.dimen.list_element_width);
                int elementMargin = (int)getResources().getDimension(R.dimen.list_element_margin);

                int padding = Resources.getSystem().getDisplayMetrics().widthPixels / 2 - elementWidth - elementMargin/2;

                if (position == 0) {
                    outRect.left = padding;

                } else {
                    outRect.right = padding;
                }
            }
        }
    });

Mind elementWidth and elementMargin are fixed values in my dimens.xml file that I use in my xml layout:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="@dimen/list_element_margin"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:layout_margin="@dimen/list_element_width">
    <TextView
        android:id="@+id/day_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</LinearLayout>
Prince Dholakiya
  • 3,255
  • 27
  • 43
manuel
  • 356
  • 2
  • 9
  • 2
    This answer assumes that your recyclerView's width is always `match_parent`. I also don't understand why you're fetching dimensions when you can use those of the given `view`. Could you please elaborate on your thought process with this answer? – Zach Jul 03 '19 at 21:55
  • this will be problematic when new items are added or removed from the list. because it doesn't calculate the offset change for all the items – Shayan_Aryan Jul 11 '19 at 12:57
7

Easy as pie with the new class added to the support library- android.support.v7.widget.LinearSnapHelper

Just create an instance and attach it to the recyclerView as below-

LinearSnapHelper snapHelper  = new LinearSnapHelper();
snapHelper.attachToRecyclerView(recyclerView);
Jishin Dev
  • 371
  • 7
  • 10
4

@majormajors answer was close but will start by centering the first item in the list and not center the items (as a whole) like shown in the images. There needs to be an additional calculation done to check the total item width. I've modified the code posted by @majormajors.

public class CenterItemLayoutManager extends LinearLayoutManager {
  private final int parentWidth;
  private final int itemWidth;
  private final int numItems;

  public CenterItemLayoutManager(
      final Context context,
      final int orientation,
      final int parentWidth,
      final int itemWidth,
      final int numItems) {
    super(context, orientation, false);
    this.parentWidth = parentWidth;
    this.itemWidth = itemWidth;
    this.numItems = numItems;
  }

  @Override
  public int getPaddingLeft() {
    final int totalItemWidth = itemWidth * numItems;
    if (totalItemWidth >= parentWidth) {
      return super.getPaddingLeft(); // do nothing
    } else {
      return Math.round(parentWidth / 2f - totalItemWidth / 2f);
    }
  }

  @Override
  public int getPaddingRight() {
    return getPaddingLeft();
  }
}

In this case the items will be centered only if they do not exceed the recyclerview's width...otherwise it will just act as it normally would.

IZI_Shadow_IZI
  • 1,921
  • 4
  • 30
  • 59
1

Just added android:orientation="vertical" to root of item view. Let's try it.

<?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:layout_width="match_parent"
                  android:layout_height="180dp"
                  android:orientation="vertical"
                  android:padding="4dp">

        <ImageView
            android:id="@+id/movie_column_photo"
            android:layout_width="80dp"
            android:layout_height="120dp"
            android:layout_gravity="center_horizontal"/>

        <TextView
            android:id="@+id/movie_column_title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"/>
    </LinearLayout>
sonida
  • 4,411
  • 1
  • 38
  • 40
1

The best way to make the selected item to the center of the screen is to apply

paddingHorizontal to recyclerView. Give paddingHorizontal value which makes your item to center then add clipToPadding = false.

After that when you select recyclerView item then use recyclerView.smoothScrollToPosition(pos). It will scroll your item to that position

Now Your selected item visible at the center of the screen!

Krishna Sony
  • 1,286
  • 13
  • 27
1
  • In this situation, firstly we need to know the recyclerview items size. In addition we need to know how many visible items can place in recyclerview width. Then we can calculate how much padding we need.

      public class CenterLinearLayoutManager extends LinearLayoutManager {
      private int mParentWidth;
      private int mItemWidth;
      private boolean needMargin = false;
      private int mItemSize = 0;
    
      public CenterLinearLayoutManager(Context context, int parentWidth, int itemWidth, int itemSize) {
          super(context);
          mParentWidth = parentWidth;
          mItemWidth = itemWidth;
          mItemSize = itemSize;
    
          this.setOrientation(RecyclerView.HORIZONTAL);
    
          int myCount = (mParentWidth / mItemWidth);
    
          if (itemSize < myCount) {
              needMargin = true;
          }
      }
    
      @Override
      public int getPaddingLeft() {
          if (needMargin) {
              return Math.round((mParentWidth - mItemWidth * mItemSize) / 2f);
          } else {
              return super.getPaddingLeft();
          }
      }
    
      @Override
      public int getPaddingRight() {
          if (needMargin) {
              return Math.round((mParentWidth - mItemWidth * mItemSize) / 2f);
          } else {
              return super.getPaddingLeft();
          }
      }
    

    }

  • don't forget to calculate item padding in item width

Javad Shirkhani
  • 343
  • 6
  • 11
0

Add below decorator to the recycler view;

class CenterAlignDecorator : RecyclerView.ItemDecoration() {
    override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
        parent.adapter?.let {
            if (parent.getChildAdapterPosition(view) == 0 && view.width > 0) {
                val itemSize = view.width + view.paddingStart + view.paddingEnd
                val itemLimit = (parent.width / itemSize) + 1
                if (state.itemCount <= itemLimit) {
                    val padding = (parent.width - (it.itemCount * itemSize)) / 2
                    outRect.set(padding, 0, 0, 0)
                }
            } else {
                outRect.set(0, 0, 0, 0)
            }
        }
    }
}
Zahid Rasheed
  • 1,536
  • 1
  • 10
  • 28
0

You need to set parent Relativelayout Gravity to centre_horizontal. And Also set layout_gravity of Recylerview center_horizontal under RelativeLayout. Like this:

<RelativeLayout
   android:layout_width="match_parent"
   android:gravity="center_horizontal"
   android:id="@+id/centre_lay"
   android:layout_height="wrap_content">
      <androidx.recyclerview.widget.RecyclerView
         android:id="@+id/list"
         android:background="@color/white"
         android:layout_width="wrap_content"
         android:layout_gravity="center_horizontal"
         android:layout_height="wrap_content">

    </androidx.recyclerview.widget.RecyclerView>
</RelativeLayout>