Overriding 'top level function' - android

I'm trying to create a BaseRecyclerViewAdapter<T>:RecyclerView.Adapter<T> class to provide a default/common functionality for classes using generics. Because of the difference in view models' the onBindViewHolder and onCreateViewHolder methods throw NotImplementedError on the base class. Now when I want to implement the methods in the classes which inherit BaseRecyclerViewAdapter<T>, I encounter the following error:
Modifier 'override' is not applicable to 'top level function'
How can I safely override those without encountering this error?
Base.kt:
package com.example
abstract class BaseAdapter<T, THolder: RecyclerView.ViewHolder> (
private val mValues: List<T>,
private val mListener: OnFragmentInteractionListener<T>?
) : RecyclerView.Adapter<THolder>() {
private val mOnClickListener: View.OnClickListener
init {
mOnClickListener = View.OnClickListener { v ->
val item = v.tag as T
mListener?.onListFragmentInteraction(item)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): THolder {
throw NotImplementedError()
}
override fun onBindViewHolder(holder: THolder, position: Int) {
throw NotImplementedError()
}
override fun getItemCount(): Int = mValues.size
//And some other fancy methods
}
Other class.kt:
package com.example
class TheAdapter(
private val mValues: List<Item>,
private val mListener: OnFragmentInteractionListener<Item>?
) : BaseAdapter<Item, ViewHolder>(mValues, mListener)
//Error here
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
}
//Error here
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
}
inner class ViewHolder(val mView: View) : RecyclerView.ViewHolder(mView) {
//ViewHolder properties and methods
}
}

You've overloaded the class name ViewHolder. The ViewHolder in your class definition is RecyclerView.ViewHolder. The one in your function signature is your inner class. You need to specify your inner one in the class definition: BaseAdapter<String, TheAdapter.ViewHolder> to avoid the confusion. Or better yet, use a different class name for your inner class.

Related

How can I use ViewBinding with RecyclerView Adapter with an already existing inner class in Kotlin?

TodoAdapter.kt
class TodoAdapter (var todos:List<Todo>) : RecyclerView.Adapter<TodoAdapter.TodoViewHolder>(){
inner class TodoViewHolder(itemView: View): RecyclerView.ViewHolder(itemView)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TodoViewHolder {
val view= LayoutInflater.from(parent.context).inflate(R.layout.todo_layout, parent, false)
return TodoViewHolder(view)
}
override fun onBindViewHolder(holder: TodoViewHolder, position: Int) {
holder.itemView.apply {
}
}
override fun getItemCount(): Int {
return todos.size
}
}
Above is the Recycler View Adapter Class.
I already have an inner class TodoAdapter. How do I use ViewBinding with this?
The layout file from where I want to access views is todo_layout.xml
I am assuming you are familiar with ViewBinding. Here is how you can use ViewBinding with your RecyclerViewAdapter.
Here will be your TodoAdapter.kt
class TodoAdapter (var todos:List<Todo>) : RecyclerView.Adapter<TodoAdapter.TodoViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TodoViewHolder {
val binding = TodoLayoutBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return TodoViewHolder(binding)
}
override fun onBindViewHolder(holder: TodoViewHolder, position: Int) {
holder.bind()
}
override fun getItemCount(): Int {
return todos.size
}
inner class TodoViewHolder(private val binding: TodoLayoutBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(){
binding.apply{
// Assign Values
}
}
}
}
And now inside your TodoViewHolder bind function call, you can access all your views.
It shows like this. DataBindingUtil and getRoot() are shown in red. When I hover over it android studio asks me to create class,enum,interface,etc and for getRoot() it shows to create reference and create an extension function
Primary Constructor Call Expected Error on super(binding.root)

android - Creating and using ViewBinder inside a RecyclerView adapter

In my app, there is an Activity which has a RecyclerView inside, which loads the list of options needed for that screen.
In the code below, i tried to implement a binder, which is needed because of the recent Android changes.
However, when i open the activity starts, the application crashes, throwing this error, linking the line with binding = ItemSettingsBinding.bind(binding.root):
kotlin.UninitializedPropertyAccessException: lateinit property binding has not been initialized
What am i doing wrong? What's the correct way to implement a binder inside an adapter?
AdapterSettings.kt
class AdapterSettings(
var settingsList: List<DataItemSettings>,
var listener: OnItemClickListener
) : RecyclerView.Adapter<AdapterSettings.SettingsViewHolder>() {
private lateinit var binding: ItemSettingsBinding
inner class SettingsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
init {
itemView.setOnClickListener(this)
}
override fun onClick(p0: View?) {
val position : Int = adapterPosition
if (position != RecyclerView.NO_POSITION) {
listener.OnItemClick(position)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SettingsViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_settings, parent, false)
return SettingsViewHolder(view)
}
override fun getItemCount(): Int {
return settingsList.size
}
override fun onBindViewHolder(holder: SettingsViewHolder, position: Int) {
binding = ItemSettingsBinding.bind(binding.root)
holder.itemView.apply {
binding.rvTitle.text = settingsList[position].stringTitle
binding.rvDescription.text = settingsList[position].stringDescription
binding.rvIcon.setImageResource(settingsList[position].itemIcon)
}
}
interface OnItemClickListener {
fun OnItemClick(position: Int)
}
}
I believe you're missing your inflate in onCreateViewHolder:
// Pseudo-Code
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SettingsViewHolder {
val binding = ItemSettingsBinding
.inflate(LayoutInflater.from(parent.context), parent, false)
return SettingsViewHolder(binding)
}
Then you can make use of it.
Create the binding in onCreateViewHolder and pass the binding into the ViewHolder instead of the inflated View. Thus you create a binding for each created view and only need to do the apply stuff in the onBindViewHolder
Example:
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SettingsViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_settings, parent, false)
val binding = ItemSettingsBinding.bind(view)
return SettingsViewHolder(binding)
}
override fun onBindViewHolder(holder: SettingsViewHolder, position: Int) {
holder.binding.apply {
rvTitle.text = settingsList[position].stringTitle
rvDescription.text = settingsList[position].stringDescription
rvIcon.setImageResource(settingsList[position].itemIcon)
}
}
Adapt your ViewHolder accordingly
There is indeed another way to ViewBind in an adapter.
First, we need to setup the ViewHolder in a different way:
inner class SettingsViewHolder(private val binding: ItemSettingsBinding):
RecyclerView.ViewHolder(binding.root), View.OnClickListener {
With this, we created a binding value inside the brackets, so we are able to call the items of the actual view or layout trough binding.root
Inside the viewholder, we need to create a function used to bind our items. We can either bind like this:
fun bind(item: Item) {
binding.item = item
binding.executePendingBindings()
}
Or like this:
fun bind(item: DataItemSettings) {
binding.rvTitle.text = settingsList[position].stringTitle
binding.rvDescription.text = settingsList[position].stringDescription
binding.rvIcon.setImageResource(settingsList[position].itemIcon)
}
NOTICE: 'getter for position: Int' is deprecated. Deprecated in Java.
And, final step, we need to write this, inside bindViewHolder:
override fun onBindViewHolder(holder: SettingsViewHolder, position: Int) {
holder.bind(settingsList[position])
}

Kotlin Generics - Call companion object method

I've two implementation of RecyclerView.ViewAdapter, which are very identical and I want to unify them as a single class with a type parameter T. The only difference is one class uses ImageViewHolder and the other one VideoViewHolder.
class ImageGridAdapter : RecyclerView.Adapter<ImageViewHolder>() {
private val asyncListDiffer = AsyncListDiffer<Image>(this, diffCallback)
init { setHasStableIds(true) }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ImageViewHolder {
// How to call this companion object method from a generic class?
return ImageViewHolder.from(parent)
}
override fun onBindViewHolder(holder: ImageViewHolder, position: Int) {
holder.bind(asyncListDiffer.currentList[position], dragSelector, lifecycle.get())
}
fun submit(list: List<Image>) = asyncListDiffer.submitList(list)
override fun getItemCount(): Int {
return asyncListDiffer.currentList.size
}
override fun getItemId(position: Int): Long {
return asyncListDiffer.currentList[position].id
}
}
Question: How can I call *ViewHolder.from(parent) in a generic way in Kotlin? I really don't want to create two separate classes just to override a single method!
You can use composition instead of inheritance, delegating ViewHolder creation to a function passed as a constructor parameter:
class GenericGridAdapter<VH : ViewHolder>(private val viewHolderCreator : (ViewGroup) -> VH) : RecyclerView.Adapter<VH>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
return viewHolderCreator.invoke(parent)
}
// Other common methods
}
// And then:
val imageGridAdapter = GenericGridAdapter { ImageViewHolder.from(it) }
val videoGridAdapter = GenericGridAdapter { VideoViewHolder.from(it) }
// or using method references:
val imageGridAdapter = GenericGridAdapter((ImageViewHolder)::from)
val videoGridAdapter = GenericGridAdapter((VideoViewHolder)::from)

Android RecyclerView - Class is not abstract and does not implement abstract base class member

I got this:
class RecentAdapter(private val context: Context, private val videolist: MutableList<Videos>) : RecyclerView.Adapter<RecentAdapter.ViewHolder>(){
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val video = videolist[position]
holder.title.text = video.title
holder.remove.setOnClickListener {
videolist.removeAt(holder.adapterPosition)
notifyItemRemoved(holder.adapterPosition)
}
}
override fun getItemCount() = videolist.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.videoview, parent, false)
return ViewHolder(view)
}
class ViewHolder(itemView: View?) : RecyclerView.ViewHolder(itemView!!){
val title = itemView!!.videoviewTitle!!
val remove = itemView!!.videoviewRemove!!
val like = itemView!!.videoviewLike!!
}
}
I get the error:
Class 'RecentAdapter' is not abstract and does not implement abstract base class member public abstract fun onBindViewHolder(#NonNull p0: RecentAdapter.ViewHolder, p1: Int): Unit defined in android.support.v7.widget.RecyclerView.Adapter
on class RecentAdapter
And
'onBindViewHolder' overrides nothing
And
Unresolved reference on title and remove
Can someone help me?
Change onBindViewHolder()'s signature to this:
override fun onBindViewHolder(holder: ViewHolder, position: Int)
The 1st argument's type is ViewHolder and not RecyclerView.ViewHolder
For the first question:
You are not overriding the right method. In your constructor, you are declaring that the ViewHolder to use is RecentAdapter.ViewHolder, but your onBindViewHolder references the generic RecyclerView.ViewHolder. To fix this, simply change
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
to
override fun onBindViewHolder(holder: RecentAdapter.ViewHolder, position: Int) {
For the second question, I am not sure what the error is with the given files. Make sure you that there are views with Ids videoViewTitle and videoViewRemove within the videoview layout file to start?

Constructor of inner class ViewHolder can be called only with receiver of containing class

I want to set a listener on RecyclerView items using RxJava2. The items are checkboxes. I want to listen to each item separately.
So I am getting an error Constructor of inner class ViewHolder can be called only with receiver of containing class in the
return TraceAdapter.ViewHolder(view)
class TraceAdapter(private var checkList: List<TraceViewModelRow> = listOf()) :
RecyclerView.Adapter<TraceAdapter.ViewHolder>() {
private val publishSubject = PublishSubject.create<Event>()
val events: Observable<Event> = publishSubject
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TraceAdapter.ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_trace_task, parent, false)
return TraceAdapter.ViewHolder(view)
}
override fun getItemCount(): Int = checkList.size
override fun onBindViewHolder(holder: TraceAdapter.ViewHolder, position: Int) {
holder.bindTraceList(checkList[position])
}
override fun onViewRecycled(holder: ViewHolder) {
super.onViewRecycled(holder)
}
inner class ViewHolder(override val containerView: View) : RecyclerView.ViewHolder(containerView), LayoutContainer {
fun bindTraceList(trace: TraceViewModelRow) {
with(trace) {
checkbox_itemText.clicks()
.map { checkList[layoutPosition] }
.subscribe { publishSubject }
checkbox_itemText.text = description
checkbox_itemText.isChecked = isChecked
}
}
}
}
I think you just need to change this line:
return TraceAdapter.ViewHolder(view)
to this:
return ViewHolder(view)
As you've explicitly marked it as an inner class, it requires an instance of the outer class, which you get by default because you're constructing it from within the outer class. However only if you don't prefix it with the outer class type, as shown above.
The distinction between inner and nested classes is a bit different in Kotlin and Java (Java uses the term static for nested classes that don't have an instance of the outer class). It's explained in more detail here.

Categories

Resources