Kotlin RecyclerView DataBindingAdapter I do not know why I get the error - android

I want to combine imageview with data bindingadapter. I was searching Google about my problem. The problem is databindingadapter doesn't work in my XML.
To put it easy, other views recognize well such as textView. If I enter a variable name at ImageView, the color should change but it will not change.
Here is my code
<data class> (databindingadapter)
import android.net.Uri
import android.widget.ImageView
import androidx.databinding.BindingAdapter
import com.squareup.picasso.Picasso
class Person {
val name = ""
val age = ""
var image = ""
}
object imageBindingAdapter {
val person = Person()
#JvmStatic
#BindingAdapter("image")
fun bindImage(imageView: ImageView) {
Picasso.get().load(Uri.parse(person.image))
.fit().centerCrop().into(imageView)
}
}
XML code
<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
<variable name="person"
type="com.example.kotlinerecyclerview.Person"/>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<ImageView android:layout_width="100dp" android:layout_height="100dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:src="#android:drawable/btn_default"
app:image="person.image"
/>
<TextView
android:text="#{person.name}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/tv_name"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:text="#{String.valueOf(person.age)}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="#+id/tv_name"
app:layout_constraintStart_toStartOf="parent"
android:id="#+id/tv_age"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Here is my recyclerview adapter class
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.kotlinerecyclerview.databinding.ItemBinding
class ListAdapter(val items: List<Person>, private val clickListener:
(person: Person) -> Unit) :
RecyclerView.Adapter<ListAdapter.SampleViewHolder>() {
class SampleViewHolder(val binding: ItemBinding) :
RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
SampleViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item, parent, false)
val holder = SampleViewHolder(ItemBinding.bind(view))
view.setOnClickListener {
clickListener.invoke(items[holder.adapterPosition])
}
return holder
}
override fun getItemCount() = items.size
override fun onBindViewHolder(holder: SampleViewHolder, position: Int) {
holder.binding.person = items[position]`enter code here`
}
}
I moved this code to another class ex) mainactivity, listadapter
but it doesn't work.
I wrote the code as described in other videos or documents about databindingadapter.

Try to pass the Image Url also to the Binding Adapter
#JvmStatic
#BindingAdapter("image")
fun bindImage(imageView: ImageView, url: String) {
Picasso.get().load(url)
.fit().centerCrop().into(imageView)
}
Also change your xml as given below
<ImageView android:layout_width="100dp" android:layout_height="100dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:src="#android:drawable/btn_default"
app:image="#{person.image}"
/>
Check this Guide also

First workaround:
Move binding adapter in separate file and add "app:"
ImageViewBindings.java
#BindingAdapter("app:image")
fun setImage(view: ImageView, url: String) {
Picasso.get().load(url).fit().centerCrop().into(view)
}
In
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:src="#android:drawable/btn_default"
app:image ="person.image"/>
android:src and app:image
try to do the same thing. It can be a reason of conflict.
If your's intent is set multiple images, consider to separate them (for example to 2 different ImageViews).
person.image is not an ObservableField so you should use executePendingBindings like
override fun onBindViewHolder(holder: SampleViewHolder, position: Int) {
holder.binding.person = items[position]
holder.binding.executePendingBindings()
}
Second workaround:
There is possibility what your urls use http protocol, not https.
In this case this link may help: Picasso not loading image from http url but loads images from https url?

Related

problem, can't create an event that generates a toast message on kotlin recyclerview

I'm using Kotlin to create an event that generates a toast message on click of a recyclerview. I run into trouble making a Tost message in a recyclerview event.
I tried the following page, but couldn't solve it.
Toast message is not working in Recycler View
error code is
in kotlin & None of the following functions can be called with the arguments supplied: public open fun makeText(p0: Context!, p1: CharSequence!, p2: Int): Toast! defined in android.widget.Toast public open fun makeText(p0: Context!, p1: Int, p2: Int): Toast! defined in android.widget.Toast
PrintActivity.kt
package com.questionbank
class PrintActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val vBinding = ActivityPrintBinding.inflate(layoutInflater)
setContentView(vBinding.root)
val helper = SqliteHelper(this, "myDB.sql", 1)
var recyclerViewAdapter = CustomAdapter()
recyclerViewAdapter.listData = helper.select()
vBinding.myRecyclerView.adapter = recyclerViewAdapter
vBinding.myRecyclerView.layoutManager = LinearLayoutManager(this)
vBinding.myRecyclerView.addItemDecoration(
DividerItemDecoration(this, DividerItemDecoration.VERTICAL)
)
}
class CustomAdapter : RecyclerView.Adapter<CustomAdapter.Holder>() {
var listData = ArrayList<questionType>()
inner class Holder(val vBinding: QuestionLayoutRecyclerBinding) :
RecyclerView.ViewHolder(vBinding.root) {
fun setData(id:Int?, question: String, answer: String, exp: String) {
vBinding.printId.text=id.toString()
vBinding.myLinear.setOnClickListener {
// error occur
Toast.makeText(this#PrintActivity, "test", Toast.LENGTH_SHORT).show()
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
val vBinding = QuestionLayoutRecyclerBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
return Holder(vBinding)
}
override fun onBindViewHolder(holder: Holder, position: Int) {
val question = listData[position]
holder.setData(question.id, question.question, question.answer, question.exp)
}
override fun getItemCount(): Int {
return listData.size
}
}
}
activity_print
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".PrintActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/myRecyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="32dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="32dp"
android:layout_marginBottom="32dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
question_layout_recycler.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/myLinear"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dp">
<TextView
android:id="#+id/printId"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView" />
</LinearLayout>
Two ways to fix,
Make CustomAdapter class as inner class.
innner class CustomAdapter : RecyclerView.Adapter<CustomAdapter.Holder>() {
So toast function it will take constant from activity class.
In viewholder, get context from view. it.context will get context from linearlayout.
vBinding.myLinear.setOnClickListener {
Toast.makeText(it.context, "test", Toast.LENGTH_SHORT).show()
}
Its recommended to place adapter logic in separate file and use second solution.
So you dont need to make adapter as inner class.

recycler view does not respect list item's margin

how it should look -
how it looks -
listitem.xml -
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardBackgroundColor="#color/card_background"
android:layout_marginVertical="5dp"
android:layout_marginHorizontal="10dp"
app:cardCornerRadius="8dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/is_app_or_web_image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/card_spacing"
android:padding="#dimen/image_padding"
app:layout_constraintBottom_toBottomOf="#id/delete_image_view"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="#id/delete_image_view"
tools:src="#drawable/is_website_icon_24" />
<TextView
android:id="#+id/service_name_text_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="#dimen/card_spacing"
android:text="#string/service_name"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="#id/delete_image_view"
app:layout_constraintLeft_toRightOf="#id/is_app_or_web_image_view"
app:layout_constraintRight_toLeftOf="#id/edit_image_view"
app:layout_constraintTop_toTopOf="#id/delete_image_view" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="#+id/delete_image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/card_spacing"
android:layout_marginEnd="#dimen/card_spacing"
android:background="#color/card_foreground"
android:padding="#dimen/image_padding"
android:src="#drawable/ic_baseline_delete_20"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="#+id/show_image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="#dimen/card_spacing"
android:background="#color/card_foreground"
android:padding="#dimen/image_padding"
android:src="#drawable/ic_baseline_show_20"
app:layout_constraintBottom_toBottomOf="#id/delete_image_view"
app:layout_constraintRight_toLeftOf="#id/delete_image_view"
app:layout_constraintTop_toTopOf="#id/delete_image_view" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="#+id/edit_image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="#dimen/card_spacing"
android:background="#color/card_foreground"
android:padding="#dimen/image_padding"
android:src="#drawable/ic_baseline_edit_20"
app:layout_constraintBottom_toBottomOf="#id/delete_image_view"
app:layout_constraintRight_toLeftOf="#id/show_image_view"
app:layout_constraintTop_toTopOf="#id/delete_image_view" />
<androidx.cardview.widget.CardView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginVertical="#dimen/card_spacing"
app:cardBackgroundColor="#color/card_foreground"
app:cardCornerRadius="#dimen/corner_radius"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="#id/is_app_or_web_image_view"
app:layout_constraintRight_toRightOf="#id/delete_image_view"
app:layout_constraintTop_toBottomOf="#id/is_app_or_web_image_view">
<com.google.android.material.textview.MaterialTextView
android:id="#+id/service_password_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
android:background="#00000000"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="#string/service_password" />
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
what I have already tried -
setting the list item's height to be a fixed value. something like 200 dp (that had no effect. It was as if that value wasn't even entered)
tried changing layout managers(no effect)
now, I don't even know what to try. any help is useful.
adapter code -
package com.kenetic.savepass.adapters
import android.util.Log
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.kenetic.savepass.R
import com.kenetic.savepass.databinding.PassListBinding
import com.kenetic.savepass.password.PasswordData
class PassAdapter() :
ListAdapter<PasswordData, PassAdapter.PassViewHolder>(diffCallBack) {
class PassViewHolder(val binding: PassListBinding) : RecyclerView.ViewHolder(binding.root) {
var verifiedWithPassword = false
private val TAG = "PassAdapter"
fun bind(passwordData: PasswordData) {
binding.isAppOrWebImageView.setImageResource(
if (passwordData.isAnApplication) {
R.drawable.is_application_icon_24
} else {
R.drawable.is_website_icon_24
}
)
binding.serviceNameTextView.text = passwordData.serviceName
binding.servicePasswordTextView.text = passwordData.servicePassword
binding.deleteImageView.setOnClickListener {
Log.d(TAG, "delete image onClick working")
}
binding.showImageView.setOnClickListener {
Log.d(TAG, "show image onClick working")
}
binding.editImageView.setOnClickListener {
Log.d(TAG, "edit image onClick working")
}
}
}
companion object {
private val diffCallBack = object : DiffUtil.ItemCallback<PasswordData>() {
override fun areItemsTheSame(oldItem: PasswordData, newItem: PasswordData) =
(oldItem.id == newItem.id)
override fun areContentsTheSame(oldItem: PasswordData, newItem: PasswordData) =
(oldItem == newItem)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PassViewHolder {
return PassViewHolder(PassListBinding.inflate(LayoutInflater.from(parent.context)))
}
override fun onBindViewHolder(holder: PassViewHolder, position: Int) {
holder.bind(getItem(position))
}
}
I had the same problem, but it turned that I wasn't inflating the recyclerview item the good way.
I was previously inflating these parameters :
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TodoViewHolder {
return TodoViewHolder(
TodoListItemBinding.inflate(LayoutInflater.from(parent.context))
)
}
And here's the inflating way that made margin between items appear correctly :
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TodoViewHolder {
return TodoViewHolder(
TodoListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
)
}
list item will work remove margin android:layout_marginVertical="5dp" android:layout_marginHorizontal="10dp" and add this line android:layout_margin="5dp" if you want use margin different using margin top and margin_bottom instead of marginvertical
(answering my own question) (work around)
my list item xml was a card layout. I changed it such that the card layout was inside a constraint layout and then constrained the card layout to all four sides of the parent constraint layout. So, now my list item xml is a constraint layout with a card layout inside it. Then, I added margin to the card layout which gave spacing between the border of the card layout and the constraint layout. So, now I visually have spacing between my list items.
the next issue is the constraint layout background being of white colour. To fix that, set the constraint layout colour to be transparent #00000000 . That's all.
Florian-Martin answered the question perfectly. and the solution is very simple
in your
#override
ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
}
method change following line.
return PassViewHolder(PassListBinding
.inflate(LayoutInflater
.from(parent.getContext())));
to
return PassViewHolder(PassListBinding
.inflate(LayoutInflater
// add ViewGroup parent and boolean attachToRoot parameters
.from(parent.getContext())),parent,false);

Unable to bind data in RecyclerView

I'm trying to learn mobile development and got stuck trying to do a recyclerview.
I've set up the data class and also the xml file of the item.
When I try to bind the data to the xml file, in the onBindViewHolder function, the system gives me no option to select which ID I want to bind to.
Data Class
package com.example.recyclerview1
data class Todo(
val title: String,
var isChecked: Boolean
)
XML item
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="100dp"
android:padding="16dp">
<TextView
android:id="#+id/tvTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="title"
android:textSize="24sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#+id/cbDone"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<CheckBox
android:id="#+id/cbDone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Adapter Class
package com.example.recyclerview1
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
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.item_todo,parent,false)
return TodoViewHolder(view)
}
override fun getItemCount(): Int { return todos.size}
override fun onBindViewHolder(holder: TodoViewHolder, position: Int) {
holder.itemView.tvTitle << right here I want to select the ID of the TextView, but the IDE does not show me
}
}
You can use the findViewById method for getting an instance of the view component and then perform some operation on them like this:
class TodoAdapter(var todos:List<Todo>) : RecyclerView.Adapter<TodoAdapter.TodoViewHolder>() {
inner class TodoViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
{
var tvTitle: TextView = itemView.findViewById(R.id.tvTitle)
var cbDone: CheckBox = itemView.findViewById(R.id.cbDone)
}
override fun onBindViewHolder(holder: TodoViewHolder, position: Int) {
holder.tvTitle. // DO YOUR STUFF HERE
}
}
Or you can use ViewBinding capabilities for accessing view instances like is written in this post.
If you want to perform the whole data binding inside XML, then you need to use DataBinding feature. To do that, just follow this post on how to enable DataBinding and how to use it inside your project.
One cannot data-bind, while the XML will generate no data-binding. That item XML needs layout & data node and the model class might need #Bindable annotations. Better inflate with the data-binding, but for that it would need the be generated.

Image gets replaced in recylerview while scrolling, android?

I am displaying a list of countries along with their flags in recylerview
The 1st element does not have a image and uses a default image which is visible on launch of page
But when I scroll and come back to it the image gets changed to some random from the list which should not happen
This is my adapter
class CountryAdapter(private val list: MutableList<Data?>?) :
RecyclerView.Adapter<CountryAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding = ElementCountryBinding.inflate(inflater)
return ViewHolder(binding)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val country: Data? = list?.get(position)
if (country != null) {
holder.bind(country)
}
holder.itemView.setOnClickListener {
}
}
override fun getItemCount(): Int = list!!.size
inner class ViewHolder(val binding: ElementCountryBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(country: Data) {
binding.data = country
if (country.filePath != null)
Glide.with(binding.root.context)
.load(country.filePath!!.trim()).into(binding.ivFlag)
}
}
}
This is the xml layout
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="data"
type="com.mountmeru.model.Data" />
</data>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.cardview.widget.CardView
android:id="#+id/main_cardview"
android:layout_width="match_parent"
android:layout_height="80dp"
android:layout_marginBottom="5dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.AppCompatImageView
android:id="#+id/iv_flag"
android:layout_width="100dp"
android:layout_height="70dp"
android:layout_marginStart="10dp"
android:adjustViewBounds="true"
android:src="#drawable/ic_share"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/tvCountryName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:text="#{data.countryName}"
app:layout_constraintBottom_toBottomOf="#+id/iv_flag"
app:layout_constraintLeft_toRightOf="#+id/iv_flag"
app:layout_constraintTop_toTopOf="#+id/iv_flag" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
< /RelativeLayout>
</layout>
screenshot
So the default image you specified in your XML layout is the ic_share, this means that when onBindViewHolder is called, the image gets substituted by:
.load(country.filePath!!.trim()).into(binding.ivFlag)
However, you never specified that at position 0, the icon must be ic_share, so because of RecyclerView's nature, when you scroll downwards and upwards and the first itemHolder is created (again) it uses a recycled view from further down, and as you're not setting ic_share to iv_flag at position 0 it just uses the recycled view image.
If you just add a line of code like #ADM suggested in your bind method like this:
if(adapterPosition==0){
binding.ivFlag.setImageResource(R.drawable.ic_share)
}
With the ic_share, I think that should make it work
That's normal due to the recycling mechanism of views in RV/LV. To avoid that set it to null
if (country.filePath != null)
Glide.with(binding.root.context)
.load(country.filePath!!.trim()).into(binding.ivFlag)
else
binding.ivFlag.setImageDrawable(null)
assuming ivFlag is an ImageView, or a default/placeholder if you have it
This is happening because you never set ic_share during bind View.
inner class ViewHolder(val binding: ElementCountryBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(country: Data) {
binding.data = country
if(adapterPosition==0){
binding.ivFlag.setImageResource(R.drawable.binding.ivFlag)
}else {
if (country.filePath != null)
Glide.with(binding.root.context)
.load(country.filePath!!.trim()).into(binding.ivFlag)
}
}
}
Having the function getItemViewType also solves the problem
override fun getItemViewType(position: Int): Int {
return position
}

Android RecyclerView Adapter DataBinding - cannot find symbol {layout}BindingImpl

I have a Fragment that displays a list of cities with weather informations. I am using a RecyclerView and I am trying to implement the Data Binding Library in my RecyclerView Adapter but for some reason I get this compile error :
> error: cannot find symbol import
com.example.zach.weatherapp.databinding.CityListItemBindingImpl;
> ^
> symbol: class CityListItemBindingImpl
> location: package com.example.zach.weatherapp.databinding
It's an auto generated class so i really don't know where the error is. I had the same error previously for other layouts when there was someting wrong in the xml file but here it seems fine.
ForecastAdapter.kt
package com.example.zach.weatherapp.Adapter
import ...
class ForecastAdapter(var myDataset: List<City>) :
RecyclerView.Adapter<ForecastAdapter.ForecastViewHolder>() {
var context:Context?=null
// Provide a reference to the views for each data item
// Complex data items may need more than one view per item, and
// you provide access to all the views for a data item in a view holder.
class ForecastViewHolder(var binding: CityListItemBinding) : RecyclerView.ViewHolder(binding.root){
fun bind(city: City){
binding.city = city
}
}
// Create new views (invoked by the layout manager)
override fun onCreateViewHolder(parent: ViewGroup,
viewType: Int): ForecastAdapter.ForecastViewHolder {
context = parent.context
val layoutIdForListItem = R.layout.city_list_item
val inflater = LayoutInflater.from(context)
val shouldAttachToParentImmediately = false
val binding = DataBindingUtil.inflate<CityListItemBinding>(inflater,layoutIdForListItem,parent,shouldAttachToParentImmediately)
//val view = inflater.inflate(layoutIdForListItem, parent, shouldAttachToParentImmediately)
return ForecastViewHolder(binding)
}
// Replace the contents of a view (invoked by the layout manager)
override fun onBindViewHolder(holder: ForecastViewHolder, position: Int) {
val city = myDataset[position]
holder.bind(city)
Glide.with(context)
.load("http://openweathermap.org/img/w/${city.weather[0].icon}.png")
.into(holder.binding.forceastImageView)
holder.binding.container.setOnClickListener{ view: View ->
Timber.d("Clicked on city %s",city.name)
Navigation.findNavController(view).navigate(ListFragmentDirections.actionListFragmentToForecastDetailsFragment(city.id))}
}
// Return the size of your dataset (invoked by the layout manager)
override fun getItemCount() = myDataset.size
}
city_list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="city" type="com.example.zach.weatherapp.data.City"/>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/container">
<TextView
tools:text="Caen"
android:text="#{city.name}"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="#+id/city_name_textview"
app:layout_constraintStart_toStartOf="parent" android:layout_marginStart="16dp"
android:layout_marginTop="16dp" app:layout_constraintTop_toTopOf="parent"
android:fontFamily="#font/roboto_light" android:textSize="22sp" android:textStyle="bold"
android:maxLines="1" android:ellipsize="end"/>
<TextView
tools:text="Sunny"
android:text="#{city.weather[0].description}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/city_forecast_textview" app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="16dp"
app:layout_constraintTop_toBottomOf="#+id/city_name_textview" android:fontFamily="#font/roboto_light"
android:layout_marginBottom="16dp" app:layout_constraintBottom_toBottomOf="parent"/>
<ImageView
android:layout_width="48dp"
android:layout_height="48dp" app:srcCompat="#drawable/sunny"
android:id="#+id/forceast_imageView" android:layout_marginTop="8dp"
app:layout_constraintTop_toTopOf="parent" android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintVertical_bias="0.562"
android:layout_marginEnd="32dp" app:layout_constraintEnd_toStartOf="#+id/temperatures_layout"/>
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="#+id/temperatures_layout"
app:layout_constraintEnd_toEndOf="parent" android:layout_marginEnd="16dp" android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent" android:layout_marginTop="8dp"
app:layout_constraintTop_toTopOf="parent">
<TextView
tools:text="15°"
android:text="#{city.main.temp_max}"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="#+id/max_temperature_textview"
android:fontFamily="#font/roboto_light"
tools:layout_editor_absoluteY="17dp" tools:layout_editor_absoluteX="313dp"/>
<TextView
tools:text="9°"
android:text="#{city.main.temp_min}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/min_temperature_textview"
android:fontFamily="#font/roboto_light" tools:layout_editor_absoluteY="45dp"
tools:layout_editor_absoluteX="321dp" android:layout_gravity="right"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
It might just be an Android Studio error because the xml file seems fine.
UPDATE :
Error seems to come from Xml. I removed the android:text="#{city.xxx}" in my xml layout and instead updated my textViews manually in my ViewHolder bind method like so :
fun bind(boundCity: City){
with(binding){
cityNameTextview.text = boundCity.name
cityForecastTextview.text = boundCity.weather[0].description
maxTemperatureTextview.text = "${boundCity.main.temp_max}°"
minTemperatureTextview.text = "${boundCity.main.temp_min}°"
Glide.with(root.context)
.load("http://openweathermap.org/img/w/${boundCity.weather[0].icon}.png")
.into(forceastImageView)
container.setOnClickListener{ view: View ->
Timber.d("Clicked on city %s",boundCity.name)
Navigation.findNavController(view).navigate(ListFragmentDirections.actionListFragmentToForecastDetailsFragment(boundCity.id))}
}
}
And I no longer get the error. The error comes whenever I add android:text="#{city.xx}" in my textviews and bind the city variable in the bind method. I still don't know why though....
This should work for you I believe;
class ForecastAdapter(private var myDataset: List<City>) : RecyclerView.Adapter<ForecastAdapter.ForecastViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ForecastAdapter.ForecastViewHolder {
val itemBinding = CityListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ForecastViewHolder(itemBinding)
}
override fun onBindViewHolder(holder: ForecastViewHolder, position: Int) {
val city = myDataset[position]
holder.bind(city)
}
override fun getItemCount() = myDataset.size
inner class ForecastViewHolder(var binding: CityListItemBinding) : RecyclerView.ViewHolder(binding.root){
fun bind(boundCity: City){
with(binding) {
city = boundCity
Glide.with(root.context)
.load("http://openweathermap.org/img/w/${city.weather[0].icon}.png")
.into(forceastImageView)
container.setOnClickListener { view ->
Timber.d("Clicked on city %s", city.name)
Navigation.findNavController(view).navigate(ListFragmentDirections.actionListFragmentToForecastDetailsFragment(city.id))
}
}
}
Hey you could try adding the following line after the <data> tag :
<import type="android.view.View" />
I found that worked for me when I had that error.

Categories

Resources