Recycleview in Kotlin - android

I have a problem with:
RecyclerView: No adapter attached; skipping layout
I can't define adapter in OnCreate because the list is not ready.
How I can define adapter in OnCreate? or what is a possible solution for resolve my problem?

In onCreate I did:
adapter = MyAdapter(this#MyActivity)
adapter.data = ArrayList()
Then later I just set adapter.date = xxx
In my adapter I have:
class MyAdapter(val activity: MyActivity) :
RecyclerView.Adapter<MyAdapter.BodyViewHolder>() {
var data: MutableList<MyModel>? = null
set(value) {
field = value
notifyDataSetChanged()
}
override fun getItemCount() = data?.size ?: 0
fun ViewGroup.inflate(#LayoutRes layoutRes: Int, attachToRoot: Boolean = false): View {
return LayoutInflater.from(context).inflate(layoutRes, this, attachToRoot)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BodyViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.my_layout, parent, false)
return BodyViewHolder(view)
}
override fun onBindViewHolder(holder: BodyViewHolder, position: Int) {
holder.bindValue(data?.get(position), activity)
}
class BodyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bindValue(record: MyModel?, activity: MyActivity) {
record?.let {
itemView.mTextView.text = ....
}
}
}
}
Worth mentioning that the height of my recycler view is wrap_content
<android.support.v7.widget.RecyclerView
android:id="#+id/mRecyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

It's totally ok if you don't have data in onCreate. What you need to do is to define the adapter and bind it with your RecyclerView. Once you have data ready, add data to the list in adapter and notify it. Example as below
class MyAdapter: RecyclerView.Adapter<MyViewHolder>() {
private val data = mutableListOf<MyModel>()
override fun getItemCount() = data.count()
fun addData(data : MyModel) {
// add single data the list or call addAll() to add a group of data.
// just remember not to replace the variable
}
fun clearData() {
}
fun deleteData(id: Int) {
}
}

class adpCoba(val context: Context, val datalist:ArrayList<dataCoba>):RecyclerView.Adapter<adpCoba.MyViewHolder>(){
class MyViewHolder (itemView:View):RecyclerView.ViewHolder(itemView){
val text :TextView=itemView.findViewById(R.id.textcoba)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val itemView = LayoutInflater.from(context).inflate(R.layout.fetch_coba,parent,false)
return MyViewHolder(itemView)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val currntItem = datalist [position]
holder.text.text= currntItem.nama
}
override fun getItemCount(): Int {
return datalist.size
}
}

Related

trying use view binding with recycler view adaptor

Where in the code should i be using the following lines to work with my widgets:
binding = WorkoutCardBinding.inflate(layoutInflater)
setContentView(binding.root)
I know it should be used in an activiy's onCreate function but I can't seem to get it to work with the below adapter class
class WorkoutAdaptor (
var workouts: List<Workout>
) : RecyclerView.Adapter<WorkoutAdaptor.WorkoutViewHolder>() {
private lateinit var binding: WorkoutCardBinding
inner class WorkoutViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WorkoutViewHolder {
binding = WorkoutCardBinding.inflate(layoutInflater)
setContentView(binding.root)
val view = LayoutInflater.from(parent.context).inflate(R.layout.workout_card, parent, false)
return WorkoutViewHolder(view)
}
override fun onBindViewHolder(holder: WorkoutViewHolder, position: Int) {
holder.itemView.apply {
binding.tvWorkoutCard.text = workouts[position].
}
}
override fun getItemCount(): Int {
return workouts.size
}
val binding = WorkoutCardBinding.inflate(
LayoutInflater.from(parent.context), parent, false)
return WorkoutViewHolder(binding)
//Change you onCreateViewHolder completely to this code
You don't have to create a binding variable in the adapter class. To use view binding in recyclerView adapter there are a few things that you've to change.
Firstly, change your viewHolder class constructor to accept viewBinding instead of a view
inner class WorkoutViewHolder(private val workoutCardBinding: WorkoutCardBinding) :
RecyclerView.ViewHolder(workoutCardBinding.root) {
fun bind(workout: Workout) {
workoutCardBinding.apply {
// create your view here
}
}
Change onCreateViewHolder, to return a viewHolder object using binding.
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WorkoutViewHolder {
val workoutCardBinding=
WorkoutCardBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return WorkoutCardBinding(workoutCardBinding)
}
Your complete adapter class will look like this -
class WorkoutAdaptor (
var workouts: List<Workout>
) : RecyclerView.Adapter<WorkoutAdaptor.WorkoutViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WorkoutViewHolder {
val workoutCardBinding =
WorkoutCardBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return WorkoutCardBinding(workoutCardBinding)
}
override fun onBindViewHolder(holder: WorkoutViewHolder, position: Int) {
holder.bind(workouts[position])
}
override fun getItemCount(): Int {
return workouts.size
}
inner class WorkoutViewHolder(private val workoutCardBinding: WorkoutCardBinding) :
RecyclerView.ViewHolder(workoutCardBinding.root) {
fun bind(workout: Workout) {
workoutCardBinding.apply {
// create your view here
}
}
}
}

How can i add empty constructor to RecyclerView Adapter android kotlin

I need to add empty constructor to my Adapter, but i have already primary constructor.
My code:
class RvStatesAdapter(private var stateList: List<State>): RecyclerView.Adapter<RvStatesAdapter.MyViewHolder>() {
inner class MyViewHolder(val binding: RvStateListBinding): RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
return MyViewHolder(RvStateListBinding.inflate(LayoutInflater.from(parent.context), parent, false))
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
}
override fun getItemCount(): Int {
return stateList.size
}
I have tried to use costructor(): this() but in this case i dont understand what i need to put in this() brackets
You can make Adapter constructor without params by instead of passing list in primary constructor use function to submit list in adapter.
class RvStatesAdapter(): RecyclerView.Adapter<RvStatesAdapter.MyViewHolder>() {
private val stateList: ArrayList<State> = ArrayList<State>()
/**
* submit list to recycler view adapter for populating items
*/
fun submitList(list: List<State>) {
stateList.addAll(list)
}
inner class MyViewHolder(val binding: RvStateListBinding) :
RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
return MyViewHolder(
RvStateListBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
}
override fun getItemCount(): Int {
return stateList.size
}
}
Submit list using adapter instance
val list = emptyList<State>()
val rvStatesAdapter = RvStatesAdapter()
// init recyclerview properties
rvStatesAdapter.submitList(list)

Recyclerview with view binding, click listener

Iam trying without succes to add detailview (onclick listener) after click on recyclerview item.
Iam using viewbinding and thats new to me.
My code for the adapter:
class MyRecyclerAdapter(listitems: MyFragment) : RecyclerView.Adapter<MyRecyclerAdapter.MyViewHolder>() {
var items: ArrayList<ListItems> = ArrayList()
// class MyViewHolder(val binding: LayoutMyListBinding) : RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
return MyViewHolder(
LayoutMyListBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
class MyViewHolder(var LayoutMyListBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(appInfo: ListItem) {
binding.root.setOnClickListener {
println("clicked for detail")
}
}
}
override fun getItemCount(): Int {
return items.size
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.binding.listItem.text = items[position].titel
}
fun submitList(ItemsList: ArrayList<ListItem>) {
items = ListItems as ArrayList<ListItem>
}
}
How can i implement the click listener with viewbinding in the adapter ? i know i can do holder.binding.setonclicklistener. But this is a bad option.
I I solved it so set an click listener on the ViewHolder
The bind() never get called, so call it in onBindViewHolder
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.binding.vacatureTitel.text = items[position].titel
holder.binding.vacatureWerkgever.text = items[position].werkgever.bedrijfsnaam + " gevestigd in " + items[position].werkgever.werkgever_plaatsnaam
holder.bind(items[position]) // <<< Change here
}
I solved it to put an clicklistener on the Viewholder.

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])
}

Recyclerview illegal state exception with data binding and viewModel

I am fetching a list of categories from an api and setting them into recyclerview . Adapter code is written in viewModel class and is called by the fragment that is calling the api. Below are the methods for setting adapters.
fun getAdapter(
listener: OtherVideoCategoriesRecyclerAdapter.OtherVideoCategoriesRecyclerAdapterListener,context: Context
): OtherVideoCategoriesRecyclerAdapter {
if (categoriesRecyclerAdapter == null)
categoriesRecyclerAdapter = OtherVideoCategoriesRecyclerAdapter(listener,context)
return categoriesRecyclerAdapter as OtherVideoCategoriesRecyclerAdapter
}
fun setItems(categories: ArrayList<OtherCategoriesItem>) {
categoriesList = categories
categoriesRecyclerAdapter!!.setItems(categoriesList!!)
}
And this is how I call these methods from my fragment class.
otherVideoViewModel.setItems(it.first.data!!.otherCategories as ArrayList<OtherCategoriesItem>)
Set Adapter method
private fun setAdapter() {
otherVideosCategoriesBinding.recyclerView.layoutManager = LinearLayoutManager(activity)
otherVideosCategoriesBinding.recyclerView.itemAnimator = DefaultItemAnimator()
adapter = otherVideoViewModel.getAdapter(adapterListener,activity!!)
otherVideosCategoriesBinding.recyclerView.adapter = adapter
}
And this is the adapter class.
class OtherVideoCategoriesRecyclerAdapter(private val listener: OtherVideoCategoriesRecyclerAdapterListener,val context: Context): RecyclerView.Adapter<OtherVideoCategoriesRecyclerAdapter.ViewHolder>() {
var categories = ArrayList<OtherCategoriesItem>()
interface OtherVideoCategoriesRecyclerAdapterListener {
fun onItemClicked(position: Int)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): OtherVideoCategoriesRecyclerAdapter.ViewHolder {
val inflater=LayoutInflater.from(context)
val binding = ItemOtherVideoCategoryBinding.inflate(inflater, parent, false)
return OtherVideoCategoriesRecyclerAdapter.ViewHolder(binding)
}
override fun getItemCount(): Int {
return categories.size
}
override fun onBindViewHolder(holder: OtherVideoCategoriesRecyclerAdapter.ViewHolder, position: Int) {
val item = categories[position]
holder.bindViews(item)
}
class ViewHolder(private val binding: ItemOtherVideoCategoryBinding): RecyclerView.ViewHolder(binding.root) {
fun bindViews(model: OtherCategoriesItem){
binding.model=model
binding.executePendingBindings()
}
}
fun setItems(categoriesList: ArrayList<OtherCategoriesItem>) {
categories = categoriesList
notifyDataSetChanged()
}
}
When I run this code, it crashes with following exception.
java.lang.IllegalStateException: ViewHolder views must not be attached when created. Ensure that you are not passing 'true' to the attachToRoot parameter of LayoutInflater.inflate(..., boolean attachToRoot)
I have tried all the related answers to this error but none of them worked for my case as many of those answers doesn't included data binding.
Hey just changing your onCreateViewHolder a bit. Try this:
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): OtherVideoCategoriesRecyclerAdapter.ViewHolder {
val inflater=LayoutInflater.from(context)
val binding:ItemOtherVideoCategoryBinding = DataBindingUtil.inflate(inflater,R.layout.your_layout_name, parent, false)
return OtherVideoCategoriesRecyclerAdapter.ViewHolder(binding)
}

Categories

Resources