androidstaggered-gridviewstaggeredgridlayoutstaggeredgridlayoutmanager

How to determine viewholder is left or right in RecyclerView with StaggeredGridLayoutManager


I have read a lot of similar articles but still didnt find answer how to know view holder is left or right in RecyclerView with StaggeredGridLayoutManager.

Situation: I have RecyclerView, StaggeredGrid and want to do padding like

8 dp [left view] 8 dp [right view] 8 dp

So since I cannot do it in XML I have to add some margins -

For left view: left margin 8dp, right margin 4dp

For right view: left margin 4dp, right margin 8dp

Usually views are placed like this:

[0][1]
[2][3]
[4][5]

So simpliest solution was to try determine it by position:

override fun onBindViewHolder(ViewHolder holder, int position) {
    ...
        val params = holder.cardView.layoutParams as FrameLayout.LayoutParams

        if (position % 2 == 0) {
            params.leftMargin = pxFromDp(context, 8f).toInt()
            params.rightMargin = pxFromDp(context, 4f).toInt()
        }
        if (position % 2 == 1) {
            params.rightMargin = pxFromDp(context, 8f).toInt()
            params.leftMargin = pxFromDp(context, 4f).toInt()
        }
        params.bottomMargin = pxFromDp(context, 2f).toInt()
        params.topMargin = pxFromDp(context, 6f).toInt()

        holder.cardView.layoutParams = params
    ...
}

And that works, but if view 2 has less height than view 1 they are placed

[0][1]
[3][2]
[5][4]

So it does not work.

How can I know if there are left or right viewholder?


Solution

  • The only one option which really helped is using custom RecyclerView.ItemDecoration()

    class StaggeredGridDecoration(val margin: Int) : RecyclerView.ItemDecoration() {
        override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State?) {
            super.getItemOffsets(outRect, view, parent, state)
            val position = parent.getChildAdapterPosition(view)
            val spanIndex = (view.layoutParams as StaggeredGridLayoutManager.LayoutParams).spanIndex
            val type = parent.adapter.getItemViewType(position)
            val halfOfMargin = margin / 2
            when (type) {
                1 -> {//use here 0 if you didnt create custom types
                    var top = 0
                    val bottom = pxFromDp(parent.context, margin.toFloat()).toInt()
                    //for first 2 elements I need additional margin top
                    if (position < 3) {
                        top = pxFromDp(parent.context, margin.toFloat()).toInt()
                    }
                    if (spanIndex == 0) {
                        //settings for left column
                        val left = pxFromDp(parent.context, margin.toFloat()).toInt()
                        val right = pxFromDp(parent.context, halfOfMargin.toFloat()).toInt()
                        setMargins(view, left, right, top, bottom)
                    } else {
                        //settings for right column
                        val left = pxFromDp(parent.context, halfOfMargin.toFloat()).toInt()
                        val right = pxFromDp(parent.context, margin.toFloat()).toInt()
                        setMargins(view, left, right, top, bottom)
                    }
                }
            }
        }
    
        private fun setMargins(view: View, left: Int, right: Int, top: Int, bottom: Int) {
            val cardView: CardView = view.findViewById(R.id.cardView)
            val params = cardView.layoutParams as FrameLayout.LayoutParams
    
            params.rightMargin = right
            params.leftMargin = left
            params.bottomMargin = bottom
            params.topMargin = top
    
            cardView.layoutParams = params
        }
    
    }
    

    And just added it

    recyclerView.addItemDecoration(new StaggeredGridDecoration(8));