I want to add an object from my RecyclerView to database by clicking on its icon. I use MVVM pattern, so I should add a object in Fragment. I have 2 callbacks and second one should add an object to database when by clicking. How can I get object so as to use it in viewModel.saveWord() method in adapter callback. Or I should do it anywhere else?
RecyclerView Adapter:
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))
}
}
RecyclerView ViewHolder:
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)
}
}
}
RecyclerView differ callback:
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
}
}
Adapter initialization in fragment:
searchDefAdapter = SearchDefAdapter(
object : SearchDefAdapter.OnItemClickListener {
override fun onItemClick(position: Int) {
if (!translationsList.isNullOrEmpty()){
val bundle = Bundle()
bundle.putStringArray(INFO_BUNDLE_ID, translationsList[position])
val wordFragment = WordFragment()
wordFragment.arguments = bundle
parentFragmentManager.beginTransaction().apply {
replace(R.id.searchFragment, wordFragment)
commit()
}
}
}
},
object : SearchDefAdapter.OnItemClickListener {
override fun onItemClick(position: Int) {
//viewModel.saveWord()
}
}
)
Change your callback parameter from position to your item type.
interface OnItemClickListener {
fun onItemClick(def: Def)
}
//...
class SearchDefViewHolder(
private val binding: SearchWordCardBinding,
addListener: SearchDefAdapter.OnItemClickListener,
infoListener: SearchDefAdapter.OnItemClickListener
): RecyclerView.ViewHolder(binding.root) {
private lateinit var def: Def
fun bind(data: Def) {
with (binding) {
searchCardTv.text = "${data.text} - ${data.tr[0].text}"
}
def = data
}
init {
binding.addSearchCard.setOnClickListener {
addListener.onItemClick(def)
}
binding.infoSearchCard.setOnClickListener {
infoListener.onItemClick(def)
}
}
}
Then in your Fragment, your listeners are easier to define and directly give you the item to write to database.
Related
Hi I have a function inside my viewModel which make a pager and return a flow:
fun loadLastMoviesList(genreId: Int) = Pager(config = PagingConfig(10)) {
LastMoviesPaging(repository, genreId)
}.flow.cachedIn(viewModelScope)
and a click listener in my HomeFragment which whenever user click it execute the function with different genreId:
genresAdapter.setOnItemClickListener { genre, name ->
lastMoviesTitle.text = "$name Movies"
genre.id?.let { id ->
lifecycleScope.launchWhenCreated {
viewModel.loadLastMoviesList(id).collect {
lastMoviesAdapter.submitData(it)
}
}
}
}
my problem is that when I click on an Item of recyclerView and it send new request with new body
the new itm's appear on the top of last item in recyclerView I want to update the new list with the last list in the recycler view when the genreId change how can I do it??
Adapter:
class LastMoviesAdapter #Inject constructor(): PagingDataAdapter<Data, LastMoviesAdapter.ViewHolder>(differCallBack) {
private lateinit var binding: ItemHomeMoviesLastBinding
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LastMoviesAdapter.ViewHolder {
binding = ItemHomeMoviesLastBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder()
}
override fun onBindViewHolder(holder: LastMoviesAdapter.ViewHolder, position: Int) {
getItem(position)?.let { holder.setData(it) }
holder.setIsRecyclable(false)
}
inner class ViewHolder: RecyclerView.ViewHolder(binding.root){
fun setData(item: Data){
binding.apply {
movieNameTxt.text = item.title
movieRateTxt.text = item.imdbRating
movieCountryTxt.text = item.country
movieYearTxt.text = item.year
moviePosterImg.load(item.poster){
crossfade(true)
crossfade(800)
}
root.setOnClickListener{
onItemClickListener?.let {
it(item)
}
}
}
}
}
private var onItemClickListener: ((Data) -> Unit)? = null
fun setOnItemClickListener(listener: (Data) -> Unit){
onItemClickListener = listener
}
companion object{
val differCallBack = object : DiffUtil.ItemCallback<Data>(){
override fun areItemsTheSame(oldItem: Data, newItem: Data): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: Data, newItem: Data): Boolean {
return oldItem == newItem
}
}
}
pagingSourceClase:
class LastMoviesPaging (private val repository: HomeRepository, private val genreId: Int): PagingSource<Int, Data>() {
override fun getRefreshKey(state: PagingState<Int, Data>): Int? {
return null
}
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Data> {
return try {
val currentPage = params.key ?: 1
Log.d("paging3", "paging running genre: $genreId")
val responseData = repository.lastMoviesList(genreId, currentPage).run {
body()?.data?.toString()?.let { Log.d("paging3", it) }
body()?.data ?: emptyList()
}
LoadResult.Page(responseData, if (currentPage == 1) null else -1 , if (responseData.isEmpty()) null else currentPage + 1)
}catch (e: Exception){
LoadResult.Error(e)
}
}
and I also want to save the flow in viewModel instead of resiving it directly in the HomeFragment
so any one can suggest a solution for my problem? I will provide more information if needed
I am replacing a regular RecyclerView with a ListAdapter.
I am trying to implement a delete on swipe on the list within a fragment.
I have created the following ListAdapter class, the code for which is as follows:
class NewsAdapter(private val listener: OnItemClickListener) : ListAdapter<Article, NewsAdapter.ArticleViewHolder>(DiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ArticleViewHolder {
val binding = ItemArticlePreviewBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ArticleViewHolder(binding)
}
override fun onBindViewHolder(holder: ArticleViewHolder, position: Int) {
val currentArticle = getItem(position)
holder.bind(currentArticle)
}
inner class ArticleViewHolder(private val binding: ItemArticlePreviewBinding): RecyclerView.ViewHolder(binding.root) {
init {
binding.apply {
root.setOnClickListener {
val position = adapterPosition
if(position != RecyclerView.NO_POSITION) {
val article = getItem(position)
listener.onItemClick(article)
}
}
}
}
fun bind(article: Article) {
binding.apply {
Glide.with(itemView.context).load(article.urlToImage).into(ivArticleImage)
tvSource.text = article.source.name
tvTitle.text = article.title
tvDescription.text = article.description
tvPublishedAt.text = article.publishedAt
}
}
}
/*
* Had to create my own function here since for some reason,
* newsAdapter.currentList is not working in any fragment
* */
fun getArticleAt(position: Int): Article {
return getItem(position)
}
interface OnItemClickListener {
fun onItemClick(article: Article)
}
class DiffCallback : DiffUtil.ItemCallback<Article>() {
override fun areItemsTheSame(oldItem: Article, newItem: Article): Boolean {
return oldItem.url == newItem.url
}
override fun areContentsTheSame(oldItem: Article, newItem: Article): Boolean {
return oldItem == newItem
}
}
}
Within the fragment where this list resides, I have the following:
...
lateinit var newsAdapter: NewsAdapter
...
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
...
ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(0,
ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
return false
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val position = viewHolder.adapterPosition
val article = newsAdapter.getArticleAt(position) // this works, but it is not what I want
viewModel.deleteArticle(article)
Snackbar.make(view, "Article successfully deleted.", Snackbar.LENGTH_LONG)
.apply {
setAction("Undo") {
viewModel.saveArticle(article)
}
show()
}
}
}).attachToRecyclerView(rvSavedNews)
...
}
...
Notice above, that I had to call:
val article = newsAdapter.getArticleAt(position)
within the fragment, since for some reason, I cannot use currentList as follows:
val article = newsAdapter.currentList[position]
The delete on swipe works just fine from what I can tell, however, I don't think I am always getting the most current list. Note: calling submitList on my adapter works just fine for other purposes. currentList seems to not be callable and I cannot figure out why.
In my scenario, I have two fragments. In my first Fragment I have a RecyclerView with SelectionTracker. After selecting an item in my first fragment I can navigate to the second fragment. But from my second fragment if I navigate back to the first fragment using back button all my previous selection is lost. How can I prevent this?
I am using Jetpack Navigation Component to handle Fragment navigation.
Here is my fragment
class SelectVideoForCourseFragment : Fragment(R.layout.fragment_select_video_for_course) {
.....
..
private val adapter by lazy { MediaListAdapter() }
private lateinit var selectionTracker: SelectionTracker<String>
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initializeListeners()
initializeRecyclerView(savedInstanceState)
fetchMedias()
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
selectionTracker.onSaveInstanceState(outState)
}
private fun fetchMedias() {
val mediaRepo = MediaRepo()
mediaRepo.getVideos(contentResolver).observe(viewLifecycleOwner) {
adapter.submitItems(it)
}
}
private fun initializeRecyclerView(savedInstanceState: Bundle?) {
binding.mediaList.layoutManager = GridLayoutManager(requireContext(), 3)
binding.mediaList.addItemDecoration(GridSpacingItemDecoration(3, 5.dp, false))
binding.mediaList.adapter = adapter
selectionTracker = SelectionTracker.Builder(
"tracker-select-video-for-course",
binding.mediaList,
MediaItemKeyProvider(adapter),
MediaItemDetailsLookup(binding.mediaList),
StorageStrategy.createStringStorage()
).withSelectionPredicate(
SelectionPredicates.createSelectSingleAnything()
).build()
if (savedInstanceState != null) {
selectionTracker.onRestoreInstanceState(savedInstanceState)
}
adapter.tracker = selectionTracker
}
......
...
}
My RecyclerView Adapter:
class MediaListAdapter: RecyclerView.Adapter<RecyclerView.ViewHolder>() {
val differ = AsyncListDiffer(this, DIFF_CALLBACK_MEDIA)
var tracker: SelectionTracker<String>? = null
fun submitItems(updatedItems: List<Any>) {
differ.submitList(updatedItems)
}
fun getSelections(): List<MediaEntity> {
....
..
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return if (viewType == 1) {
val binding = ListItemImageBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
ViewHolderImage(binding)
} else {
val binding = ListItemVideoBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
ViewHolderVideo(binding)
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder is ViewHolderImage) {
tracker?.let {
val item = differ.currentList[position] as ImageEntity
holder.bind(item , it.isSelected(item.uri.toString()))
}
} else {
holder as ViewHolderVideo
val item = differ.currentList[position] as VideoEntity
tracker?.let {
holder.bind(item, it.isSelected(item.uri.toString()))
}
}
}
override fun getItemCount(): Int = differ.currentList.size
override fun getItemViewType(position: Int): Int {
return if (differ.currentList[position] is ImageEntity) {
1
} else {
2
}
}
inner class ViewHolderImage(
private val binding: ListItemImageBinding): RecyclerView.ViewHolder(binding.root
) {
fun bind(item: ImageEntity, isSelected: Boolean) {
.....
...
}
fun getItemDetails(): ItemDetailsLookup.ItemDetails<String> =
object : ItemDetailsLookup.ItemDetails<String>() {
override fun getPosition(): Int = absoluteAdapterPosition
override fun getSelectionKey(): String {
val item = differ.currentList[absoluteAdapterPosition] as ImageEntity
return item.uri.toString()
}
override fun inSelectionHotspot(e: MotionEvent): Boolean = true
}
}
inner class ViewHolderVideo(
private val binding: ListItemVideoBinding,
): RecyclerView.ViewHolder(binding.root) {
fun bind(item: VideoEntity, isSelected: Boolean) {
.....
...
}
fun getItemDetails(): ItemDetailsLookup.ItemDetails<String> =
object : ItemDetailsLookup.ItemDetails<String>() {
override fun getPosition(): Int = absoluteAdapterPosition
override fun getSelectionKey(): String {
val item = differ.currentList[absoluteAdapterPosition] as VideoEntity
return item.uri.toString()
}
override fun inSelectionHotspot(e: MotionEvent): Boolean = true
}
}
companion object {
val DIFF_CALLBACK_MEDIA = object: DiffUtil.ItemCallback<Any>() {
.....
...
}
}
}
I'm trying to update the below recycler view to show the new data that input from the form in the second activity. However, its only showing the original list that I had in there. What am I doing wrong here?
Here is the main activity:
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
var workoutList = mutableListOf(
Workout("a","d","d","d"),
Workout("a","d","d","d"),
Workout("a","d","d","d")
)
val adaptor = WorkoutAdaptor(workoutList)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.btnNext.setOnClickListener {
Intent(this, SecondActivity::class.java).also {
startActivity(it)
}
}
binding.recyclerView.adapter = adaptor
binding.recyclerView.layoutManager = LinearLayoutManager(this)
}
override fun onRestart() {
super.onRestart()
var workout = intent.getSerializableExtra("EXTRA_WORKOUT") as Workout
workoutList.add(workout)
adaptor.notifyDataSetChanged()
}
}
Here is the adapter:
class WorkoutAdaptor (
var workouts: List<Workout>
) : RecyclerView.Adapter<WorkoutAdaptor.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val workoutCardBinding = WorkoutCardBinding.inflate(
LayoutInflater.from(parent.context), parent, false)
return ViewHolder(workoutCardBinding)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(workouts[position])
}
override fun getItemCount(): Int {
return workouts.size
}
inner class ViewHolder(private val workoutCardBinding: WorkoutCardBinding) :
RecyclerView.ViewHolder(workoutCardBinding.root) {
fun bind(workout: Workout) {
workoutCardBinding.tvWorkoutCard.text = workout.toString()
}
}
}
And here is the activity where I get the workout object from:
class SecondActivity : AppCompatActivity() {
private lateinit var binding: ActivitySecondBinding
lateinit var spWorkoutsPosition: String
lateinit var spIncrementsPosition: String
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivitySecondBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.spWorkoutTypes.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
spWorkoutsPosition = p0?.getItemAtPosition(p2) as String
}
override fun onNothingSelected(p0: AdapterView<*>?) {
}
}
binding.spIncrements.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(adapterView: AdapterView<*>?, view: View?, position: Int, id: Long) {
spIncrementsPosition = adapterView?.getItemAtPosition(position) as String
}
override fun onNothingSelected(p0: AdapterView<*>?) {
}
}
binding.btnSave.setOnClickListener {
val workoutName = binding.tvWorkoutName.text.toString()
val workoutType = spWorkoutsPosition.toString()
val workoutIncrement = spIncrementsPosition.toString()
val workoutRestTime = binding.etRestTime.text.toString()
val workout = Workout(workoutName, workoutType, workoutIncrement, workoutRestTime)
Intent(this, MainActivity::class.java).also {
it.putExtra("EXTRA_WORKOUT", workout)
startActivity(it)
}
}
}
}
As #John Doe commented you should probably use ActivityResultLauncher. refer here.
and as for the update issue the recommended way of implementing recyclerView's adapter is androidx.recyclerview.widget.ListAdapter. The ListAdapter handles addition and removal without the need to redraw the entire view, and even animate those changes.
here's the implementation for your adapter:
class WorkoutAdaptor :
ListAdapter<Workout, WorkoutAdaptor.ItemViewHolder>(DiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
return ItemViewHolder(
ItemAutocompleteSearchResultBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
holder.bind(position)
}
inner class ItemViewHolder(val binding: ItemAutocompleteSearchResultBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(position: Int) {
binding.apply {
workoutCardBinding.tvWorkoutCard.text = getItem(position).toString()
}
}
}
private class DiffCallback : DiffUtil.ItemCallback<Workout>() {
override fun areContentsTheSame(oldItem: Workout, newItem: Workout): Boolean {
return oldItem == newItem
}
override fun areItemsTheSame(oldItem: Workout, newItem: Workout): Boolean {
return oldItem.param1 == newItem.param1 &&
oldItem.param2 == newItem.param2 &&
oldItem.param3 == newItem.param3 &&
oldItem.param4 == newItem.param4
}
}
}
then you only have to call,
adapter.submitList(workoutList)
after you update the workoutList and the ListAdapter handles the rest.
and also call the adapter.submitList() in onStart() or onResume() methods.
My problem is that when I write MyListAdapter.currentList.isEmpty() the value is true even tho the list is NOT EMPTY! (I can see it in my view and in my database). What am I doing wrong, why is the value true even tho it should be false?
Fragment
#AndroidEntryPoint
class ShoppingCartFragment(
private val cacheMapper: ShopCacheMapper
) : Fragment(R.layout.fragment_shopping_cart), ShoppingCartListAdapter.OnItemClickListener {
private val shoppingCartViewModel: ShoppingCartViewModel by viewModels()
private val shoppingCartBinding: FragmentShoppingCartBinding by viewBinding()
#Inject lateinit var shoppingCartAdapter: ShoppingCartListAdapter
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
bindObjects()
observeList()
hideBtnIfListEmpty()
toast("LISTE IST EMPTY: ${shoppingCartAdapter.currentList.isEmpty()}")
}
private fun observeList() {
shoppingCartViewModel.productList.observe(viewLifecycleOwner) {
shoppingCartAdapter.submitList(it)
}
}
private fun bindObjects() = with(shoppingCartBinding) {
adapter = shoppingCartAdapter.apply { clickHandler(this#ShoppingCartFragment) }
viewModel = shoppingCartViewModel
lifecycleOwner = viewLifecycleOwner
}
override fun forwardCardClick(productCacheEntity: ProductCacheEntity) {
val product = cacheMapper.mapFromEntity(productCacheEntity)
val action = ShoppingCartFragmentDirections.actionShoppingCartFragmentToShopItemFragment(product)
findNavController().navigate(action)
}
override fun fordwardBtnIncreaseClick(id: Int) {
shoppingCartViewModel.increaseProductQuantityById(id)
}
override fun fordwardBtnDecreaseClick(id: Int) {
shoppingCartViewModel.decreaseProductQuantityById(id)
}
override fun forwardBtnDeleteClick(id: Int) {
shoppingCartViewModel.deleteProductById(id)
}
override fun onDestroyView() {
requireView().findViewById<RecyclerView>(R.id.rv_shopping_cart).adapter = null
super.onDestroyView()
}
// here lies the problem
private fun hideBtnIfListEmpty() {
if (shoppingCartAdapter.currentList.isEmpty()) shoppingCartBinding.shoppingCartBtn.root.visibility = View.INVISIBLE
else if (shoppingCartAdapter.currentList.isNotEmpty()) shoppingCartBinding.shoppingCartBtn.root.visibility = View.VISIBLE
}
}
ListAdapter
#FragmentScoped
class ShoppingCartListAdapter #Inject constructor() : ListAdapter<ProductCacheEntity, ShoppingCartListAdapter.ProductViewHolder>(Companion) {
private lateinit var clickListener: OnItemClickListener
companion object: DiffUtil.ItemCallback<ProductCacheEntity>() {
override fun areItemsTheSame(oldItem: ProductCacheEntity, newItem: ProductCacheEntity): Boolean = oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: ProductCacheEntity, newItem: ProductCacheEntity): Boolean = oldItem == newItem
}
inner class ProductViewHolder(val binding: ShoppingCartListItemBinding): RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProductViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding = ShoppingCartListItemBinding.inflate(layoutInflater, parent, false)
return ProductViewHolder(binding).also {
with(binding) {
ivProduct.setOnClickListener { clickListener.forwardCardClick(binding.product!!) }
tvTitle.setOnClickListener { clickListener.forwardCardClick(binding.product!!) }
btnIncrease.setOnClickListener { clickListener.fordwardBtnIncreaseClick(binding.product!!.id) }
btnDecrease.setOnClickListener { clickListener.fordwardBtnDecreaseClick(binding.product!!.id) }
btnDelete.setOnClickListener { clickListener.forwardBtnDeleteClick(binding.product!!.id) }
}
}
}
override fun onBindViewHolder(holder: ProductViewHolder, position: Int) {
val currentProduct = getItem(position) ?: return
holder.binding.product = currentProduct
holder.binding.btnDecrease.isEnabled = currentProduct.quantity > 1
holder.binding.executePendingBindings()
}
interface OnItemClickListener {
fun forwardCardClick(productCacheEntity: ProductCacheEntity)
fun fordwardBtnIncreaseClick(id: Int)
fun fordwardBtnDecreaseClick(id: Int)
fun forwardBtnDeleteClick(id: Int)
}
fun clickHandler(clickEventHandler: OnItemClickListener) {
this.clickListener = clickEventHandler
}
}
Layout
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rv_shopping_cart"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toTopOf="#id/shopping_cart_btn"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/headline"
app:recyclerview_adapter_stableID="#{adapter}"
tools:listitem="#layout/shopping_cart_list_item" />
Bindingadapter (app:recyclerview_adapter_stableID)
#BindingAdapter("recyclerview_adapter_stableID")
fun setRecyclerViewAdapterWithStableId(view: RecyclerView, adapter: RecyclerView.Adapter<*>) {
view.run {
this.setHasFixedSize(true)
this.adapter = adapter
}
}
You are calling ListAdapter.Currentlist.isEmpty() within hideBtnIfListEmpty() too early before making sure that the list is ready and submitted by observeList()
You can call it after you submit the list to the adapter:
private fun observeList() {
shoppingCartViewModel.productList.observe(viewLifecycleOwner) {
shoppingCartAdapter.submitList(it)
hideBtnIfListEmpty()
}
}