I'm trying to create a RecycleView of EditText that update on the spot as you add EditText with a Button.
For reference, I'd like to have something that works like a checkbox question editing on a Gform , but only with EditTexts .
Here's my code :
Main Activity Code
class AjoutObjectifActivity : AppCompatActivity() {
//ArrayList used to add items to the RecyclerView
private var etapes: ArrayList<String> = ArrayList()
private lateinit var binding: ActivityAjoutObjectifBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityAjoutObjectifBinding.inflate(layoutInflater)
setContentView(binding.root)
setupButtons()
}
//The click listener of my button
private fun setupButtons() {
binding.buttonAddEtape.setOnClickListener{
addEtape()
}
}
//The function that add one element to my ArrayList, and is supposed to reload my RecyclerView to add the corresponding EditText
//I know it is supposed to erase all the EditTexts contents as well, but I'm looking to repair this after I manage to add EditTexts
private fun addEtape() {
etapes.add("")
val adapterEtapes = EtapesAdapter(etapes)
binding.recyclerViewObjectifsEtapes.adapter = adapterEtapes
}
}
My "EtapesAdapter" Code
class EtapesAdapter(private val etapes: ArrayList<String>) : RecyclerView.Adapter<EtapesAdapter.EtapeViewHolder>() {
override fun getItemCount(): Int = etapes.size
override fun onCreateViewHolder(parent: ViewGroup, position: Int): EtapeViewHolder {
val view: View = LayoutInflater.from(parent.context)
.inflate(R.layout.item_etapes_input, parent, false)
return EtapeViewHolder(view)
}
override fun onBindViewHolder(holder: EtapeViewHolder, position: Int) {
holder.bind(etapes[position])
}
class EtapeViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val etape : TextView = itemView.findViewById(R.id.edit_text_etapes_objectif)
fun bind(etapes: String) = with(itemView){
etape.text = etapes
}
}
}
My Main Layout
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler_view_objectifs_etapes"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</ScrollView>
My Item Layout
<EditText xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/edit_text_etapes_objectif"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp" />
After a quick check of the varaibles with the debbuger, it seems that my ArrayList works perfectly and grows as you press the button, so I guess the error must be somewhere in the adapter.
I tried to do it with an Array first, but since you can't add size to it, I decided to use the ArrayList instead.
Remember that I'm kind of a beginner, so maybe I use the wrong type of variables or view, in that case, feel free to tell me !
Thanks in advance
Thanks Menno, this code sure was helpful.
Your "addEtape" function reinitialised the ArrayList completly, so I replaced it by puting "etapes.add("")", instead of calling "addEtape" in mainActivity
Here is the final code ;).
Main Activity
class AjoutObjectifActivity : AppCompatActivity() {
private var etapes: ArrayList<String> = ArrayList()
private lateinit var binding: ActivityAjoutObjectifBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityAjoutObjectifBinding.inflate(layoutInflater)
setContentView(binding.root)
setupButtons()
}
private fun setupButtons() {
binding.buttonAddEtape.setOnClickListener{
addEtape()
}
}
private fun addEtape() {
val adapterEtapes = EtapesAdapter()
etapes.add("")
binding.recyclerViewObjectifsEtapes.adapter = adapterEtapes
binding.recyclerViewObjectifsEtapes.layoutManager =
LinearLayoutManager(this)
adapterEtapes.addEtapes(etapes)
}
}
The adapter
class EtapesAdapter : RecyclerView.Adapter<EtapesAdapter.EtapeViewHolder>() {
private val etapes = ArrayList<String>()
fun addEtapes(etapes: ArrayList<String>) {
this.etapes.addAll(etapes)
notifyDataSetChanged()
}
override fun getItemCount(): Int = etapes.size
override fun onCreateViewHolder(parent: ViewGroup, position: Int): EtapeViewHolder {
val view: View = LayoutInflater.from(parent.context)
.inflate(R.layout.item_etapes_input, parent, false)
return EtapeViewHolder(view)
}
override fun onBindViewHolder(holder: EtapeViewHolder, position: Int) {
holder.bind(etapes[position])
}
class EtapeViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val etape : TextView = itemView.findViewById(R.id.edit_text_etapes_objectif)
fun bind(etapes: String) = with(itemView){
etape.text = etapes
}
}
}
It works better if you keep track of the 'etapes' in the adapter itself, like this.
class EtapesAdapter : RecyclerView.Adapter<EtapesAdapter.EtapeViewHolder>() {
private val etapes = ArrayList<String>()
fun addEtape(etape: String) {
this.etapes.add(etape)
notifyDataSetChanged()
}
fun addEtapes(etapes: ArrayList<String>) {
this.etapes.addAll(etapes)
notifyDataSetChanged()
}
override fun getItemCount(): Int = etapes.size
override fun onCreateViewHolder(parent: ViewGroup, position: Int): EtapeViewHolder {
val view: View = LayoutInflater.from(parent.context)
.inflate(R.layout.item_etapes_input, parent, false)
return EtapeViewHolder(view)
}
override fun onBindViewHolder(holder: EtapeViewHolder, position: Int) {
holder.bind(etapes[position])
}
class EtapeViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val etape : TextView = itemView.findViewById(R.id.edit_text_etapes_objectif)
fun bind(etapes: String) = with(itemView){
etape.text = etapes
}
}
}
So now you can do this:
val adapterEtapes = EtapesAdapter()
binding.recyclerViewObjectifsEtapes.adapter = adapterEtapes
binding.recyclerViewObjectifsEtapes.layoutManager = LinearLayoutManager()
adapterEtapes.addEtapes(etapes)
and it should reload the list.
Related
I implemented a simple RecyclerView example, with multiple items selection possibility, using the selection library.
Here's my code:
class MainActivity : AppCompatActivity() {
private var listOfItems = ArrayList<Item>()
private var selectionTracker: SelectionTracker<Long>? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val recyclerView = findViewById<RecyclerView>(R.id.rvMainRecyclerView)
for (i in 0..32) {
listOfItems.add(Item(i, "Item $i"))
}
val recyclerViewAdapter = RecyclerViewAdapter(listOfItems)
recyclerView.adapter = recyclerViewAdapter
selectionTracker = SelectionTracker.Builder<Long>(
"Itemselection",
recyclerView,
MyItemKeyProvider(recyclerView),
MyItemDetailsLookup(recyclerView),
StorageStrategy.createLongStorage()
).build()
selectionTracker?.addObserver(
object: SelectionTracker.SelectionObserver<Long>() {
override fun onSelectionChanged() {
super.onSelectionChanged()
val itemsSelected = selectionTracker?.selection?.size()
Log.d("MainActivity", "$itemsSelected items selected")
}
}
)
recyclerViewAdapter.tracker = selectionTracker
}
}
class RecyclerViewAdapter(
val dataSet: ArrayList\<Item\>
): RecyclerView.Adapter\<RecyclerViewAdapter.ViewHolder\>() {
var tracker: SelectionTracker<Long>? = null
init {
setHasStableIds(true)
}
inner class ViewHolder(view: View): RecyclerView.ViewHolder(view) {
val textView: TextView
init {
textView = view.findViewById(R.id.tvRecyclerViewItemTitle)
}
fun getItemDetails(): ItemDetails<Long> {
return object : ItemDetails<Long>() {
override fun getPosition(): Int {
return adapterPosition
}
override fun getSelectionKey(): Long? {
return itemId
}
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(LayoutInflater
.from(parent.context).inflate(R.layout.recycler_view_item, parent, false))
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val currentItem = dataSet[position]
holder.textView.text = currentItem.value
holder.itemView.isActivated = tracker!!.isSelected(position.toLong())
}
override fun getItemCount(): Int {
return dataSet.size
}
override fun getItemId(position: Int): Long {
return dataSet[position].id.toLong()
}
}
I'm curious why the onSelectionChanged (in MainActivity selectionTracker?.addObserver...) is triggered several times while I hold an item to start selection mode?
To be more precise, this is the use case:
Nothing is selected,
Long click on any item => selection mode activated; the item is selected
Here, "1 items selected" is being printed out as long as I hold the first item.
I am trying to work wih courutines and recycler view. I've done everything necessary to send requests to the API, but I still can't get the list that should be inside the recycler view. I am using fragment to create list and bottom nav. When I go to the fragment where my list should be, then I get the error: RecyclerView: No layout manager attached; skipping layoutand nothing more. I googled about this error and it says I should define the layout manager in xml, but it alreadt has layuout manager in my fragment
<androidx.recyclerview.widget.RecyclerView 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:id="#+id/list_item"
app:layoutManager="LinearLayoutManager"
Also my adapter and fragment for recycler view look like this:
class MyItemRecyclerViewAdapter(
) : RecyclerView.Adapter<MyItemRecyclerViewAdapter.ViewHolder>() {
var userNameResponse = mutableListOf<UsersNameItem>()
fun setNamesList(names: List<UsersNameItem>) {
this.userNameResponse = userNameResponse.toMutableList()
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
FragmentItemBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = userNameResponse[position]
holder.idView.text = item.id
holder.contentView.text = item.name
}
override fun getItemCount(): Int = userNameResponse.size
class ViewHolder(binding: FragmentItemBinding) : RecyclerView.ViewHolder(binding.root) {
val idView: TextView = binding.itemNumber
val contentView: TextView = binding.content
}
}
Fragment:
class ItemFragment : Fragment() {
private var layoutManager: RecyclerView.LayoutManager? = null
private var adapter: RecyclerView.Adapter<MyItemRecyclerViewAdapter.ViewHolder>? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_item_list, container, false)
}
override fun onViewCreated(itemView: View, savedInstanceState: Bundle?) {
super.onViewCreated(itemView, savedInstanceState)
layoutInflater.apply {
layoutManager = LinearLayoutManager(activity)
adapter = MyItemRecyclerViewAdapter()
}
}
companion object {
fun newInstance() = ItemFragment()
}
}
And MainActivity where I set up courutines and trying to make a request:
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
lateinit var nameviewModel: ListNamesViewModel
val adapter = MyItemRecyclerViewAdapter()
///trying to create http-request for lists
val retrofitService = BasicApiService.getInstance()
val mainRepository = MainRepository(retrofitService)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
nameviewModel = ViewModelProvider(
this,
MyViewModelFactory(mainRepository)
)[ListNamesViewModel::class.java]
nameviewModel.userName.observe(
this, Observer {
adapter.setNamesList(it)
})
nameviewModel.message.observe(this) {
Toast.makeText(this, it, Toast.LENGTH_SHORT).show()
}
nameviewModel.loadUsers()
val navView: BottomNavigationView = binding.navView
val navController = findNavController(R.id.nav_host_fragment_activity_main)
val appBarConfiguration = AppBarConfiguration(
setOf(
R.id.navigation_home,
R.id.navigation_dashboard,
R.id.navigation_notifications,
R.id.list_name_navig
)
)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
}
}
Also I wanted to ask (I am very new to Kotlin nad just trying to make things work) if it is a good practice to leave request to API in the MainActivity ot I should do it in another way?
I see some confusion in your code, try to apply the next:
Why are you creating the layoutManager and adapter in the ItemFragment?
override fun onViewCreated(itemView: View, savedInstanceState: Bundle?) {
super.onViewCreated(itemView, savedInstanceState)
layoutInflater.apply {
layoutManager = LinearLayoutManager(activity)
adapter = MyItemRecyclerViewAdapter()
}
}
You need to move it and RecyclerView to your activity.
The using layoutInflater.apply {} doesn't make sense, it's doing nothing in your case. You need to set layoutManager and adapter to recyclerView then in the activity it should look like that (and rename list_item to recyclerView)
binding.recyclerView.apply {
layoutManager = LinearLayoutManager(this)
adapter = MyItemRecyclerViewAdapter()
}
Remove app:layoutManager="LinearLayoutManager" from XML.
It looks like you don't need to use ItemFragment for your purpose because you use it just like a view and binding in the ViewHolder
class ViewHolder(binding: FragmentItemBinding) : RecyclerView.ViewHolder(binding.root) {
val idView: TextView = binding.itemNumber
val contentView: TextView = binding.content
}
try changing in xml
app:layoutManager="LinearLayoutManager"
to
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
I am making a simple application. There are 2 screens in the application: List screen, Add screen.
Scenario: When the application is opened, there is a list page on the screen. The user goes to the Adding page with the help of the Floating Action Button on the list page. Here, he enters the items with the help of Edit Text and comes to the List page by pressing the Add Button.
I am using MVVM in this application and I am a beginner. So I can't find the problem. Here is the problem: I am adding new elements to my list in the Add Fragment, but these elements are not seems in the List Fragment.
Thanks in advance.
Model Class
data class Movie(
val movieName: String,
val releaseDate: String
)
ViewModel class
class MovieViewModel : ViewModel() {
val movies = MutableLiveData<ArrayList<Movie>>()
var movieList = arrayListOf<Movie>()
fun addMovie(movie: Movie) {
movieList.add(movie)
movies.value = movieList
}
}
Adapter Class
class MovieAdapter : RecyclerView.Adapter<MovieAdapter.MyViewHolder>() {
private var movieList = emptyList<Movie>()
class MyViewHolder(private val binding: RowItemBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(movie: Movie) {
binding.textViewMovieName.text = movie.movieName
binding.textViewReleaseDate.text = movie.releaseDate
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding = RowItemBinding.inflate(layoutInflater, parent, false)
return MyViewHolder(binding)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val currentItem = movieList[position]
holder.bind(currentItem)
}
override fun getItemCount(): Int {
return movieList.size
}
fun setData(movie: List<Movie>) {
val movieDiffUtil = MovieDiffUtil(movieList, movie)
val movieDiffResult = DiffUtil.calculateDiff(movieDiffUtil)
this.movieList = movie
movieDiffResult.dispatchUpdatesTo(this)
}
}
Add Fragment
class AddFragment : Fragment() {
private var _binding: FragmentAddBinding? = null
private val binding get() = _binding!!
private val movieViewModel: MovieViewModel by viewModels<MovieViewModel>()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentAddBinding.inflate(inflater, container, false)
binding.buttonAdd.setOnClickListener {
insertData()
}
return binding.root
}
private fun insertData() {
val movieName = binding.editTextMovieName.text.toString()
val releaseDate = binding.editTextReleaseDate.text.toString()
val movie = Movie(movieName, releaseDate)
movieViewModel.addMovie(movie)
findNavController().navigate(R.id.action_addFragment_to_listFragment)
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
List Fragment
class ListFragment : Fragment() {
private var _binding: FragmentListBinding? = null
private val binding get() = _binding!!
private val adapter: MovieAdapter by lazy { MovieAdapter() }
private val movieViewModel: MovieViewModel by viewModels<MovieViewModel>()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentListBinding.inflate(inflater, container, false)
setAdapter()
movieViewModel.movies.observe(viewLifecycleOwner, { data ->
adapter.setData(data)
binding.textView.setText(data.get(0).movieName)
})
binding.floatingActionButton.setOnClickListener {
findNavController().navigate(R.id.action_listFragment_to_addFragment)
}
return binding.root
}
private fun setAdapter() {
val recyclerView = binding.recyclerView
recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(requireActivity())
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
After adding an item to a List<> you need to notify the Adapter that the data has changed.
You can do so by calling adapter.notifyDataSetChanged().
I added my List directly into the Adapter but you can create a function like in the following stackoverflow post.
How to update my Recyclerview using kotlin android?
So I've been wrestling with this for days and I need some help. I've made this code work in an activity, but then I move it to a fragment it doesn't work. Everything else is the same between the two.
Using the debugger with the working Activity, the line
apiService = retrofit.create<HomeJsonApiService>(HomeJsonApiService::class.java)
goes to getItemCount(). However in the fragment it goes directly to onCreateView in the Fragment. I've attached my code below. Thanks in advance for the help! And be gentle. I'm still new to this :)
First is my fragment:
class TabHomeActivity : Fragment() {
val itemList = ArrayList<HomeCards>()
lateinit var adapter: HomeCardsAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
var binding = FragmentTabHomeActivityBinding.inflate(layoutInflater)
adapter = HomeCardsAdapter()
var rv = binding.rvHomeCards
rv.adapter = adapter
loadData()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.cards_home, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
private fun loadData() {
ApiManager.getInstance().service.listHeroes()
.enqueue(object : Callback<ResponseData<List<HomeCards>>> {
override fun onResponse(
call: Call<ResponseData<List<HomeCards>>>,
response: Response<ResponseData<List<HomeCards>>>
) {
val listData: List<HomeCards> = response.body()!!.data
// updating data from network to adapter
itemList.clear()
itemList.addAll(listData)
adapter.updateData(itemList)
adapter.notifyDataSetChanged()
}
override fun onFailure(call: Call<ResponseData<List<HomeCards>>>, t: Throwable) {
}
})
}
}
The HTTP request:
data class ResponseData<T> (
val code: Int,
val data: T
)
interface HomeJsonApiService {
#GET("marvel-heroes.asp?h=2")
fun listHeroes(): retrofit2.Call<ResponseData<List<HomeCards>>>
}
class ApiManager {
private var apiService: HomeJsonApiService? = null
init {
createService()
}
val service: HomeJsonApiService get() = apiService!!
private fun createService() {
val loggingInterceptor =
HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger {
override fun log(message: String) {
Log.i("Retrofit", message)
}
})
loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
val client = OkHttpClient.Builder()
.readTimeout(30, TimeUnit.SECONDS)
.connectTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.addInterceptor(loggingInterceptor)
.build()
val retrofit: Retrofit = Retrofit.Builder()
.client(client)
.baseUrl("https://www.mywebsite.com/jsonfolder/JSON/")
.addConverterFactory(GsonConverterFactory.create())
.build()
apiService = retrofit.create(HomeJsonApiService::class.java)
}
companion object {
private var instance: ApiManager? = null
fun getInstance(): ApiManager {
return instance ?: synchronized(this) {
ApiManager().also { instance = it }
}
}
}
}
And my adapter:
class HomeCardsAdapter() : RecyclerView.Adapter<HomeCardsAdapter.ViewHolder>() {
private lateinit var itemList: List<HomeCards>
lateinit var context: Context
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
context = parent.context
val view = LayoutInflater.from(context).inflate(R.layout.cards_home, parent, false)
return ViewHolder(view)
}
override fun getItemCount(): Int {
return if (::itemList.isInitialized) itemList.size else 0
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind()
}
fun updateData(list: List<HomeCards>) {
itemList = list;
notifyDataSetChanged()
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
//var binding = ActivityMainBinding(layoutInflater(inf))
fun bind() {
val item = itemList.get(adapterPosition)
ViewHolder(itemView).itemView.findViewById<TextView>(R.id.cardHomeTitle).text = item.name
ViewHolder(itemView).itemView.findViewById<TextView>(R.id.cardHomeTitle).text = item.superheroName
Glide.with(context)
.load(item.photo)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.circleCrop()
.into(ViewHolder(itemView).itemView.findViewById<ImageView>(R.id.cardHomeIcon))
}
}
}
class HomeCards {
#SerializedName("superhero_name")
var superheroName: String = ""
var name: String = ""
var photo: String = ""
}
The main problem is:
var binding = FragmentTabHomeActivityBinding.inflate(layoutInflater)
That is inside on onCreate but onCreateView is returning another view inflater.inflate(R.layout.cards_home, container, false)
So you are applying the adapter to a recycler that is on the binding, but the view on the screen is inflated from the layout. Change it to this:
private lateinit var binding: FragmentTabHomeActivityBinding
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?): View? {
binding = FragmentTabHomeActivityBinding.inflate(layoutInflater, container, false)
return binding.root
}
And move the code from from onCreate to onViewCreated but make sure to use the lateinit binding
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
adapter = HomeCardsAdapter()
var rv = binding.rvHomeCards
rv.adapter = adapter
loadData()
}
After that there is a problem in your adapter: private lateinit var itemList: List<HomeCards> very specifically List<HomeCards>. The method notifyDataSetChanged doesn't work by changing or updating the reference of the data structure but when the collection is modified. Change it to this:
private val list = mutableListOf<HomeCards>()
override fun getItemCount(): Int {
return list.size()
}
fun updateData(list: List<HomeCards>) {
this.itemList.clear()
this.itemList.addAll(list)
notifyDataSetChanged()
}
If onResponse() gets called and provides response, verify that code updating UI is running on main/ui thread. Common source of issue when working with network (other threads).
activity?.runOnUiThread {
itemList.clear()
itemList.addAll(listData)
adapter.updateData(itemList)
adapter.notifyDataSetChanged()
}
I have code in ListBukpotAdapter, how can I get data listener.OnClick(currentItem) to parse on other UpdateActivity
Error:
kotlin.UnitializedPropertyAccessExeption: lateinit property listener has not been initialized
class ListBukpotAdapter : RecyclerView.Adapter<ListBukpotAdapter.MyViewHolder>() {
private var bukpotList = emptyList<QrResultBukpot>()
private lateinit var listener: OnAdapterListener
interface OnAdapterListener {
fun OnClick(bukpotDataParsing: QrResultBukpot)
}
class MyViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
return MyViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.custom_row_bukpot, parent, false))
}
override fun getItemCount(): Int {
return bukpotList.size
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val currentItem = bukpotList[position]
holder.itemView.txtNomorBukpot.text = currentItem.nomorBukpot
holder.itemView.txtNpwpPemotong.text = currentItem.npwpPemotong
holder.itemView.txtMasaPajak.text = currentItem.masaPajak + " / " + currentItem.tahunPajak
holder.itemView.txtMixCode.text = currentItem.mixCode
holder.itemView.rowLayoutBukpot.setOnClickListener {
listener.OnClick(currentItem)
val context = holder.itemView.context
val intent = Intent(context, UpdateBukpotActivity::class.java)
context.startActivity(intent)
}
}
fun setDataBukpot(bukpot: List<QrResultBukpot>){
this.bukpotList = bukpot
notifyDataSetChanged()
}
}
You should initilaze your listener. Your listener variable is lateinit so before you use this, you need to initialize. You can give listener as a constructor parameter from your activity or fragment and can listen interface from your activity or fragment which contains recyclerview.