Get the column number where a view is on a GridLayoutManager
R

1

6

I have different types of items being rendered in a RecyclerView using a GridLayoutManager with dynamic column number. The problem is, I have an RecyclerView.ItemDecoration which is to be applied for let's say Type A items only. this RecyclerView.ItemDecoration adds margin to the left/start to those items on the left hand side column, and margin to the right/end to those items on the right hand side column. It's basically to make the items look a bit more centered and so so stretched (this is used on a tablet/landscape mode). The RecyclerView grid looks something like this:

| A | | A |
| A | | A |
   | B |
| A | | A |
| A | | A |
   | B |
| A | | A |

The ItemDecoration looks like this:

class TabletGridSpaceItemDecoration(private val space: Int) : RecyclerView.ItemDecoration() {

    override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) = with(outRect) {
        val isTypeAItemLayout = view.findViewById<ConstraintLayout>(R.id.type_a_item_container) != null

        if (isTypeAItemLayout) {
            val adapterPosition = parent.getChildAdapterPosition(view)

            if (adapterPosition % 2 == 0) {
                left = space
                right = 0
            } else {
                left = 0
                right = space
            }
        }
    }
}

The problem with this decorator is that after the first type B item on the list, the indexes get screwed for the next type A items. So first item after B according to the example provided will have adapterPosition == 5, so according to TabletGridSpaceItemDecoration the marging should be added to the right, which is incorrect.

  • I tried having a HashMap to keep the adapterPosition and the real position of the item, i.e. the position it would have on the adapter ignoring non type A items. This has some other problems I won't get too much into details, but it's doesn't feel the correct way to do it.

  • Another thing I tried is to check the location on the screen of the view (more to the left or to the right) the item decoration is going to be applied. The problem with this is that the view has not been rendered yet when this decorator is ran. Adding a ViewTreeObserver.OnGlobalLayoutListener on the view is worthless since by the time the view is rendered the item decoration is already applied, which makes no effect on the view.

What I would like to do is to check whether or not an item is in "column 0" or "column 1" and add margin accordingly.

I don't know how is this possible and didn't find the way to do it looking at what the GridLayoutManager provides, that can be accessed by parent.layoutManager as GridLayoutManager.

Any ideas? Thanks

Rosenfeld answered 26/6, 2019 at 14:54 Comment(0)
E
17

I'm sharing this as answer because it is too long for a comment.. Let me know the result and then, I delete if does not work.

Also, sorry to share in Java.. I'm illiterate with Kotlin

Instead of use the position, you can try to use the spanIndex

@Override
public void getItemOffsets(final Rect outRect, final View view, final RecyclerView parent, final State state) {
    ... 
    if(isTypeAItemLayout) {
        int column = ((GridLayoutManager.LayoutParams) view.getLayoutParams()).getSpanIndex();
        if (column == 0) {
            // First Column
        } else {
            // Second Column
        }
    }
}

Upd.

For Kotlin:

val column: Int = (view.layoutParams as GridLayoutManager.LayoutParams).spanIndex
Elizabethelizabethan answered 26/6, 2019 at 15:18 Comment(2)
This was exactly what I wanted to do! Didn't cross my mind to get the span from LayoutParams . Works like a charm, thank you!Lineberry
This answer saved my time. Upvoted. Thank you so much!Strafford

© 2022 - 2024 — McMap. All rights reserved.