No Adapter Attached RecyclerView - kotlin

I want to implement my app. My app went smoothly, but I also got an error with 2022-05-31 14:14:32.663 8626-8626/id.kotlin.belajar E/RecyclerView: No adapter attached; skipping layout; skipping layout.
E/RecyclerView: No adapter attached; skipping layout; skipping layout
Home Activity:
class HomeActivity : DaggerAppCompatActivity(), HomeView {
#Inject
lateinit var presenter: HomePresenter
private lateinit var progressBar: ProgressBar
private lateinit var recyclerView: RecyclerView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_home)
progressBar = findViewById(R.id.pb_home)
recyclerView = findViewById(R.id.rv_home)
presenter.discoverMovie()
}
override fun onShowLoading() {
progressBar.visibility = View.VISIBLE
}
override fun onHideLoading() {
progressBar.visibility = View.GONE
recyclerView.visibility = View.VISIBLE
}
override fun onResponse(results: List<Result<String>>) {
recyclerView.addItemDecoration(DividerItemDecoration(this#HomeActivity,
DividerItemDecoration.VERTICAL))
recyclerView.adapter = HomeAdapter(results)
}
override fun onFailure(t:Throwable) {
Log.e(HomeActivity::class.java.simpleName, "${t.printStackTrace()}")
}
}
Home Adapter:
class HomeAdapter (private val results: List<Result>):
RecyclerView.Adapter<HomeAdapter.HomeViewHolder>(){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HomeViewHolder {
return HomeViewHolder(
LayoutInflater
.from(parent.context).inflate(
R.layout.item_home,
parent,
false
)
)
}
override fun onBindViewHolder(holder: HomeViewHolder, position: Int){
holder.bind()
}
override fun getItemCount(): Int{
return results.count()
}
inner class HomeViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
fun bind() {
with(itemView) {
val title = findViewById<TextView>(R.id.original_title)
title.text
val overview = findViewById<TextView>(R.id.overview)
overview.text
}
}
}
}
Home Presenter:
class HomePresenter(private val view: HomeView, datasource: HomeDatasource) {
fun discoverMovie(){
view.onShowLoading()
val dataSource = Networkmodule.providesHttpAdapter(client =
OkHttpClient()).create(HomeDatasource::class.java)
dataSource.discoverMovie().enqueue(object : Callback<HomeResponse> {
override fun onResponse(call: Call<HomeResponse>, response: Response<HomeResponse>){
view.onHideLoading()
view.onResponse(((response.body()?.results ?: emptyList()) as List<Result<String>>))
}
override fun onFailure(call: Call<HomeResponse>, t:Throwable){
view.onHideLoading()
view.onFailure(t)
}
})
}
}
I need your help.

This is my .xml files:
activity_home:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".presentation.HomeActivity">
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/white"
android:elevation="4dp"
app:layout_constraintTop_toTopOf="parent"
app:title="Belajar"
app:titleTextColor="#color/black"/>
<ProgressBar
android:id="#+id/pb_home"
style="#style/Widget.AppCompat.ProgressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminate="true"
android:indeterminateTint="#color/black"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="gone" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rv_home"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="?android:attr/actionBarSize"
android:scrollbars="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:listitem="#layout/item_home" />
</androidx.constraintlayout.widget.ConstraintLayout>

I can see two major problems here :
Adapter instantiation
You do not need to re-instantiate the adapter each time you receive data.
Just instanciate it in the onCreate method with an empty list and update the list each time you have new data (don't forget to call notify method).
And attach the adapter to the recyclerView in the onCreate method as well
Exemple
In your activity you need to :
Declare and instantiate your Adapter as a class attribute.
In the onCreate method attach it to the recyclerView
On the onResponse method call a new method of your adapter called " update "
class HomeActivity : DaggerAppCompatActivity(), HomeView {
#Inject
lateinit var presenter: HomePresenter
private lateinit var progressBar: ProgressBar
private lateinit var recyclerView: RecyclerView
private val homeAdapter = HomeAdapter(emptyList())
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_home)
progressBar = findViewById(R.id.pb_home)
recyclerView = findViewById(R.id.rv_home)
recyclerView.adapter = homeAdapter
presenter.discoverMovie()
}
override fun onShowLoading() {
progressBar.visibility = View.VISIBLE
}
override fun onHideLoading() {
progressBar.visibility = View.GONE
recyclerView.visibility = View.VISIBLE
}
override fun onResponse(results: List<Result<String>>) {
recyclerView.addItemDecoration(DividerItemDecoration(this#HomeActivity,
DividerItemDecoration.VERTICAL))
homeAdapter.update(results)
}
override fun onFailure(t:Throwable) {
Log.e(HomeActivity::class.java.simpleName, "${t.printStackTrace()}")
}
}
In your adapter :
Make results a mutableList
Create the update method
Clear the results list
Add your newResults list to your list
notifyDataSetChanged to redraw the list with new values
class HomeAdapter (private val results: MutableList<Result>):
RecyclerView.Adapter<HomeAdapter.HomeViewHolder>(){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HomeViewHolder {
return HomeViewHolder(
LayoutInflater
.from(parent.context).inflate(
R.layout.item_home,
parent,
false
)
)
}
override fun onBindViewHolder(holder: HomeViewHolder, position: Int){
holder.bind()
}
override fun getItemCount(): Int{
return results.count()
}
fun update(newResults: List<Result>) {
results.apply {
clear()
addAll(newResults)
}
notifyDataSetChanged()
}
inner class HomeViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
fun bind() {
with(itemView) {
val title = findViewById<TextView>(R.id.original_title)
title.text
val overview = findViewById<TextView>(R.id.overview)
overview.text
}
}
}
}
Layout manager missing ?
We don't have your xml code so I don't know if you added the layoutManager on the xml like this (assuming you want a linearManager) :
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
You can do it programaticaly using the layoutManager attribute on the recyclerView :
recyclerView.layoutManager = LinearLayoutManager()
You can set it in the onCreate method (if it is an activity) or onViewCreated (if it's a fragment) before attaching your adapter to the recyclerView
To avoid errors like that, the best way is to setup your recyclerView as soon as possible in the activity (onCreate) or fragment (onViewCreated) lifecycle.

Related

Dynamic and unlimited RecycleView of EditText

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.

RecyclerView with coroutines, using Fragment doesn't populate data Kotlin

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"

How to implement nested paging in android kotlin?

I have a recyclerview , for i using pagin 3 with coroutine live data .
but child recyclerview also have paginate data so how can i perform this nested pagination in android kotlin.
So , I done this with pagination for child recyclerview inside recyclerview .
Firstly of all call api for fetch dates and perform api for every date at same time when date are fetching.
so structure like this...
lateinit var adapter: WeeklyAdapter
lateinit var dayAdapter: DayDetailsPagingAdapter
private fun observeData() {
viewModel.weeklyAcquiringDetails(AcquiringVM.WEEKLY, "").observe(this){
wsWithLoader(it){
binding.rvAcquiringWeeklyDetails.adapter = WeeklyAdapter(it.data!!.data, object : WeeklyAdapter.OnPerformApi{
override fun performApi(date: String, rvMonthly: RecyclerView) {
dayAdapter = DayDetailsPagingAdapter(object : DayDetailsPagingAdapter.OnClick{
override fun onClick(result: DayAcquiring.Data) {}
})
rvMonthly.adapter = dayAdapter
lifecycleScope.launchWhenCreated {
viewModel.getDayAcquiringDetails(date).collectLatest { data ->
dayAdapter.submitData(data)
}
}
}
})
}
}
}
Weekly Adapter
class WeeklyAdapter (
val list: List<WeeklyAcquiring.Data>,
val action: OnPerformApi
) : RecyclerView.Adapter<WeeklyAdapter.ViewHolder>() {
#NonNull
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(DataBindingUtil.inflate(LayoutInflater.from(parent.context), R.layout.list_monthly, parent, false))
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val result = list[position]
holder.binding.apply {
tvDate.text = changeDateFormat(result.doctorScheduleDate,"yyyy-MM-dd","E, dd MMM yyyy")
action.performApi( result.doctorScheduleDate , rvMonthly)
}
}
override fun getItemCount(): Int {
return list.size
}
inner class ViewHolder(binding: ListMonthlyBinding) : BaseViewHolder<ListMonthlyBinding>(binding) {}
interface OnPerformApi{
fun performApi(date : String, rvMonthly: RecyclerView)
}
}
list_monthly_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"
xmlns:tools="http://schemas.android.com/tools">
<data>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/tvDate"
style="#style/TVBold"
android:layout_marginTop="#dimen/_5sdp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="#dimen/_12sdp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="May 2022" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rvMonthly"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/_10sdp"
app:layout_constraintEnd_toEndOf="parent"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintStart_toStartOf="parent"
tools:listitem="#layout/list_acquiring_details"
app:layout_constraintTop_toBottomOf="#+id/tvDate" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
DayDetailsPagingAdapter
class DayDetailsPagingAdapter (private val onClick: OnClick) : PagingDataAdapter<DayAcquiring.Data, DayDetailsPagingAdapter.ViewHolder>(DiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(ListAcquiringDetailsBinding.inflate(LayoutInflater.from(parent.context), parent, false))
}
#SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val result = getItem(position)!!
holder.binding.apply {
data = result
tvName.text = result.userName
tvAmount.text = "€ ${result.amount}"
}
}
class ViewHolder(val binding: ListAcquiringDetailsBinding) : RecyclerView.ViewHolder(binding.root) {}
private class DiffCallback : DiffUtil.ItemCallback<DayAcquiring.Data>() {
override fun areItemsTheSame(
oldItem: DayAcquiring.Data,
newItem: DayAcquiring.Data
): Boolean = oldItem == newItem
override fun areContentsTheSame(
oldItem: DayAcquiring.Data,
newItem: DayAcquiring.Data
): Boolean = oldItem == newItem
}
interface OnClick {
fun onClick(result: DayAcquiring.Data )
}
}
**in viewmodel **
fun getDayAcquiringDetails( date: String ) = apiHelper.getDayAcquiringDetails( date ).cachedIn(viewModelScope)
fun weeklyAcquiringDetails( filter: String , date: String) = apiHelper.weeklyAcquiringDetails( filter , date)
hope you like this .

How to write the adapter class for the following recycler view?

I am trying to make a Todo app and I have done the Room part and able to store data now I want to display the data in the form of Recycler View but I am not getting how to write the adapter class corresponding to it. I looked for it in different sites I never got any satisfying answer.
**TodoFragViewModel.kt""
class TodofragViewModel(
val database: TodoDao, applicaltion: Application
): AndroidViewModel(applicaltion) {
// TODO: Implement the ViewModel
/**
* viewModelJob allows us to cancel all coroutines started by this ViewModel.
*/
private var viewModelJob = Job()
/**All coroutines can be cancelled by viewmodelJob.cancel() and Dispatcher.main is byDefault choice
*/
private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
private val currenctTodo = MutableLiveData<Todo?>()
private val allTodo = database.getAllTodo()
init{
intializeThisTodo()
}
private fun intializeThisTodo(){
uiScope.launch {
currenctTodo.value=getFromDatabase()
}
}
private suspend fun getFromDatabase(): Todo? {
return withContext(Dispatchers.IO){
val info =database.getCurrentTodo()
info
}
}
private suspend fun insert(thisTodo: Todo) {
withContext(Dispatchers.IO) {
database.insert(thisTodo)
Log.i("Database","${database.getCurrentTodo()?.description} and ${database.getCurrentTodo()?.time}")
}
}
fun onAdded(time:String,description:String) {
uiScope.launch {
val thisTodo = Todo(time,description)
insert(thisTodo)
currenctTodo.value=getFromDatabase()
}
}
/**
* Called when the ViewModel is dismantled.
* At this point, we want to cancel all coroutines;
* otherwise we end up with processes that have nowhere to return to
* using memory and resources.
*/
override fun onCleared() {
super.onCleared()
viewModelJob.cancel()
}
}
todo_recycler_view
<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">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/date_text"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="TextView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/todo_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="TextView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/date_text" />
</androidx.constraintlayout.widget.ConstraintLayout>
TodoFrag.kt
class todofrag : Fragment() {
companion object {
fun newInstance() = todofrag()
}
private lateinit var viewModel: TodofragViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.todofrag_fragment, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
val application = requireNotNull(this.activity).application
val dataSource= TodoDatabase.getInstance(application)?.InformationDatabaseDao
val viewModelFactory = dataSource?.let { TodoViewModelFactory(it, application) }
val viewModel=ViewModelProviders.of(this,viewModelFactory).get(TodofragViewModel::class.java)
add_button.setOnClickListener{
val currentDate: String = SimpleDateFormat("dd/MM/yyyy", Locale.getDefault()).format(Date())
val currentTime: String = SimpleDateFormat("HH:mm:ss", Locale.getDefault()).format(Date())
val time:String="${currentDate} \n ${currentTime}"
viewModel.onAdded(time,todo_text.text.toString())
}
}
}
Please let me know if any other files are added. By the way, I tried to use card view so that it looked good.
The developer documentation explains it pretty well.
This might not be perfectly suited for what you need, but it should be a good start. Specifically, I don't know all the fields for your Todo class, so make sure you account for those in this code.
Basically, you'll want to have a ViewHolder that represents your CardView
class TodoViewHolder(convertView: View) : RecyclerView.ViewHolder(convertView) {
val dateText = convertView.findViewById(R.id.date_text)
val description = convertView.findViewById(R.id.todo_description)
// whatever else you need access to
}
And you'll want to use DiffUtil for a better user experience. This allows for some animations when things in the list change, such as removing an item, editing an item, or adding an item.
private class TodoDiffCallback : DiffUtil.ItemCallback<Todo>() {
override fun areItemsTheSame(oldItem: Todo, newItem: Todo) =
oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: Todo, newItem: Todo) =
oldItem.dateText == newItem.dateText && oldItem.description == newItem.description
}
You'll want to extend ListAdapter and override its methods. onCreateViewHolder creates an instance of your TodoViewHolder for each view that is seen and onBindViewHolder allows you to add behavior to each item in the list. It is worth noting that you can pass parameter into the adapter in case you need to.
class MyListAdapter : ListAdapter<Todo, TodoViewHolder>(TodoDiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = TodoViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.todo_recycler_view, parent, false))
override fun onBindViewHolder(holder: TodoViewHolder, position: Int) {
val todo = getItem(position)
holder.dateText = todo.dateText
holder.description = todo.description
// add whatever click listener and other stuff you need
}
}
In your fragment, when you access your RecyclerView, just add an instance of the adapter if it's null.
if (recyclerView.adapter == null) {
recyclerView.adapter = TotoListAdapter()
}
And when you want to add data (that you have retrieved from Room or your API) to the adapter (in the fragment/activity), just do the following:
(recyclerView.adapter as? TodoListAdapter)?.submitList(data)
On a side note, make sure to clean up your style (you can use the Reformat Code command in the Code menu), and you would want to rename the todo_recycler_view to something like todo_view. You'll want to have a RecyclerView layout in your fragment layout.

Using recycleview in android

I read some document about using recyclingview for activity. Now i try to use recycleview to my fragment. the problem is my fragment look empty when i execute.
fragment:
class KategoriFragment : Fragment() {
var araclarKategori = ArrayList<AracMarka>()
private lateinit var galleryViewModel: GalleryViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
veriKaynaginiDoldur()
galleryViewModel =
ViewModelProviders.of(this).get(GalleryViewModel::class.java)
var root = inflater.inflate(R.layout.fragment_category, container, false)
veriKaynaginiDoldur()
var rvarackategori = root.findViewById(R.id.rvarackategori) as RecyclerView
var MyAdapter = AracMarkaAdapter(araclarKategori)
rvarackategori.adapter = MyAdapter
/
return root
}
fun veriKaynaginiDoldur(): ArrayList<AracMarka> {
var aracLogo = arrayOf(R.drawable.opellogo, R.drawable.chevroletlogo)
var aracismi = resources.getStringArray(R.array.arabaisim)
for (i in 0 until min(aracismi.size, aracLogo.size)) {
var eklenecekaracKategori = AracMarka(aracismi[i], aracLogo[i])
araclarKategori.add(eklenecekaracKategori)
}
return araclarKategori
}
}
I create an adapter. I think there is no problem on it.
adapter:
class AracMarkaAdapter(tumKategori: ArrayList<AracMarka>) :
RecyclerView.Adapter<AracMarkaAdapter.AracMarkaViewHolder>() {
var araclar = tumKategori
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AracMarkaViewHolder {
var inflater = LayoutInflater.from(parent.context)
var arackategori = inflater.inflate(R.layout.arac_kategori, parent, false)
return AracMarkaViewHolder(arackategori)
}
override fun getItemCount(): Int {
return araclar.size
}
override fun onBindViewHolder(holder: AracMarkaViewHolder, position: Int) {
holder.aracismi.text=araclar.get(position).aracAdi
holder.aracLogo.setImageResource(araclar.get(position).aracLogo)
}
class AracMarkaViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var teksatirKategori= itemView
var aracismi= teksatirKategori.tvaracAdi
var aracLogo=teksatirKategori.img_arac_sembol
}
}
fragment xml:
<?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">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rvarackategori"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
So, when i click button, fragment opens but it is empty. Do you have any idea about it?
After modifying the data in your list in veriKaynaginiDoldur(), you need to call myAdapter.notifyDataSetChanged() so it knows to rebind the data. Or you could call veriKaynaginiDoldur() before you instantiate your adapter.
Edit: Your other error is in your for loop within veriKaynaginiDoldur(). You are making a range using the size of the araclarKategori list when it is still zero.
Instead of
for (i in 0..araclarKategori.size - 1)
use
for (i in 0 until min(aracLogo.size, aracismi.size))
you have to call this veriKaynaginiDoldur() function after the below
lines of code below I have mentioned please check
var rvarackategori = root.findViewById(R.id.rvarackategori) as RecyclerView
var MyAdapter = AracMarkaAdapter(araclarKategori)
rvarackategori.adapter = MyAdapter
veriKaynaginiDoldur()