I make an api call to get a list, in the logcat I can see all the groups that came back from the api but the Recylerview doesn't show my items.
ViewModel
class MyGroupScreenViewMode(private val context: Context, private val codagramApi: CodagramApi) : ViewModel() {
#ExperimentalCoroutinesApi
private val myGroups = MutableLiveData<List<Group>>()
#ExperimentalCoroutinesApi
fun getMyGroups(): LiveData<List<Group>> = myGroups
init {
viewModelScope.launch(Dispatchers.IO){
val response = codagramApi.getAllGroups()
updateUi(response.groupList)
}
}
#ExperimentalCoroutinesApi
private fun updateUi(update: List<Group>){
viewModelScope.launch(Dispatchers.Main){
myGroups.value = update
}
}
and the fragment
class MyGroupScreen : Fragment() {
private val myGroupsAdapter: GroupAdapter by inject()
private lateinit var bindingGroup: FragmentThirdBinding
#ExperimentalCoroutinesApi
private val viewModel: MyGroupScreenViewMode by inject()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
bindingGroup = FragmentThirdBinding.inflate(layoutInflater, container, false)
return bindingGroup.root
}
#ExperimentalCoroutinesApi
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
bindingGroup.myGroups.apply {
adapter = this#MyGroupScreen.myGroupsAdapter
layoutManager = LinearLayoutManager(context)
}
myGroupsAdapter.currentList
bindingGroup.fabAdd.setOnClickListener {
it.findNavController().navigate(MyGroupScreenDirections.actionFirstViewToGroupscreen())
}
bindgetmyGroupToLiveData()
}
#ExperimentalCoroutinesApi
private fun bindgetmyGroupToLiveData() {
viewModel.getMyGroups().observe(viewLifecycleOwner, androidx.lifecycle.Observer {
myGroupsAdapter.submitList(it)
})
}
and the adapter
class GroupAdapter : ListAdapter<Group,GroupScreenViewHolder>(object: DiffUtil.ItemCallback<Group>(){
override fun areItemsTheSame(oldItem: Group, newItem: Group): Boolean = oldItem.creator?.id == newItem.creator?.id
override fun areContentsTheSame(oldItem: Group, newItem: Group): Boolean =
oldItem == newItem
}) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GroupScreenViewHolder {
return GroupScreenViewHolder(
GroupscreenMygroupsBinding.inflate(LayoutInflater.from(parent.context), parent, false)
)
}
#RequiresApi(Build.VERSION_CODES.N)
override fun onBindViewHolder(holder: GroupScreenViewHolder, position: Int) {
holder.bind(getItem(position))
}
}
class GroupScreenViewHolder(private val binding: GroupscreenMygroupsBinding) :
RecyclerView.ViewHolder(binding.root) {
#RequiresApi(Build.VERSION_CODES.N)
fun bind(postData: Group) {
binding.textView.text = postData.id.toString()
}
}
Check if data is in the list, then assign the list to adapter and call adapter.notifyDataSetChanged(),
Related
AdapterItem
class AdapterItem(val context: Context, val userList: List): RecyclerView.Adapter<AdapterItem.ViewHolder>() {
class ViewHolder(itemView:View):RecyclerView.ViewHolder(itemView){
var numeLista: TextView
var caloriiLista: TextView
init{
numeLista=itemView.numeLista
caloriiLista=itemView.caloriiLista
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
var itemView = LayoutInflater.from(context).inflate(R.layout.row_items, parent, false)
return ViewHolder(itemView)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.numeLista.text=userList[position].foodItems.get(0).foodName
holder.caloriiLista.text=userList[position].foodItems.get(0).calories.toString()
}
override fun getItemCount(): Int {
return userList.size
}
}
InformationFragment:
const val BASE_URL= "https://raw.githubusercontent.com/terrenjpeterson/caloriecounter/master/src/data/"
class InformationFragment : Fragment() {
private var binding: FragmentInformationBinding? =null
private val sharedViewModel: SharedViewModel by activityViewModels()
lateinit var adapterItem: AdapterItem
lateinit var linearLayoutManager: LinearLayoutManager
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
recyclerview_lista.setHasFixedSize(true)
linearLayoutManager = LinearLayoutManager(this.context)
recyclerview_lista.layoutManager=linearLayoutManager
getMyData()
val fragmentBinding = FragmentInformationBinding.inflate(inflater, container, false)
binding= fragmentBinding
return fragmentBinding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding?.apply {
viewModel= sharedViewModel
informationFragment=this#InformationFragment
lifecycleOwner= viewLifecycleOwner
}
}
private fun getMyData() {
val retrofitBuilder= Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(BASE_URL)
.build()
.create(ApiInterface::class.java)
val retrofitData= retrofitBuilder.getData()
retrofitData.enqueue(object : Callback<List<MyDataItem>?> {
override fun onResponse(
call: Call<List<MyDataItem>?>,
response: Response<List<MyDataItem>?>
) {
val responseBody= response.body()!!
adapterItem= AdapterItem(,responseBody ) // ** here what context should I pass ???? ... I cant use baseContext**
adapterItem.notifyDataSetChanged()
recyclerview_list.adapter=adapterItem
}
override fun onFailure(call: Call<List<MyDataItem>?>, t: Throwable) {
Log.d( "informationFragment" , "onFailure: " + t.message)
}
})
}
}
There is no need to pass the context as an arg you can use the context of the parent passed to your viewHolder
you shouldn't pass context. change:
class AdapterItem(val context: Context, val userList: List):
to:
class AdapterItem(val userList: List):
and
var itemView = LayoutInflater.from(context).inflate(R.layout.row_items, parent, false)
to:
var itemView = LayoutInflater.from(parent.context).inflate(R.layout.row_items, parent, false)
Be careful if you want to pass context to the adapter, if you assigned the view context to a variable in your adapter, it probably would lead to a memory leak. Consider using WeakReference to avoid that if happens.
Update
move
recyclerview_lista.setHasFixedSize(true)
to onViewCreated
At first I tried to pass the data to a TextView and it worked without any problems... but now I wanna pass the data to a recycleview to be able to add a checkbox for each element
This is the code where I pass the data to TextView:
const val BASE_URL= "https://raw.githubusercontent.com/terrenjpeterson/caloriecounter/master/src/data/"
class InformationFragment : Fragment() {
private var binding: FragmentInformationBinding? =null
private val sharedViewModel: SharedViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
getMyData()
val fragmentBinding = FragmentInformationBinding.inflate(inflater, container, false)
binding= fragmentBinding
return fragmentBinding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding?.apply {
viewModel= sharedViewModel
informationFragment=this#InformationFragment
lifecycleOwner= viewLifecycleOwner
}
}
private fun getMyData() {
val retrofitBuilder= Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(BASE_URL)
.build()
.create(ApiInterface::class.java)
val retrofitData= retrofitBuilder.getData()
retrofitData.enqueue(object : Callback<List<MyDataItem>?> {
override fun onResponse(call: Call<List<MyDataItem>?>, response: Response<List<MyDataItem>?>) {
val responseBody= response.body()!!
val myStringBuilder = StringBuilder()
for(myData in responseBody)
{
myStringBuilder.append(myData.foodItems.get(0).foodName)
myStringBuilder.append(" ")
myStringBuilder.append(myData.foodItems.get(0).calories)
myStringBuilder.append("\n")
myStringBuilder.append("\n")
}
txtId.text = myStringBuilder
}
override fun onFailure(call: Call<List<MyDataItem>?>, t: Throwable) {
Log.d( "informationFragment" , "onFailure: " + t.message)
}
})
}
}
You can use the context of view parameter of your viewholder.
My app still keep crashing when it reaches the InformationFragment
class AdapterItem( val userList: List): RecyclerView.Adapter<AdapterItem.ViewHolder>() {
class ViewHolder(itemView: View):RecyclerView.ViewHolder(itemView){
var numeLista: TextView
var caloriiLista: TextView
init{
numeLista=itemView.numeLista
caloriiLista=itemView.caloriiLista
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
var itemView = LayoutInflater.from(parent.context).inflate(R.layout.row_items, parent, false)
return ViewHolder(itemView)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.numeLista.text=userList[position].foodItems.get(0).foodName
holder.caloriiLista.text=userList[position].foodItems.get(0).calories.toString()
}
override fun getItemCount(): Int {
return userList.size
}
}
const val BASE_URL= "https://raw.githubusercontent.com/terrenjpeterson/caloriecounter/master/src/data/"
class InformationFragment : Fragment() {
private var binding: FragmentInformationBinding? = null
private val sharedViewModel: SharedViewModel by activityViewModels()
lateinit var adapterItem: AdapterItem
lateinit var linearLayoutManager: LinearLayoutManager
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
recyclerview_lista.setHasFixedSize(true)
linearLayoutManager = LinearLayoutManager(this.context)
recyclerview_lista.layoutManager = linearLayoutManager
getMyData()
val fragmentBinding = FragmentInformationBinding.inflate(inflater, container, false)
binding = fragmentBinding
return fragmentBinding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding?.apply {
viewModel = sharedViewModel
informationFragment = this#InformationFragment
lifecycleOwner = viewLifecycleOwner
}
}
private fun getMyData() {
val retrofitBuilder = Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(BASE_URL)
.build()
.create(ApiInterface::class.java)
val retrofitData = retrofitBuilder.getData()
retrofitData.enqueue(object : Callback<List<MyDataItem>?> {
override fun onResponse(
call: Call<List<MyDataItem>?>,
response: Response<List<MyDataItem>?>
) {
val responseBody = response.body()!!
adapterItem = AdapterItem(responseBody)
adapterItem.notifyDataSetChanged()
recyclerview_lista.adapter=adapterItem
}
override fun onFailure(call: Call<List<MyDataItem>?>, t: Throwable) {
d("informationFragment", "onFailure: " + t.message)
}
})
}
}
My App is just shows an Empty Screen and RecyclerView is not showing anything or is not working but there are no compile time or run time errors.
It would be great help if I get an answer ...
I have been making an app that uses a RecyclerView but it's not showing any thing..why contents of the recycler view have not been showing up.my codes are bellow
Here is My FeedFragment, Adapter
//Fragment
#AndroidEntryPoint
class FeedFragment : Fragment(R.layout.feed_fragment) {
private var _binding: FeedFragmentBinding? = null
private val binding get() = _binding!!
private lateinit var feedAdapter: FeedAdapter
private val viewModel: FeedViewModel by viewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FeedFragmentBinding.inflate(inflater, container, false)
val view = binding.root
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupRv()
getCharacters()
}
private fun getCharacters() {
viewModel.listData.observe(viewLifecycleOwner, Observer { pagingData ->
feedAdapter.submitData(viewLifecycleOwner.lifecycle, pagingData)
})
}
private fun setupRv() {
feedAdapter = FeedAdapter()
binding.feedRv.layoutManager =
LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
binding.feedRv.apply {
adapter = feedAdapter
setHasFixedSize(true)
visibility = View.VISIBLE
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
//PagingDataAdapter
class FeedAdapter : PagingDataAdapter<Characters,FeedAdapter.FeedViewHolder >(DiffCallback()) {
inner class FeedViewHolder(private val binding: FeedFragmentRvBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(characters: Characters) {
with(binding) {
Glide.with(itemView)
.load(characters.image)
.transition(DrawableTransitionOptions.withCrossFade())
.error(R.drawable.ic_launcher_background)
.into(feedRvImage)
feedRvName.text = characters.name
feedRvStatus.text = characters.status
feedRvSpecies.text = characters.species
feedRvGender.text = characters.gender
println(characters.name)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FeedViewHolder {
val binding =
FeedFragmentRvBinding.inflate(LayoutInflater.from(parent.context),parent,false)
return FeedViewHolder(binding)
}
override fun onBindViewHolder(holder: FeedViewHolder, position: Int) {
getItem(position)?.let { holder.bind(it) }
}
class DiffCallback : DiffUtil.ItemCallback<Characters>() {
override fun areItemsTheSame(oldItem: Characters, newItem: Characters): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: Characters, newItem: Characters): Boolean {
return oldItem == newItem
}
}
}
// retrofit response
Update your CharacterResponse model class to parse data accordingly.
data class CharacterResponse(
val info: Info,
val results: List<Characters>
)
I have home fragment and I want it to go to another fragment which will show its "details", which passes the data of the clicked recyclerview item to that "details" fragment.
When I click the article it will move to detail article which passes the data.
As for the code, here's the adapter:
class ArticleAdapter(private val articleList: ArrayList<Article>) :
RecyclerView.Adapter<ArticleAdapter.MyViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val binding = ItemArticleBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return MyViewHolder(binding)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val data = articleList[position]
holder.bind(data)
}
class MyViewHolder(private val binding: ItemArticleBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(data: Article) {
Glide.with(binding.root.context)
.load(data.img)
.into(binding.articleImage)
binding.articleTitle.text = data.title
binding.root.setOnClickListener {
val article = Article(
data.title,
data.img,
data.content
)
}
}
}
override fun getItemCount(): Int {
return articleList.size
}
}
Here's my detailFragment
class DetailArticleFragment : Fragment() {
private var _binding: FragmentDetailArticleBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentDetailArticleBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val intent = Intent(binding.root.context, DetailArticleFragment::class.java)
val article = intent.getParcelableExtra<Article>(DETAIL_ARTICLE) as Article
Glide.with(this)
.load(article.img)
.into(_binding!!.articleImage)
_binding!!.articleTitle.text = article.title
_binding!!.articleDescription.text = article.content
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
companion object {
const val DETAIL_ARTICLE = "detail_article"
}
}
You need to pass click listener interface to adapter, for example:
typealias OnArticleClick = (article: Article) -> Unit
class ArticleAdapter(
private val articleList: ArrayList<Article>
) :
RecyclerView.Adapter<ArticleAdapter.MyViewHolder>() {
var onArticleClick: OnArticleClick? = null
...
binding.root.setOnClickListener {
val article = Article(
data.title,
data.img,
data.content
)
onArticleClick?.invoke(article)
}
...
}
And init onArticleClick in your home fragment with RecyclerView:
adapter.apply {
onArticleClick = {
// show details fragment
}
}
I have a RecyclerView in MVVM project. I have to get text from editText (searchWordEt) and then pass it to the function that invokes API method in viewmodel.API works fine and returns data. But when I invoke searchDefAdapter.submitList(response) in SearchFragment nothing happens and RecyclerView data not showing.
class SearchDefAdapter(
private var infoListener: OnItemClickListener,
private var addListener: OnItemClickListener
):
ListAdapter<Def, SearchDefViewHolder>(differCallback) {
interface OnItemClickListener {
fun onItemClick(position: Int)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchDefViewHolder {
return SearchDefViewHolder(
SearchWordCardBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
),
addListener,
infoListener
)
}
override fun onBindViewHolder(holder: SearchDefViewHolder, position: Int) {
holder.bind(getItem(position))
}
}
class SearchDefViewHolder(
private val binding: SearchWordCardBinding,
addListener: SearchDefAdapter.OnItemClickListener,
infoListener: SearchDefAdapter.OnItemClickListener
): RecyclerView.ViewHolder(binding.root) {
fun bind(data: Def) {
with (binding) {
searchCardTv.text = "${data.text} - ${data.tr[0].text}"
}
}
init {
binding.addSearchCard.setOnClickListener {
addListener.onItemClick(adapterPosition)
}
binding.infoSearchCard.setOnClickListener {
infoListener.onItemClick(adapterPosition)
}
}
}
val differCallback = object : DiffUtil.ItemCallback<Def>() {
override fun areItemsTheSame(oldItem: Def, newItem: Def): Boolean {
return oldItem.text == newItem.text
}
override fun areContentsTheSame(oldItem: Def, newItem: Def): Boolean {
return oldItem == newItem
}
}
#AndroidEntryPoint
class SearchFragment : Fragment() {
private var _binding: FragmentSearchBinding? = null
private val binding get() = _binding!!
lateinit var searchDefAdapter: SearchDefAdapter
private val viewModel: DictionaryViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentSearchBinding.inflate(inflater, container, false)
val view = binding.root
searchDefAdapter = SearchDefAdapter(
object : SearchDefAdapter.OnItemClickListener {
override fun onItemClick(position: Int) {
Log.d("tag", "Item Added!")
//viewModel.saveWord(position)
}
},
object : SearchDefAdapter.OnItemClickListener {
override fun onItemClick(position: Int) {
val wordFragment = WordFragment()
fragmentManager?.beginTransaction()?.replace(
R.id.nav_host_fragment_container,
wordFragment
)?.commit()
}
}
)
setUpRecyclerView(searchDefAdapter)
var job: Job? = null
binding.searchWordEt.addTextChangedListener { editable ->
job?.cancel()
job = MainScope().launch {
delay(SEARCH_WORD_TIME_DELAY)
editable?.let {
if (editable.toString().isNotEmpty())
viewModel.getTranslation(editable.toString())
}
}
}
viewModel.def.observe(viewLifecycleOwner, Observer { response ->
binding.apply {
searchDefAdapter.submitList(response)
}
})
return view
}
override fun onDestroyView() {
_binding = null
super.onDestroyView()
}
private fun setUpRecyclerView(adapter: SearchDefAdapter){
binding.searchRv.apply {
adapter
layoutManager = LinearLayoutManager(activity)
}
}
}
You haven't actually set the adapter in setUpRecyclerView()
private fun setUpRecyclerView(myAdapter: SearchDefAdapter){
binding.searchRv.apply {
layoutManager = LinearLayoutManager(activity)
adapter = myAdapter // here
}
}
I am following this article here.
All is working well but now I want to implement the onclicklistener for each item.
Here's my code:
SongViewModelAdapter.kt
class SongViewModelAdapter(private val onSelect: ((ViewModelLyric?) -> Unit)?) : PagingDataAdapter<ViewModelLyric,
SongViewModelAdapter.SongViewHolder>(Companion) {
val TAG = "SongViewModelAdapter"
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SongViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val dataBinding = LyricDataBinding.inflate(
layoutInflater,
parent,
false
)
return SongViewHolder(dataBinding)
}
override fun onBindViewHolder(holder: SongViewHolder, position: Int) {
val lyric = getItem(position) ?: return
holder.bindLyric(lyric, onSelect)
}
companion object : DiffUtil.ItemCallback<ViewModelLyric>() {
override fun areItemsTheSame(oldItem: ViewModelLyric, newItem: ViewModelLyric): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: ViewModelLyric, newItem: ViewModelLyric): Boolean {
return oldItem == newItem
}
}
inner class SongViewHolder(
private val dataBinding: LyricDataBinding
) : RecyclerView.ViewHolder(dataBinding.root) {
fun bindLyric(lyric: ViewModelLyric, onSelect: ((ViewModelLyric?) -> Unit)?) {
dataBinding.itemTitle.text = lyric.title
dataBinding.itemArtist.text = lyric.artist
dataBinding.root.setOnClickListener {
onSelect?.let { it1 -> it1(lyric) }
}
}
}
}
HomeFragment.kt
#AndroidEntryPoint
class HomeFragment : Fragment() {
val TAG = "HomeFragment"
private lateinit var binding : FragmentHomeBinding
private val viewModel by viewModels<HomeFragmentViewModel>()
private var adapter = SongViewModelAdapter(null)
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = DataBindingUtil.inflate(
inflater, R.layout.fragment_home, container, false)
binding.productsRecyclerView.adapter = SongViewModelAdapter(::onSelect)
return binding.root
}
private fun onSelect(viewModelLyric: ViewModelLyric?) {
val id = viewModelLyric?.id
val bundle = bundleOf("id" to id)
Log.d(TAG, id.toString())
view?.findNavController()?.navigate(R.id.songDetailFragment, bundle)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setLyricsAdapter()
getLyrics()
setProgressBarAccordingToLoadState()
}
private fun setLyricsAdapter() {
binding.productsRecyclerView.adapter = adapter
}
private fun getLyrics() {
lifecycleScope.launch {
viewModel.flow.collectLatest {
adapter.submitData(it)
}
}
}
private fun setProgressBarAccordingToLoadState() {
lifecycleScope.launch {
adapter.loadStateFlow.collectLatest {
binding.progressBar.isVisible = it.append is LoadState.Loading
}
}
}
}
I am using FirestorePagingAdapter at the moment and it works well. For those experienced in paging 3, how would you pass total items to implement the click listener for each item. Saw similar questions but never a clear solution. Been scratching my head for a day. Your help will be much appreciated.
Edit 1: Added HomeFragment.kt
Edit 2: Updated with user suggestions. Click still not working. I needed to add the null checks.
It is not a good practice to handle click inside your adapter.
The adapter concern is just to act as a bridge b/w our data and the recyclerview not to handle click. Passing it back to fragment/activity restores that. Second, using callbacks may lead to Memory leaks as it going to have the reference to the fragment/activity.
class YourRecyclerViewAdapter(private val onSelect: (ViewModelLyric?) -> Unit) : PagingDataAdapter<ViewModelLyric, SongViewModelAdapter.SongViewHolder>(Companion) {
override fun onBindViewHolder(holder: YourViewHolder, position: Int) {
val lyric = getItem(position) ?: return
holder.bindLyric(lyric)
}
class SongViewHolder(private val binding: YourViewBinding) : RecyclerView.ViewHolder(dataBinding.root) {
fun bindLyric(yourDataType: ViewModelLyric?, onSelect: (ViewModelLyric?) -> Unit) {
dataBinding.itemTitle.text = lyric.title
dataBinding.itemArtist.text = lyric.artist
dataBinding.root.setOnClickListener {
onSelect(yourDataType)
}
}
}
}
In your Fragment/Activity
// Set this adapter to your recycler view
binding.productsRecyclerView.adapter = YourRecyclerViewAdapter { viewModelLyric->
// Handle click here
}
EDIT:
SongViewModelAdapter
class SongViewModelAdapter(private val onSelect: (ViewModelLyric) -> Unit) : PagingDataAdapter<ViewModelLyric, SongViewModelAdapter.SongViewHolder>(Companion) {
val TAG = "SongViewModelAdapter"
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SongViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val dataBinding = LyricDataBinding.inflate(layoutInflater, parent, false)
return SongViewHolder(dataBinding)
}
override fun onBindViewHolder(holder: SongViewHolder, position: Int) {
val lyric = getItem(position) ?: return
holder.bindLyric(lyric, onSelect)
}
companion object : DiffUtil.ItemCallback<ViewModelLyric>() {
override fun areItemsTheSame(oldItem: ViewModelLyric, newItem: ViewModelLyric): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: ViewModelLyric, newItem: ViewModelLyric): Boolean {
return oldItem == newItem
}
}
class SongViewHolder(private val dataBinding: LyricDataBinding) : RecyclerView.ViewHolder(dataBinding.root) {
fun bindLyric(lyric: ViewModelLyric, onSelect: (ViewModelLyric) -> Unit)) {
dataBinding.itemTitle.text = lyric.title
dataBinding.itemArtist.text = lyric.artist
dataBinding.root.setOnClickListener {
onSelect(lyric)
}
}
}
}
HomeFragment
#AndroidEntryPoint
class HomeFragment : Fragment() {
val TAG = "HomeFragment"
private lateinit var binding : FragmentHomeBinding
private val viewModel by viewModels<HomeFragmentViewModel>()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_home, container, false)
binding.productsRecyclerView.adapter = SongViewModelAdapter(::onSelect)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
getLyrics()
setProgressBarAccordingToLoadState()
}
private fun onSelect(lyric: ViewModelLyric) {
// handle click here
}
private fun getLyrics() {
lifecycleScope.launch {
viewModel.flow.collectLatest {
binding.adapter.submitData(it)
}
}
}
private fun setProgressBarAccordingToLoadState() {
lifecycleScope.launch {
adapter.loadStateFlow.collectLatest {
binding.progressBar.isVisible = it.append is LoadState.Loading
}
}
}
}