Adam
Adam

Reputation: 1343

Android FlexboxLayoutManager recreating items

I am using FlexboxLayoutManager to display Recycler View items with dynamic width. The problem I'm facing is when the background of the item is changed it reanimates/redraw all the next items which are not very attractive/required.

Screenshot of my Screen

The following screenshot shows the use case. Whenever there are two/more items with different width in a single row, selecting/unselecting views is recreating the next neighbors.

My code for the this:

 val managerVal = FlexboxLayoutManager(context, FlexDirection.ROW)
// I have used these properties as well with other combinations showing here for reference
//            managerVal.alignItems = AlignItems.FLEX_START
//            managerVal.justifyContent = JustifyContent.CENTER
            itemView.rvFilterOptions.layoutManager = managerVal
val filterOptionAdapter = FilterOptionAdapter(
                context,
                record.values
            )
itemView.rvFilterOptions.adapter = filterOptionAdapter

I have also tried changing values in Adapter

val lp = itemView.llFilterValue.getLayoutParams()
            if (lp is FlexboxLayoutManager.LayoutParams) {
               lp.flexGrow = 1.0f
               flexboxLp.flexShrink = 1f
               lp.alignSelf = AlignItems.FLEX_START

            }

The code to change the background of the item in the adapter.

if (record.isSelected) {
                itemView.tvFilterValue.setTextColor(
                    AppCompatResources.getColorStateList(
                        context,
                        R.color.textWhite
                    )
                )
                itemView.ivFilterCheck.show()
                itemView.llFilterValue.background =
                    AppCompatResources.getDrawable(
                        context,
                        R.drawable.bg_dark_rectangle_circle_edge
                    )
            } else {
                itemView.tvFilterValue.setTextColor(
                    AppCompatResources.getColorStateList(
                        context,
                        R.color.textNormal
                    )
                )
                itemView.ivFilterCheck.invisible()
                itemView.llFilterValue.background =
                    AppCompatResources.getDrawable(
                        context,
                        R.drawable.bg_gray_rectangle_circle_edge
                    )
            }

A gif to show the behavior:

enter image description here

Thank you for your time.

Upvotes: 2

Views: 5124

Answers (3)

Krishna Kumar
Krishna Kumar

Reputation: 1

I was facing a similar problem. Fixed it by adding setHasFixedSize(true) to my recycler view.

Upvotes: 0

Chrisser000
Chrisser000

Reputation: 93

I was having the same problem, and I think it's because I was setting a variable in the xml layout file via DataBinding in my onBindViewHolder function, causing the TextView in my layout file to be recreated. This seems to cause the layout manager to recalculate if the view should wrap or not, even if the text hadn't changed.

So I wanted to update the view without calling my normal bind function. I solved it by using notifyItemChanged with a payload whenever I want to mark an item as selected, for example:

notifyItemChanged(position: Int, payload: Any?)

Since you're sending a payload, you need to override

onBindViewHolder(holder: MyViewHolder, position: Int, payloads: MutableList<Any>)

It's important to note that this function is what the RecyclerView always calls, so if you don't have a payload, you need to call the super function, or the regular onBindViewHolder, e.g.:

override fun onBindViewHolder(holder: MyViewHolder, position: Int, payloads: MutableList<Any>) {
    if (payloads.isEmpty()) return super.onBindViewHolder(holder, position, payloads)
    // I know that I called notifyItemChanged with a Boolean as payload to set the selected state.
    holder.updateSelectedState(payloads.firstOrNull() as? Boolean ?: false)
}

Then in updateSelectedState I simply update the background or the imageview, and not the item (or, presumably, anything that could mess with the layout's width).

Upvotes: 1

Adam
Adam

Reputation: 1343

So the fix ended up in my XML file.

Resoning: When you are using the flexbox with the flex-direction wrap you're allowing the flexbox library to handle the view sizes. Since my use case lies in nested recycler view one of my recycler view items was using match_parent property which was causing conflicts to adjust in view at the runtime (which caused confusion at library level).

The solution was to simply use wrap_content with flexbox to let the library take decisions for view measurements.

Upvotes: 4

Related Questions