lateinit property pokemon has not been initialized - android

Could someone help me with this error?
class PokemonAdapter(
private val context: Context,
private val pokemons: MutableList<PokemonItem> = mutableListOf(),
var onItemClickListener: (pokemon: PokemonItem) -> Unit = {}
) : RecyclerView.Adapter<PokemonAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PokemonAdapter.ViewHolder {
val viewCriada = LayoutInflater.from(context).inflate(
R.layout.item_pokemon,
parent,
false
)
return ViewHolder(viewCriada)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.vincula(pokemons[position])
}
override fun getItemCount() = pokemons.size
fun add(pokemons: List<PokemonItem>) {
this.pokemons.clear()
this.pokemons.addAll(pokemons)
notifyDataSetChanged()
}
inner class ViewHolder(itemView: View) :
RecyclerView.ViewHolder(itemView) {
private lateinit var pokemon: PokemonItem
private val campoNome by lazy { itemView.item_pokemon_nome }
fun vincula(pokemonItem: PokemonItem) {
this.pokemon = pokemon
campoNome.text = pokemonItem.nome
}
}
}

Here it appears you're setting pokemon to itself but it has not actually been set yet:
fun vincula(pokemonItem: PokemonItem) {
this.pokemon = pokemon // < -- This line
campoNome.text = pokemonItem.nome
}
Perhaps you meant this:
fun vincula(pokemonItem: PokemonItem) {
this.pokemon = pokemonItem
campoNome.text = pokemonItem.nome
}

the problem is in the function vincula
this.pokemon = pokemonItem

Related

How to bind two xml in one class in android?

I'm new to Kotlin. I have a MainActivity and a list view inside.
When an item is clicked in the list view, I want to update a textView inside MainActivity.
However because data should be passed from ListView to MainActivity, I did something weird:
MainActivity.kt:
data class Country(val imgRes:Int, val name:String)
class MainActivity : AppCompatActivity() {
private lateinit var binding : ActivityMainBinding
private val data = arrayListOf<Country>()
val imgRes = intArrayOf()
val data1 = arrayOf()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
for(i in imgRes.indices) {
val country = Country(imgRes[i], data1[i])
data.add(country)
}
val adapter = RecyclerAdapter(data, object:RecyclerAdapter.OnItemClickListener {
override fun onItemClick(v: View, pos: Int) {
binding.textView.text = (v as TextView).text
}
})
binding.recycler1.adapter = adapter
binding.recycler1.layoutManager = StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)
}
}
ListView.kt:
class RecyclerAdapter(private val dataSet:List<Country>, private val listener:OnItemClickListener) : RecyclerView.Adapter<RecyclerAdapter.ViewHolder>() {
public interface OnItemClickListener {
fun onItemClick(v:View, pos:Int)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding = RowBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(binding)
}
override fun getItemCount(): Int {
return dataSet.size
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(position)
}
inner class ViewHolder(val binding: RowBinding) : RecyclerView.ViewHolder(binding.root) {
val rowImageView = binding.rowImageView
val rowTextView = binding.rowTextView
init {
binding.root.setOnClickListener {
listener.onItemClick(rowTextView, adapterPosition)
}
}
fun bind(pos: Int) {
with(binding) {
rowImageView.setImageResource(dataSet[pos].imgRes)
rowTextView.text = dataSet[pos].name
}
}
}
}
I declared interface(OnItemClickListener) at ListView.kt and redefine it at MainActivity.kt.
It works fine but is there any better way to do this?
You can use higher order functions instead of interface.
Define this function top of your Adapter:
var onClick: ((text: String) -> Unit)? = null
in ViewHolder:
binding.root.setOnClickListener {
onClick?.invoke(dataSet[position].text)
}
in MainActivity:
adapter.onClick = { name ->
binding.textView.text = name
}
You can also find more information about higher orders in here
<include android:layout_width="match_parent"
android:layout_height="wrap_content"
layout="#layout/yourlayout" />
Try this in your Adapter:
var itemClickListener: ((position: Int, name: String) -> Unit)? = null
Bindviewholder:
itemClickListner.invoke(1,"anyvalue")
in Activity:
adapter.itemClickListener = {
position, name ->
Toast.makeText(requireContext(),"position is $position name is $name ",Toast.LENGTH_SHORT).show()
}
in Adapter:
private var mListener: OnItemClickListener,
interface OnItemClickListener {
fun onClick(position: Int, routerArray: ArrayList<Router>)
}
binding.yourView.setOnClickListener {
mListener.onClick(adapterPosition, routerArray)
}
in Activity:
override fun onClick(position: Int, yourArray: ArrayList<Model>) {
binding.textView.text = yourArray[position].text
}

Android: Values are not assigned when using data binding in my custom adapter

I'm trying to pass my binding variable for the adapter to the ViewHolder to assing values to the textviews in my adapter but the values are not assigned and the click listeners dont do anything.
Here's my adapter class:
class DoneAppointmentsAdapter(var context: DoneAppointmentsFragment, listener: ContentListener, var arrayList: List<Appointment>) :
RecyclerView.Adapter<DoneAppointmentsAdapter.ItemHolder>() {
private val listener: ContentListener = listener
private var binding: DoneAppointmentsAdapterBinding? = null
var activity = context
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): ItemHolder {
val viewHolder = LayoutInflater.from(parent.context)
.inflate(R.layout.done_appointments_adapter, parent, false)
binding = DoneAppointmentsAdapterBinding.inflate(LayoutInflater.from(parent.context))
return ItemHolder(viewHolder,binding, parent.context)
}
override fun onBindViewHolder(holder: ItemHolder, position: Int) {
holder.bind(arrayList, listener)
}
override fun getItemCount(): Int {
return arrayList.size
}
class ItemHolder(view: View, var binding: DoneAppointmentsAdapterBinding?, val context: Context) : RecyclerView.ViewHolder(view) {
fun bind(listOfData: List<Appointment>, listener: ContentListener) {
val dataListItem = listOfData[adapterPosition]
binding?.donePatientItemName?.text = "${dataListItem.patientName}"
binding?.donePatientItemTime?.text = "Some date"
binding?.donePatientItemPatientInfo?.setOnClickListener {
tempAppointmentId = dataListItem.id
listener.onPatientHistoryViewClicked(dataListItem.requestedBy)
}
binding?.donePatientItemReviewCase?.setOnClickListener {
amendPrescriptionWarning(dataListItem,context)
}
binding.donePatientItemViewCase.setOnClickListener {
startPreview(dataListItem)
}
}
}
}
You have messed up inside onCreateViewHolder . you have created view twice once with the LayoutInflater.from(parent.context) and once with DoneAppointmentsAdapterBinding.inflate . There should be only one in this case onlyDoneAppointmentsAdapterBinding .
class DoneAppointmentsAdapter(var context: DoneAppointmentsFragment, listener: ContentListener, var arrayList: List<Appointment>) :
RecyclerView.Adapter<DoneAppointmentsAdapter.ItemHolder>() {
private val listener: ContentListener = listener
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): ItemHolder {
val binding = DoneAppointmentsAdapterBinding.inflate(LayoutInflater.from(parent.context))
return ItemHolder(binding)
}
override fun onBindViewHolder(holder: ItemHolder, position: Int) {
holder.bind(arrayList, listener)
}
override fun getItemCount(): Int {
return arrayList.size
}
class ItemHolder (var binding: DoneAppointmentsAdapterBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(listOfData: List<Appointment>, listener: ContentListener) {
val dataListItem = listOfData[adapterPosition]
binding.donePatientItemName.text = "${dataListItem.patientName}"
binding.donePatientItemTime.text = "Some date"
binding.donePatientItemPatientInfo.setOnClickListener {
tempAppointmentId = dataListItem.id
listener.onPatientHistoryViewClicked(dataListItem.requestedBy)
}
binding.donePatientItemReviewCase.setOnClickListener {
amendPrescriptionWarning(dataListItem,binding.donePatientItemReviewCase.context)
}
binding.donePatientItemViewCase.setOnClickListener {
startPreview(dataListItem)
}
}
}
}
It should be like this . I have also removed extra useless arguments from ItemHolder .
To make it clear the problem with your code was RecyclerView.ViewHolder(view) you were passing view while you were doing all action and stuff on binding those holds are two different instances of View . Which should be fixed with above code.

OnClickListener Button inside the Adapter Class Crashing the App in Kotlin

I am trying to store the values from retrofit into a database by pressing a AddtoCartButton but app is getting crashed when I press this button. I am able to retrieve the result from retrofit without any issues but onclicklistener is the issue.
Code:
inner class MoviesAdapter : RecyclerView.Adapter<MoviesAdapter.MovieViewHolder>() {
private val movies: MutableList<Movie> = mutableListOf()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MovieViewHolder {
return MovieViewHolder(layoutInflater.inflate(R.layout.item_movie_layout, parent, false))
}
override fun getItemCount(): Int {
return movies.size
}
override fun onBindViewHolder(holder: MovieViewHolder, position: Int) {
holder.bindModel(movies[position])
}
fun setMovies(data: List<Movie>) {
movies.addAll(data)
notifyDataSetChanged()
}
inner class MovieViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val movieGenreTxt : TextView = itemView.findViewById(R.id.movieGenre)
val movieYearTxt : TextView = itemView.findViewById(R.id.movieYear)
val movieAvatarImage : ImageView = itemView.findViewById(R.id.movieAvatar)
val movieDescription: TextView =itemView.findViewById(R.id.movieDescription)
fun bindModel(movie: Movie) {
// movieTitleTxt.text = movie.name
movieGenreTxt.text = movie.menu
movieYearTxt.text = movie.price
Picasso.get().load(movie.picture).into(movieAvatarImage)
movieDescription.text=movie.description
var movieName:String= movie.name!!
var testvar=movie.name.toString()
}
}
}
fun addtocart(view: View)
{
Toast.makeText(context,"Success on Click", Toast.LENGTH_SHORT).show()
print testvar
}
Error:
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.Button.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
Below code solves the purpose.
Code:
inner class MoviesAdapter : RecyclerView.Adapter<MoviesAdapter.MovieViewHolder>() {
private val movies: MutableList<Movie> = mutableListOf()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MovieViewHolder {
return MovieViewHolder(layoutInflater.inflate(R.layout.item_movie_layout, parent, false))
}
override fun getItemCount(): Int {
return movies.size
}
override fun onBindViewHolder(holder: MovieViewHolder, position: Int) {
holder.bindModel(movies[position])
}
fun setMovies(data: List<Movie>) {
movies.addAll(data)
notifyDataSetChanged()
}
inner class MovieViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val movieGenreTxt : TextView = itemView.findViewById(R.id.movieGenre)
val movieYearTxt : TextView = itemView.findViewById(R.id.movieYear)
val movieAvatarImage : ImageView = itemView.findViewById(R.id.movieAvatar)
val movieDescription: TextView =itemView.findViewById(R.id.movieDescription)
fun bindModel(movie: Movie) {
// movieTitleTxt.text = movie.name
movieGenreTxt.text = movie.menu
movieYearTxt.text = movie.price
Picasso.get().load(movie.picture).into(movieAvatarImage)
movieDescription.text=movie.description
var movieName:String= movie.name!!
var testvar=movie.name.toString()
val addtocart = itemView.findViewById<Button>(R.id.idCart)
addtocart!!.setOnClickListener {
print $testvar
Toast.makeText(context,"Success on Click", Toast.LENGTH_SHORT).show()
startActivity(intent) }
}
}
}

How to Retrieve a variable from a Inner Class to a function in Kotlin

I am trying to retrieve a testvar value from inner class Adapter to addtocart function but I am not getting the value. I am able to retrieve value inside Adapter class but not outside the class.
Code:
inner class MoviesAdapter : RecyclerView.Adapter<MoviesAdapter.MovieViewHolder>() {
private val movies: MutableList<Movie> = mutableListOf()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MovieViewHolder {
return MovieViewHolder(layoutInflater.inflate(R.layout.item_movie_layout, parent, false))
}
override fun getItemCount(): Int {
return movies.size
}
override fun onBindViewHolder(holder: MovieViewHolder, position: Int) {
holder.bindModel(movies[position])
}
fun setMovies(data: List<Movie>) {
movies.addAll(data)
notifyDataSetChanged()
}
inner class MovieViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val movieGenreTxt : TextView = itemView.findViewById(R.id.movieGenre)
val movieYearTxt : TextView = itemView.findViewById(R.id.movieYear)
val movieAvatarImage : ImageView = itemView.findViewById(R.id.movieAvatar)
val movieDescription: TextView =itemView.findViewById(R.id.movieDescription)
fun bindModel(movie: Movie) {
// movieTitleTxt.text = movie.name
movieGenreTxt.text = movie.menu
movieYearTxt.text = movie.price
Picasso.get().load(movie.picture).into(movieAvatarImage)
movieDescription.text=movie.description
var movieName:String= movie.name!!
var testvar=movie.name.toString()
}
}
}
fun addtocart(view: View)
{
Toast.makeText(context,"Success on Click", Toast.LENGTH_SHORT).show()
print testvar
}
Think you are looking to put a button in the RecyclerView.
In that case setOnclicklistener within the
inner class MovieViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val addtocart = itemView.findViewById<Button>(R.id.viewprofile)
addtocart!!.setOnClickListener {
}}

Item Onclick RecyclerView Kotlin Android

I have RecyclerView adapter in Kotlin and when a user clicks on categoryPhoto, I want to open a new activity. How should I implement this?
class CategoryAdapter(private val categoryList: List<Category>, private val context: Context) : RecyclerView.Adapter<CategoryAdapter.MyViewHolder>() {
class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) {
var categoryName = view.text_view_category_name
var categoryPhoto = view.image_view_category
var cardView = view.card_view_category
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = MyViewHolder(parent.inflate(R.layout.category_list_row))
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val category = categoryList[position]
// Set height of cardview based on screen width
val displayMetrics = context.resources.displayMetrics
val finalHeight = displayMetrics.widthPixels / 2
holder.cardView.layoutParams.height = finalHeight
holder.categoryName.text = category.oc
holder.categoryPhoto.loadUrl(category.icon)
}
override fun getItemCount(): Int {
return categoryList.size
}}
Just add click listener as parameter to constructor of your adapter.
class CategoryAdapter(
private val categoryList: List<Category>,
private val context: Context,
private val onClickListener: (View, Category) -> Unit
) : RecyclerView.Adapter<CategoryAdapter.MyViewHolder>() {
...
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val category = categoryList[position]
// Set height of cardview based on screen width
...
holder.itemView.setOnClickListener { view ->
onClickListener.invoke(view, category)
}
}
...
}
Then you can use as following:
fun initList() {
adapter = CategoryAdapter(
categoryList = ...,
context = ...,
onClickListener = { view, category -> openActivity(view, category) }
}
Off-top. Some optional improvements for code above
Create typealias for lambda. Make your code more readable.
typealias MyCategoryClickListener = (View, Category) -> Unit
class CategoryAdapter(
private val categoryList: List<Category>,
private val context: Context,
private val onClickListener: MyCategoryClickListener
) : RecyclerView.Adapter<CategoryAdapter.MyViewHolder>() {
Omit invoke call of listener. Lambda can be called just like function.
holder.itemView.setOnClickListener { view ->
onClickListener(view, category)
}
Replace lambda with reference when creating adapter
fun initList() {
adapter = CategoryAdapter(
categoryList = ...,
context = ...,
onClickListener = this::openActivity)
}
fun openActivity(view: View, category: Category) {
...
}
You can do it in your onBindViewHolder(...)
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val category = categoryList[position]
// Set height of cardview based on screen width
val displayMetrics = context.resources.displayMetrics
val finalHeight = displayMetrics.widthPixels / 2
holder.cardView.layoutParams.height = finalHeight
holder.categoryName.text = category.oc
holder.categoryPhoto.loadUrl(category.icon)
holder.categoryPhoto.setOnClickListener { view ->
// categoryPhoto clicked.
// start your activity here
}
}
Do like this
class RecyclerListAdapter: RecyclerView.Adapter {
var context: Context? = null
var listData: ArrayList? = null
Step 1: Activity ref..................................
var activityref:MainActivity?=null
constructor(context: Context?, listData: ArrayList<ItemDetails>?, activityref: MainActivity?) : super() {
this.context = context
this.listData = listData
this.activityref = activityref
}
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewsHolder {
val view = LayoutInflater.from(context).inflate(R.layout.row_list, parent, false)
return ViewsHolder(view)
}
override fun getItemCount(): Int {
return listData!!.size
}
override fun onBindViewHolder(holder: ViewsHolder?, position: Int) {
holder?.item=listData?.get(position)
holder!!.first!!.setText(holder.item?.First)
holder.second!!.setText(holder.item?.Second)
holder.third!!.setText(holder.item?.Third)
Step 2 OnClick on item.....................
holder.third!!.setOnClickListener{
activityref?.OnItemClicked(holder.item!!)
}
}
class ViewsHolder(itemView: View?) : RecyclerView.ViewHolder(itemView) {
var item:ItemDetails?=null
var first: TextView? = null;
var second: TextView? = null;
var third: TextView? = null;
init {
first = itemView?.findViewById(R.id.first)
second = itemView?.findViewById(R.id.second)
third = itemView?.findViewById(R.id.third)
}
}
}

Categories

Resources