1

I'm trying to create RecyclerView adapter and following this documentation.

private class Adapter(private val list: List<HashMap<String, Any>>, private val ctx: Context) : RecyclerView.Adapter<Adapter.ViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Adapter.ViewHolder {
        var mTextView: TextView

        return ViewHolder(ctx.UI {
            relativeLayout {
                mTextView = textView("1 + 1 = 2")
            }
        }.view)
    }

    override fun getItemCount() = list.size

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.mTextView.text = "1 + 1 = 3" // Should work?
    }

    class ViewHolder(view: View) : RecyclerView.ViewHolder(view)
}

How can I access mTextView in the onBindViewHolder?

Burak
  • 87
  • 1
  • 12

3 Answers3

2

If you're using kotlin extensions and have a text view with an id mTextView, then it should be:

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.itemView.mTextView.text = "1 + 1 = 3" // Should work?
    }

You can also define variables in your ViewHolder and use them directly, this is the best way in terms of performance as it won't force unecessary calls to findviewbyid:

class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    val myTextView = itemView.mTextView
}

And later access it like:

 holder.myTextView.text = "some text"
Levi Moreira
  • 11,917
  • 4
  • 32
  • 46
  • Can you give an example for a text view with an id mTextView? – Burak May 01 '18 at 00:08
  • The first part of the answer tells you how to access it directly, I'll post an example sing a variable – Levi Moreira May 01 '18 at 00:09
  • Thanks. I think my `mTextView` initialization is wrong because when I try your solution I get `Unsolved reference` in `ViewHolder`. Am I missing something? – Burak May 01 '18 at 00:21
  • Did you import you view using kotlin extensions? import kotlinx.android.synthetic..* inside the adapter? – Levi Moreira May 01 '18 at 00:24
  • Oh. I see what you saying. Basically, I'm creating view inside `onCreateViewHolder`. That's what Kotlin Anko does. So is there a way to pass variable name or just should I go with the oldschool way? – Burak May 01 '18 at 01:03
  • If you're not using android extension then findviewbyid is the way to go :) – Levi Moreira May 01 '18 at 01:18
  • That's what I thought. Just were looking for new ways. – Burak May 01 '18 at 01:55
  • Why don't you use android extensions? It's the best and most used way in Kotlin ... – Levi Moreira May 01 '18 at 02:11
  • I discovered [Kotlin Anko](https://github.com/Kotlin/anko) and got a project about it. So, I just don't want to use XML layouts. – Burak May 01 '18 at 04:22
  • It's your personal opinion that "it's the best" to use Extensions, I don't know how you know if it's "the most used way" in Kotlin. It's up to each developer / team to decide if that's the right tool for them out of [many alternatives](https://stackoverflow.com/q/44285703/4465208). Also, in your first snippet you're actually demonstrating a common error made when using it with ViewHolders, (as described [here](https://stackoverflow.com/a/44208181/4465208)), while presenting it as an equal to the correct way you show afterwards. – zsmb13 May 01 '18 at 05:28
  • It is indeed a personal opinion, as for the most used way I just used the web and SO as a thermometer to measure how much a technology is used, I've see loads of projects using kotlinx, but.once again it was a guess, thanks for clarifying. I'll edit my answer to include your comments :) – Levi Moreira May 01 '18 at 10:38
1

I was also facing Unresolved reference when trying to access views in onBindViewHolder.

override fun onBindViewHolder(holder: TodoViewHolder, position: Int) {
    //Unresolved reference fooView
    holder.itemView.fooView.text
}

In the end it turned out I was just missing the kotlinx import atop

import kotlinx.android.synthetic.main.myLayout.view.*

...And adding the extensions plugin in my app's build.gradle file.

plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'kotlin-android-extensions' //here
}

Adding the dependency and the import fixed it for me.

Mig82
  • 4,856
  • 4
  • 40
  • 63
0

What you need to do here is to create a property for your TextView inside your ViewHolder. This isn't particularly easy with Anko, but here are some ways to do it.


1. Pass every View you want be able to reference from your ViewHolder as a separate constructor parameter.

For this solution, you have to modify your ViewHolder class like this, adding a property for the TextView:

class ViewHolder(view: View, val mTextView: TextView) : RecyclerView.ViewHolder(view)

You can initialize this property in this slightly convoluted way:

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Adapter.ViewHolder {
    lateinit var mTextView: TextView

    val view = ctx.UI {
        relativeLayout {
            mTextView = textView("1 + 1 = 2")
        }
    }.view

    return ViewHolder(view, mTextView)
}

While this doesn't look very nice, you're keeping the reference to your TextView from when you're initially creating it, so it's efficient.


2. Give your Anko created View instances IDs and look them up by their IDs later.

First, you have to create constants for your IDs - the ID can be any positive number, just make sure they're unique within the scope you're using them. One way is to place these inside a companion object, like so:

companion object {
    private const val TEXT_VIEW_ID = 123 // Arbitrary positive number
}

Then, you need to give this ID to your TextView:

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Adapter.ViewHolder {
    return ViewHolder(ctx.UI {
        relativeLayout {
            textView("1 + 1 = 2") {
                id = TEXT_VIEW_ID
            }
        }
    }.view)
}

Finally, you can find your TextView again in your ViewHolder class with findViewById:

class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
    val mTextView: TextView = view.findViewById(TEXT_VIEW_ID)
}

Of course this is going to be slower, because you're losing the TextView reference you already had when you're passing in the View to the ViewHolder, and then you're looking it up again.


Note that I stuck with mTextView for the answer so that it's easier to follow, but it's generally not recommended to use Hungarian notation or any other name prefixes in Kotlin.

zsmb13
  • 85,752
  • 11
  • 221
  • 226
  • Your first answer isn't easy like you said but it's also nice when you have few views. I'll just stick with the `findViewById` method. – Burak May 02 '18 at 22:13