Kotlin - RecyclerView.ViewHolder subclass - unable to access an extra property - android-recyclerview

I have implemented RecyclerView.ViewHolder sub-class as below:
class PersonViewHolder(itemView: View, binding: ViewDataBinding) : RecyclerView.ViewHolder(itemView) { }
Now I am trying to access binding property declared in it like this within subclass of RecyclerView.Adapter:
override fun onBindViewHolder(holder: PersonViewHolder?, position: Int) {
val person = persons[position]
if (holder != null) {
holder.binding.setVariable(BR.person, person) // line with error
holder.binding.executePendingBindings() // line with error
}
}
But compiler is complaining - Unresolved reference: binding
Here is the complete implementation:
class PersonsAdapter(private var persons: Array<Person>) : RecyclerView.Adapter<PersonsAdapter.PersonViewHolder>() {
override fun onBindViewHolder(holder: PersonViewHolder?, position: Int) {
val person = persons[position]
if (holder != null) {
holder.binding.setVariable(BR.person, person)
holder.binding.executePendingBindings()
}
}
override fun getItemCount(): Int {
return persons.size
}
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): PersonViewHolder {
val itemView = LayoutInflater.from(parent!!.context).inflate(R.layout.list_item_person, parent, false)
return PersonViewHolder(itemView, DataBindingUtil.bind(itemView))
}
class PersonViewHolder(itemView: View, binding: ViewDataBinding) : RecyclerView.ViewHolder(itemView) { }
}
Any ideas if I am missing anything over here? Please suggest.

binding: ViewDataBinding - you're only defining a constructor parameter, it is never saved as a member of the class. Mark it using var or val to have it store the parameter and have it be accessible later on.

check build.gradle file (module level) in your project
in the top android extesion plugin is included or not?
plugin {
id 'kotlin-android-extensions'
}

Related

ViewPager2 and FragmentStatePagerAdapter

I want to set the adapter of ViewPager2 to FragmentStatePagerAdapter but I get this error:
Type mismatch. Required: (RecyclerView.Adapter<RecyclerView.ViewHolder!>?..RecyclerView.Adapter<*>?) Found: ViewPager2Adapter
My ViewPagerAdapter class is
class ViewPager2Adapter(fm:FragmentManager) :FragmentStatePagerAdapter(fm) {
override fun getItem(position: Int): Fragment {
return when(position) {
0 -> {
MyScansListFragment()
}
1 -> {
PurchasedItemsFragment()
}
else -> {
Fragment()
}
}
}
override fun getCount(): Int {
return 2
}
override fun getItemPosition(`object`: Any): Int {
return POSITION_NONE
}}
And in the oncreateView() :
val viewPager2Adapter = ViewPager2Adapter(activity?.supportFragmentManager!!)
binding!!.viewPager.adapter = viewPager2Adapter
okay, let's change the code a little bit.
First of all, FragmentStatePagerAdapter has been deprecated.
FragmentStatePagerAdapter & FragmentPagerAdapter have been recently deprecated, and your code must look something like this. FragmentStatePagerAdapter and if you get your cursor over it and see details, there will be a statement "Deprecated Switch to androidx.viewpager2.widget.ViewPager2 and use androidx.viewpager2.adapter.FragmentStateAdapter instead."
try the following code.
class ViewPager2Adapter(private val listFragment: ArrayList<Fragment>,
fm: FragmentManager,
lifecycle: Lifecycle) : FragmentStateAdapter(fm, lifecycle) {
override fun getItemCount(): Int {
return listFragment.size
}
override fun createFragment(position: Int): Fragment {
return listFragment[position]
}
}
so, this is now kind of your universal viewpager adapter.
The next thing is we require fragments to be passed in here.
//I don't think you need Fragment() but since it's there in your list.
val fragmentList = listOf(MyScansListFragment(), PurchasedItemsFragment(),Fragment())
val viewPager2Adapter = ViewPager2Adapter(fragmentList, activity?.supportFragmentManager!!, lifecycle)
binding!!.viewPager.adapter = viewPager2Adapter

Unexpected closure resolution

Consider this snipped:
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.test, container, false)
class MyViewHolder(val view: View): RecyclerView.ViewHolder(view) {
init {
view.setOnClickListener {
Log.d("hey", "there")
}
}
}
view.findViewById<RecyclerView>(R.id.files).adapter = object: RecyclerView.Adapter<MyViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val item = LayoutInflater.from(parent.context).inflate(R.layout.item, parent, false)
return MyViewHolder(item)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.view.findViewById<TextView>(R.id.text).text = files[position].name
}
override fun getItemCount() = files.size
}
return view
}
onCreateView creates a local variable view. The nested class MyViewHolder also declares a variable called view. What was unexpected is that the variable view accessed inside the init block of MyViewHolder (where the OnClickListener is set) is not the one declared in MyViewHolder, but the outer one. Why?
I would expect the innermost variable declaration would be used.
The class is not declared as an inner class. Outside variables should not be accesible.
What am I missing?
This is not the case of a nested class, but of a function that returns a class.
If you try to define your class as inner, you'll actually get an error message:
Modifier 'inner' is not applicable to 'local class'
I'll simplify this example a bit, so the Android part won't interfere:
// This is what you're doing
fun a(): Any {
val a = "a"
class B(val a: String = "b") {
init {
println(a)
}
}
return B()
}
// This is what you think you're doing
class A(val a: String = "a") {
class B(val a: String = "b") {
init {
println(a)
}
}
}
fun main() {
// This refers to function called a
val func = a()
// This refers to a nested class called B
val nestedClass = A.B()
}
If you actually want to refer to the local class properties, use this

How to create custom Adapter with for 3 recycleViews

Hello All,
i want to ask if i can add to my 3 recycle Views each recycle view hase interface to optimise my code i tried to add only 1 adapter for the 3 recycle View, as you can see my code below but i find my self stuck with this adapter, any 1 have idea how add custom adapter to adapt 3 recycle View? Thanx.
class CustomAdapter(private val contexte: Context) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private val context: Context = contexte
inner class FolderViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
}
inner class PagesViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
}
inner class CorpusViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
if (viewType == VIEW_TYPE_CORPUS)
return CorpusViewHolder(
LayoutInflater.from(parent.context)
.inflate(R.layout.corpus_item_layout, parent, false)
)
if (viewType == VIEW_TYPE_FOLDER)
return FolderViewHolder(
LayoutInflater.from(parent.context)
.inflate(R.layout.folder_item_layout, parent, false)
)
return PagesViewHolder(
LayoutInflater.from(parent.context).inflate(
R.layout.page_item_layout, parent, false
)
)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
TODO("Not yet implemented")
}
override fun getItemCount(): Int {
return 20
}
companion object {
internal val VIEW_TYPE_CORPUS = 1
internal val VIEW_TYPE_FOLDER = 2
internal val VIEW_TYPE_PAGES = 2
}
Instead of doing this, I suggest you to use base class that takes layout id and initialize your common adapter with that.
open class AdapterItem(val layoutId: Int)
data class Corpus(val id: Int): AdapterItem(id)
then init your adapter like
CustomAdapter<AdapterItem>(...)
in your adapter, override getView
#Override
fun getView(position: Int, convertView: View, parent: ViewGroup): View {
val item = list[position]
return if(converView != null){
convertView
} else {
LayoutInflater.from(parent.context).inflate(item.layoutId, parent, false)
}
}

I've tried to make a RecyclerView with an Adapter but I cannot see the layout?

I've tried to build a recyclerview with an adapter class and cardview layout however even though the code runs I cannot see the list with cardview (I just get a blank recyclerview)?
SpeakerAdapter.kt
class SpeakerAdapter (val speakers: ArrayList<String>): RecyclerView.Adapter<SpeakerAdapter.ViewHolder>() {
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val speakerName: TextView = itemView.findViewById(R.id.name)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.speakerName.text = speakers[position]
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val View = LayoutInflater.from(parent.context).inflate(R.layout.speaker_list, parent, false)
return ViewHolder(View)
}
override fun getItemCount() = speakers.size
}
Speaker.kt
class Speaker : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState)
setContentView(R.layout.activity_speaker)
val speakers : ArrayList<String> = ArrayList()
for (i in 1..100){
speakers.add("Speaker 1 #$i")
recyclerView_speaker.layoutManager = LinearLayoutManager(this)
recyclerView_speaker.adapter = SpeakerAdapter(speakers)
}
}}
It's a bit hard to debug without the xml. But here goes nothing:
I think it'd be worth a shot modifying your view holder class. The reference for the text view is being set when you init the view holder and the layout may not be inflated yet. Try finding the reference when you bind the data to the view to avoid this issue.
Try this:
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(name: String?) {
itemView. name.text = name
}
}
and then modify your adapter to call this method
class SpeakerAdapter (val speakers: ArrayList<String>): RecyclerView.Adapter<SpeakerAdapter.ViewHolder>() {
...
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(speakers[position])
}
}
PS.: it'd be a good idea to call your view holder something else. ViewHolder is a name that is already used by the android SDK. Ex.: SpeakerViewholder

How can I convert this Firebase Recycler Adapter implementation into an Adapter.kt class?

I have written a Kotlin FirebaseRecyclerAdapter that works just fine as part of my MainActivity. However, I would like to have this code in a separate MainAdapter.kt file/class. How can I do this?
var query = FirebaseDatabase.getInstance()
.reference
.child("").child("categories")
.limitToLast(50)
val options = FirebaseRecyclerOptions.Builder<Category>()
.setQuery(query, Category::class.java)
.setLifecycleOwner(this)
.build()
val adapter = object : FirebaseRecyclerAdapter<Category, CategoryHolder>(options) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CategoryHolder {
return CategoryHolder(LayoutInflater.from(parent.context)
.inflate(R.layout.category_row, parent, false))
}
protected override fun onBindViewHolder(holder: CategoryHolder, position: Int, model: Category) {
holder.bind(model)
}
override fun onDataChanged() {
// Called each time there is a new data snapshot. You may want to use this method
// to hide a loading spinner or check for the "no documents" state and update your UI.
// ...
}
}
class CategoryHolder(val customView: View, var category: Category? = null) : RecyclerView.ViewHolder(customView) {
fun bind(category: Category) {
with(category) {
customView.textView_name?.text = category.name
customView.textView_description?.text = category.description
}
}
}
Given your code you could do something like this :
class MainAdapter(lifecycleOwner: LifecycleOwner) : FirebaseRecyclerAdapter<Category, CategoryHolder>(buildOptions(lifecycleOwner)) {
companion object {
private fun buildQuery() = FirebaseDatabase.getInstance()
.reference
.child("").child("categories")
.limitToLast(50)
private fun buildOptions(lifecycleOwner:LifecycleOwner) = FirebaseRecyclerOptions.Builder<Category>()
.setQuery(buildQuery(), Category::class.java)
.setLifecycleOwner(lifecycleOwner)
.build()
}
class CategoryHolder(val customView: View, var category: Category? = null) : RecyclerView.ViewHolder(customView) {
fun bind(category: Category) {
with(category) {
customView.textView_name?.text = category.name
customView.textView_description?.text = category.description
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CategoryHolder {
return CategoryHolder(LayoutInflater.from(parent.context)
.inflate(R.layout.category_row, parent, false))
}
protected override fun onBindViewHolder(holder: CategoryHolder, position: Int, model: Category) {
holder.bind(model)
}
override fun onDataChanged() {
// Called each time there is a new data snapshot. You may want to use this method
// to hide a loading spinner or check for the "no documents" state and update your UI.
// ...
}
}
There are many other ways to handle this problem, this is just an encapsulated version of yours.