简体   繁体   中英

How to solve RecyclerView.Adapter issue?

I am trying to change first text(position = 0) gravity from List in 'onBindViewHolder(viewHolder: ViewHolder, position: Int)', but, except for the first, the gravity of some elements changes. Could you tell me where this problem comes from?

You can see code below and also logs

class ButtonsListAdapter(private val dataSet: List<String>) :
    RecyclerView.Adapter<ButtonsListAdapter.ViewHolder>() {

    class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val textView: TextView
        val divider: View

        init {
            textView = view.findViewById(R.id.textView)
            divider = view.findViewById(R.id.divider)
        }
    }

    override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
        val view = LayoutInflater.from(viewGroup.context)
            .inflate(R.layout.buttons_list_item, viewGroup, false)

        return ViewHolder(view)
    }

    override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {

        viewHolder.textView.text = dataSet[position]
        if(position == 0) {
            Log.i("Position", " position ->  $position ")
            viewHolder.textView.gravity = Gravity.START or Gravity.CENTER_VERTICAL
        }
        Log.i("Position", " gravity ->  $position " + viewHolder.textView.gravity)
    }

    override fun getItemCount() = dataSet.size
}

Logs

I/Position:  position ->  0 
I/Position:  gravity ->  0 8388627
I/Position:  gravity ->  1 17
I/Position:  gravity ->  2 17
I/Position:  gravity ->  3 17
I/Position:  gravity ->  4 17
I/Position:  gravity ->  5 17
I/Position:  gravity ->  6 17
I/Position:  gravity ->  7 17
I/Position:  gravity ->  8 17
I/Position:  gravity ->  9 8388627
I/Position:  gravity ->  10 17
I/Position:  gravity ->  11 17
I/Position:  gravity ->  12 17
I/Position:  gravity ->  13 17
I/Position:  gravity ->  14 17
I/Position:  gravity ->  15 17
I/Position:  gravity ->  16 17
I/Position:  gravity ->  17 17
I/Position:  gravity ->  9 8388627
I/Position:  gravity ->  8 17
I/Position:  gravity ->  7 17
I/Position:  gravity ->  6 17
I/Position:  gravity ->  5 17
I/Position:  gravity ->  4 17
I/Position:  gravity ->  3 17
I/Position:  gravity ->  2 17
I/Position:  gravity ->  1 17
I/Position:  position ->  0 
I/Position:  gravity ->  0 8388627

I believe the RecyclerView tries to use old rows to save on memory usage. It's kinda like: when the view 0 goes out the screen, the new line 9 will "recycle" it to show itself (that why it's called RecyclerView).

If you want a different first line, try override the method int getItemViewType(int position) and specify that the first item is a "different type" from the rest

(or you could just add an else on if(position == 0) and force the gravity you want, but I think the other option is more elegant)

ReyclerView s have a handful of ViewHolder s that they swap around to make it look like a large list, but really it's the same few boxes riding along a conveyor belt, being moved from the end back to the start.

onBindViewHolder is where you set the contents of one of those boxes to display the item at that position in the list. The ViewHolder that you're provided with still looks like it did when it was set up to display a previous item, so you need to make sure everything is set up correctly for the new one. Anything you don't change, will stick and look like the last item it was displaying.

So here's your problem:

override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
    viewHolder.textView.text = dataSet[position]
    if(position == 0) {
        viewHolder.textView.gravity = Gravity.START or Gravity.CENTER_VERTICAL
    }
}

Part of the display state that changes between items is the gravity - and here you're setting it for item 0 , but you're not setting it to normal for the other items. It's sticky , you only set it one way, and it can never revert. So the next item that needs to display in that recycled ViewHolder object will have that same gravity setting, because that's what it's been set to! Which is item 9 going by your logs (you can see how many ViewHolder s are getting created by when the reuse starts - looks like it happens to use 9 of them in this case)


So yeah, you need to set the gravity in all cases - set the whole display state for every item, to ensure it's consistent and that none of the old state can influence how it looks now:

override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
    viewHolder.textView.text = dataSet[position]
    // explicitly set the gravity every time
    if(position == 0) {
        viewHolder.textView.gravity = Gravity.START or Gravity.CENTER_VERTICAL
    } else {
        viewHolder.textView.gravity = // whatever
    }
}

But I hope that makes the broader point clearer - you always need to do this stuff with RecyclerView s when you're binding items, because you're reusing old items that are filled in with old data and old state. So you should fully configure them each time, for anything that could potentially change. You don't want any old state hanging around!

(Luiz's answer is also a good option, having a completely separate item type for a header allows you to use a different ViewHolder with its own layout etc, so there's no need to keep reconfiguring a single layout. But it might be too much work if you're making a small change - you have options!)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM