1

I am trying to implement Paging 3, Binding, and Room, but it does not show any data on the RecyclerView. Following some code snipped of the different components of the code.

AssetEntity.kt

@Entity(tableName = "tbl_assets")
data class AssetEntity(

    @PrimaryKey(autoGenerate = true)
    var id: Int = 0,
    var barcode: String = String(),
    var description: String = String(),
    var brand: String = String(),
    var lastUpdate: String = String(),
...
)

AssetDao.kt

@Dao
interface AssetDao {

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertAssetList(assetEntityList: List<AssetEntity>): List<Long>

    @Query("DELETE FROM tbl_assets")
    suspend fun deleteAssets(): Int

    @Query("SELECT * FROM tbl_assets ORDER BY page_key ASC ")
    fun getPagedAssetList(): PagingSource<Int,AssetEntity>
}

AssetRepositoryImpl.kt

class AssetRepositoryImpl(
    private val context: Context,
    private val networkService: NetworkService,
    private val dao: AssetDao,
    private val mapper: ItemDtoMapper,
): AssetRepository {

    private val preferenceManager = PreferenceManager(context)

    override suspend fun getAssetList(token: String): List<AssetEntity> {
        val response = networkService.getItemList(token)
        return mapper.toDomainList(response.data)
    }

    override fun getPagedAssetsDb(): PagingSource<Int,AssetEntity> {
        return dao.getPagedAssetList()
    }
...
}

AssetListViewModel.kt

@HiltViewModel
class AssetListViewModel
@Inject
constructor(repository: AssetRepository) : ViewModel() {

    val allAssets = Pager(
        config = PagingConfig(
            pageSize = 60,
            enablePlaceholders = true,
            maxSize = 200
        )
    ) {
        repository.getPagedAssetsDb()
    }.flow.cachedIn(viewModelScope)
}

AssetPageAdapter.kt

class AssetPageAdapter(private val interaction: Interaction? = null):
    PagingDataAdapter<AssetEntity, AssetPageAdapter.AssetViewHolder>(DIFF_CALLBACK) {

    private lateinit var binding: RecyclerviewAssetBinding

    companion object{
        val DIFF_CALLBACK : DiffUtil.ItemCallback<AssetEntity> = object : DiffUtil.ItemCallback<AssetEntity>(){
            override fun areItemsTheSame(oldItem: AssetEntity, newItem: AssetEntity): Boolean {
                return oldItem.id== newItem.id
            }

            override fun areContentsTheSame(oldItem: AssetEntity, newItem: AssetEntity): Boolean {
                return oldItem == newItem
            }
        }
    }

    private val differ: AsyncListDiffer<AssetEntity> = AsyncListDiffer(this, DIFF_CALLBACK)

    fun submitList(assets: List<AssetEntity>){
        differ.submitList(assets)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) : AssetViewHolder {
        binding = RecyclerviewAssetBinding.inflate(LayoutInflater.from(parent.context),parent,false)

        return AssetViewHolder(binding,interaction)
    }


    override fun onBindViewHolder(holder: AssetViewHolder, position: Int) {
        holder.bind(differ.currentList[position])
    }

    override fun getItemCount()  = differ.currentList.size

    inner class AssetViewHolder
        constructor(
            val view: RecyclerviewAssetBinding,
            private val interaction: Interaction?
        ): RecyclerView.ViewHolder(view.root){

        fun bind(item: AssetEntity) {
            view.root.setOnClickListener {
                interaction?.onItemSelected(absoluteAdapterPosition,item)
            }
            view.assetDescription.text = item.description
            view.assetBarcode.text = item.barcode
        }
    }

    interface Interaction{
        fun onItemSelected(position: Int, item: AssetEntity)
    }
}

AssetListFragment.kt

    @AndroidEntryPoint
    class AssetListFragment : Fragment(), AssetPageAdapter.Interaction{
    
        private val viewModel: AssetListViewModel by viewModels()
        lateinit var assetPageAdapter: AssetPageAdapter
        private lateinit var navController: NavController
        private var _binding: FragmentAssetListBinding? = null
        private val binding get() = _binding!!
    
        override fun onCreateView(
            inflater: LayoutInflater, container: ViewGroup?,
            savedInstanceState: Bundle?
        ): View? {
            _binding = FragmentAssetListBinding.inflate(inflater, container, false)
            return binding.root
        }
    
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
            navController = Navigation.findNavController(view)
    
            //binding.assetsRecyclerview.layoutManager = LinearLayoutManager(activity)
            assetPageAdapter = AssetPageAdapter(this)
            binding.assetsRecyclerview.adapter = assetPageAdapter
    
            lifecycleScope.launch {
                viewModel.allAssets.collectLatest {
                    println("Item: {$it}")
                    assetPageAdapter.submitData(it)
                }
            }
        }
   ...
    }

fragment_asset_list.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
    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"
    android:fitsSystemWindows="true"
    tools:context=".framework.presentation.ui.AssetListFragment">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/assets_recyclerview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
             />

    </androidx.constraintlayout.widget.ConstraintLayout>

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/new_asset_floating_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="16dp"
        android:contentDescription="@string/new_asset"
        app:srcCompat="@drawable/ic_add_white_24"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

recyclerview_asset.xml

<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView
    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/assets_card"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:clickable="true"
    android:focusable="true"
    app:cardCornerRadius="10dp"
    app:cardElevation="10dp">
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <ImageView
            android:id="@+id/asset_image"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/asset_white_72"
            android:contentDescription="@string/app_name"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintEnd_toStartOf="@id/asset_description"/>

        <TextView
            android:id="@+id/asset_description"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="8dp"
            android:text=""
            android:textSize="16sp"
            android:textColor="@color/blue_900"
            app:layout_constraintStart_toEndOf="@+id/asset_image"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toTopOf="@id/asset_barcode"/>

        <TextView
            android:id="@+id/asset_barcode"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text=""
            android:textColor="@color/blue_500"
            app:layout_constraintStart_toStartOf="@+id/asset_description"
            app:layout_constraintTop_toBottomOf="@+id/asset_description" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>

any help is appreciated, it should be a silly error somewhere, but I have been 3 days working on it and I haven't been able to find the error. I don't get any error on the log, so I am a bit lost at this point. Help anyone?

MarioV
  • 108
  • 11

1 Answers1

0

To make it work, I did some changes to the following files:

AssetDao.kt

@Dao
interface AssetDao {

    @Query("SELECT * FROM tbl_assets ORDER BY page_key ASC LIMIT :limit OFFSET :offset")
    suspend fun getAssetListDb(limit: Int, offset: Int): List<AssetEntity>
   
}

I have created a new file AssetPagingSource.kt

private const val STARTING_KEY = 0

class AssetPagingSource(
    private val dao: AssetDao
): PagingSource<Int,AssetEntity>() {


    override fun getRefreshKey(state: PagingState<Int, AssetEntity>): Int? {
        
        return state.anchorPosition?.let { anchorPosition ->
            val anchorPage = state.closestPageToPosition(anchorPosition)
            anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
        }
    }

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, AssetEntity> {
        val page = params.key ?: STARTING_KEY

        return try {
            val assets = dao.getAssetListDb(params.loadSize, page * params.loadSize)
            LoadResult.Page(
                data = assets,
                prevKey = if (page == 0) null else page - 1,
                nextKey = if (assets.isEmpty()) null else page + 1
            )
        } catch (e: Exception) {
            LoadResult.Error(e)
        }
    }
}

Some changes to the AssetRepositoryImpl.kt file

class AssetRepositoryImpl(
    private val context: Context,
    private val networkService: NetworkService,
    private val dao: AssetDao,
    private val mapper: ItemDtoMapper,
): AssetRepository {

     fun assetPagingSource() = AssetPagingSource(dao)
    
}

Some changes to the AssetListViewModel.kt

@HiltViewModel
class AssetListViewModel
@Inject
constructor(private val repository: AssetRepository) : ViewModel() {

    val allAssets = Pager(
        config = PagingConfig(
            pageSize = 60,
            enablePlaceholders = true,
            initialLoadSize = 200
        )
    ) {
        (repository as AssetRepositoryImpl).assetPagingSource()
    }.flow.cachedIn(viewModelScope)
}

AssetPageAdapter.kt

class AssetPageAdapter(private val interaction: Interaction? = null):
    PagingDataAdapter<AssetEntity, AssetPageAdapter.AssetViewHolder>(DIFF_CALLBACK) {

    private lateinit var binding: RecyclerviewAssetBinding

    companion object{
        val DIFF_CALLBACK : DiffUtil.ItemCallback<AssetEntity> = object : DiffUtil.ItemCallback<AssetEntity>(){
            override fun areItemsTheSame(oldItem: AssetEntity, newItem: AssetEntity): Boolean {
                return oldItem.pageKey == newItem.pageKey
            }

            override fun areContentsTheSame(oldItem: AssetEntity, newItem: AssetEntity): Boolean {
                return oldItem == newItem
            }
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) : AssetViewHolder {
        binding = RecyclerviewAssetBinding.inflate(LayoutInflater.from(parent.context),parent,false)
        return AssetViewHolder(binding,interaction)
    }

    override fun onBindViewHolder(holder: AssetViewHolder, position: Int) {
        val item = getItem(position)
        if (item != null) holder.bind(item)
    }

    inner class AssetViewHolder
        constructor(
            val view: RecyclerviewAssetBinding,
            private val interaction: Interaction?
        ): RecyclerView.ViewHolder(view.root){

        fun bind(item: AssetEntity) {

            view.root.setOnClickListener {
                interaction?.onItemSelected(absoluteAdapterPosition,item)
            }
            view.apply {
                assetDescription.text = item.description
                assetBarcode.text = item.barcode
            }
        }
    }

    interface Interaction{
        fun onItemSelected(position: Int, item: AssetEntity)
    }
}

AssetListFragment.kt

@AndroidEntryPoint
class AssetListFragment : Fragment(), AssetPageAdapter.Interaction{

    private val viewModel: AssetListViewModel by viewModels()
    lateinit var assetPageAdapter: AssetPageAdapter
    private lateinit var navController: NavController
    private var _binding: FragmentAssetListBinding? = null
    private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding = FragmentAssetListBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        navController = Navigation.findNavController(view)

        assetPageAdapter = AssetPageAdapter(this)
        binding.assetsRecyclerview.adapter = assetPageAdapter

        lifecycleScope.launch {
            viewModel.allAssets.collectLatest {
                assetPageAdapter.submitData(it)
            }
        }
    }

     override fun onItemSelected(position: Int, item: AssetEntity) {
        Snackbar.make(requireView(),"Asset: ${item.description}", Snackbar.LENGTH_LONG)
            .show()
    }
}

I hope this may help anyone, if anyone spots any way to improve it, just let me know.

This links helps me a lot.

MarioV
  • 108
  • 11