20

I am using a RecyclerView to display items horizontally. I want to set the selected item to center of the view like this

enter image description here.

This is how I am doing it:

LinearLayoutManager layoutManager
                = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
        recyclerView.setLayoutManager(layoutManager);
ravi
  • 2,722
  • 7
  • 25
  • 42

5 Answers5

12

To obtain middle item on your screen from RecyclerView you can attach OnScrollListener to RecyclerView and inside listener you should get position of current items then you should check if area of given item is on middle of screen.

Code sample in Kotlin:

// Attach OnScrollListener to your RecyclerView
addOnScrollListener(object : RecyclerView.OnScrollListener() {
    override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
        recyclerView.post {
            selectMiddleItem()
        }
    }
})
// implementation of method that is called from OnScrollListener
private fun selectMiddleItem() {
    val firstVisibleIndex = layoutManager.findFirstVisibleItemPosition()
    val lastVisibleIndex = layoutManager.findLastVisibleItemPosition()
    val visibleIndexes = listOf(firstVisibleIndex..lastVisibleIndex).flatten()

    for (i in visibleIndexes) {
        val vh = findViewHolderForLayoutPosition(i)
        if (vh?.itemView == null) {
            continue
        }
        val location = IntArray(2)
        vh.itemView.getLocationOnScreen(location)
        val x = location[0]
        val halfWidth = vh.itemView.width * .5
        val rightSide = x + halfWidth
        val leftSide = x - halfWidth
        val isInMiddle = screenWidth * .5 in leftSide..rightSide
        if (isInMiddle) {
            // "i" is your middle index and implement selecting it as you want 
            // optionsAdapter.selectItemAtIndex(i)
            return
        }
    }
}

And as result you should get something like this:

enter image description here

Paweł Kanarek
  • 2,317
  • 2
  • 10
  • 17
8

This is for snapping the item in the center when scrolling, or when clicking on an ite.

You need to have a SnapHelper added to the RecyclerView. Here is how:

final RecyclerView recyclerViewObject = view.findViewById(R.id.recyclerViewObjectId);

final LinearSnapHelper snapHelper = new LinearSnapHelper();
snapHelper.attachToRecyclerView(recyclerViewObject);

recyclerViewObject.setOnFlingListener(snapHelper);

then you just call this code

recyclerViewObject.addOnItemTouchListener(
            new RecyclerItemClickListener(getContext(), recyclerViewObject ,new RecyclerItemClickListener.OnItemClickListener() {
                @Override public void onItemClick(View view, int position) {
                    recyclerViewObject.smoothScrollToPosition(position);
                }

                @Override public void onLongItemClick(View view, int position) {
                }
            })
    );
5

You can achieve the same output with minimal lines of code. Where, view is view of selected adapter item and StaticData.SCREEN_WIDTH is device width.

    View view = rvCategory.getChildAt(pos);
    if (view == null)
        return;
    int scrollX = (view.getLeft() - (StaticData.SCREEN_WIDTH / 2)) + (view.getWidth() / 2);
    rvCategory.smoothScrollBy(scrollX, 0);
DKHirani
  • 126
  • 1
  • 7
  • Use layout manager while getting view. Else it returns null. val view: View? = headerRv.layoutManager?.findViewByPosition(pos) – thebadassdev Mar 07 '22 at 10:12
3

Please try sort of this solution:

LinearLayoutManager layoutManager = ((LinearLayoutManager)recyclerView.getLayoutManager());
int totalVisibleItems = layoutManager.findLastVisibleItemPosition() - layoutManager.findFirstVisibleItemPosition()    
int centeredItemPosition = totalVisibleItems / 2;
    recyclerView.smoothScrollToPosition(position);
    recyclerView.setScrollY(centeredItemPosition );

Hope this helps.

Mustansar Saeed
  • 2,730
  • 2
  • 22
  • 46
  • 4
    recyclerview doesnot have getLastVisiblePosition, getFirstVisiblePosition and smoothScrollByOffset is not available in recyclerView. – ravi Jan 14 '16 at 11:37
  • 1
    Thanks Saeed but, getLastVisiblePosition and getFirstVisiblePosition is not available in recyclerview. – ravi Jan 14 '16 at 11:43
  • 2
    @ravi those are `LayoutManager` methods – Kaloyan Roussev Jun 06 '16 at 16:23
  • Side note: If the views are different sizes, then this will not work exactly as expected. It divides by 2 the number of children without taking into account the width of the children. So you'll get the middle child, but you may not get the child in the center of the view. – Jacob Holloway Feb 07 '17 at 18:24
  • I've posted upgraded version of your answer in case of different sizes of cells inside RecyclerView – Paweł Kanarek Oct 06 '18 at 00:51
  • 3
    recyclerView.smoothScrollToPosition(position); which position ? – Prince Dholakiya Jan 03 '19 at 10:00
  • If I read this code well, this will place the top of the view in the middle of the screen, not view (its center) itself. – Ivan Ičin Dec 06 '19 at 23:08
2

Define your custom layout manager as

class CenterLayoutManager : LinearLayoutManager {
    constructor(context: Context) : super(context)
    constructor(context: Context, orientation: Int, reverseLayout: Boolean) : super(context, orientation, reverseLayout)
    constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes)

    override fun smoothScrollToPosition(recyclerView: RecyclerView, state: RecyclerView.State, position: Int) {
        val centerSmoothScroller = CenterSmoothScroller(recyclerView.context)
        centerSmoothScroller.targetPosition = position
        startSmoothScroll(centerSmoothScroller)

    }

    private class CenterSmoothScroller(context: Context) : LinearSmoothScroller(context) {
        override fun calculateDtToFit(viewStart: Int, viewEnd: Int, boxStart: Int, boxEnd: Int, snapPreference: Int): Int = (boxStart + (boxEnd - boxStart) / 2) - (viewStart + (viewEnd - viewStart) / 2)
    }
}

Then assign this layout manager to your recycler view

myRecyclerview.layoutManager =
        CenterLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)

In recyclerview's item onClick method use

myRecyclerview.smoothScrollToPosition(position)

where position should get from onBindViewHolder

Also use LinearSnapHelper as

 val snapHelper = LinearSnapHelper()
    snapHelper.attachToRecyclerView(myRecyclerview)

it will controll scrolling effectively

Also attatch scroll listner to recyclerview to get item at center position

Recyclerview.setOnScrollListener(object:
            RecyclerView.OnScrollListener() {
            override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
                super.onScrolled(recyclerView, dx, dy)
                var view=recyclerView[0]

            }
        })

check out this stackoverflow answer for more details

Ahmad
  • 86
  • 6