Why my PagingSource doesn't give me any data? - android

The Elements of the project that don't work
And I check if data is no null and do default submitList in the fragment.
Btw here is the link to the documentation
SearchPagingSource
These logs aren't even shown
class SearchPagingSource(
private val api: Api,
private val query: String
) : PagingSource<Int, Image>
() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Image> {
val position = params.key ?: 0
Log.d("SUPERTAG", "response")
return try {
val response =
api.search(query, position, params.loadSize, Locale.getDefault().language)
val list = ArrayList<Image>()
response.gif.forEach {
list.add(it.image)
}
Log.d("SUPERTAG", "response: $list")
LoadResult.Page(
data = list,
prevKey = null,
nextKey = if (list.isEmpty()) null else position + 15
)
} catch (e: IOException) {
// no connection
Log.d("SUPERTAG", "IOException: ${e.message}")
LoadResult.Error(e)
} catch (e: HttpException) {
// error loading
Log.d("SUPERTAG", "HttpException: ${e.message}")
LoadResult.Error(e)
}
}
}
ViewModel
Null because of the null that returned by the repository.
class SearchViewModel : ViewModel() {
private val searchRepository = SearchRepository.getInstance()
private val _query = MutableLiveData<String>()
private val _results = _query.map { data ->
searchRepository.search(data).value
}
val results = _results
private val _error = MutableLiveData<String>()
val error: LiveData<String> = _error
#SuppressLint("StaticFieldLeak")
private lateinit var progressBar: ProgressBar
fun initProgress(progress: ProgressBar) {
progressBar = progress
}
fun search(query: String, errorLoading: String) {
viewModelScope.launch {
try {
progressBar.visibility = View.VISIBLE
_query.value = query
Log.d("SUPERTAG", "result2: ${searchRepository.search(_query.value!!).value}")
progressBar.visibility = View.GONE
} catch (e: Exception) {
_error.value = e.message
}
}
}
}
Repository
Exactly this part of the code returns null, I checked It by logs. I guess I do smth wrong with parameters or in general.
object SearchRepository {
private lateinit var instance: SearchRepository
private val app: App by lazy {
App().getInstance()
}
fun getInstance(): SearchRepository {
instance = this
app.initRetrofit()
return instance
}
fun search(query: String) = Pager(
config = PagingConfig(
15,
maxSize = 50,
enablePlaceholders = false
),
pagingSourceFactory = {
SearchPagingSource(app.api, query)
}
).liveData
}
If I do like this, I get at least snackbar and an error. Usually it shows nothing and even no progressBar.
So, If I add jumpThreshold = 0, I get a snackbar with an error that I don't have usually.
fun search(query: String) = Pager(
config = PagingConfig(
pageSize = 15,
jumpThreshold = 0
),
pagingSourceFactory = {
SearchPagingSource(app.api, query)
}
).liveData
Edit
So, I did it with flow and it works a bit, but Im still not getting a list in my recycler.
Repository
fun getListData(query: String): Flow<PagingData<Image>> {
return Pager(
config = PagingConfig(
pageSize = 15,
maxSize = 50,
enablePlaceholders = true
), pagingSourceFactory = {
SearchPagingSource(query = query, api = app.api)
}).flow
}
ViewModel
fun search(query: String){
viewModelScope.launch {
try {
searchRepository.getListData(query).collectLatest {
it.map {
Log.d("SUPERTAG", "image $it")
}
Log.d("SUPERTAG", it.toString())
_results.value = it
}
} catch (e: Exception){
_error.value = e.message
}
}
}

The answer was in the adapter!
class SearchAdapter(
diffCallback: DiffUtil.ItemCallback<Image>,
private val clickListener: (url: String) -> Unit
) : PagingDataAdapter<Image, SearchAdapterViewHolder>(diffCallback) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchAdapterViewHolder {
val inflater = LayoutInflater.from(parent.context)
val view = inflater.inflate(R.layout.list_item, parent, false)
return SearchAdapterViewHolder(view)
}
override fun onBindViewHolder(holder: SearchAdapterViewHolder, position: Int) {
val item = getItem(position)
if(item != null){
holder.imageView.setOnClickListener {
clickListener(item.original.url)
}
holder.bind(item)
}
}
}

Related

Data From API Has Not Appeared Using Paging 3

I'm learning paging 3, but the data from the API doesn't appear. My code is like below:
interface PokeAPI {
#GET("pokemon")
fun getPokemonList() : Call<PokemonList>
#GET("pokemon")
fun getAllPokemon(
#Query("limit") limit: Int,
#Query("offset") offset: Int) : PokemonList
#GET("pokemon/{name}")
fun getPokemonInfo(
#Path("name") name: String
) : Call<Pokemon>
}
class PokePagingSource(private val apiService: PokeAPI): PagingSource<Int, Result>() {
private companion object {
const val INITIAL_PAGE_INDEX = 1
}
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Result> {
return try {
val position = params.key ?: INITIAL_PAGE_INDEX
val responseData = apiService.getAllPokemon(position, params.loadSize)
if (responseData.results.isEmpty()) {
Log.e("Response Succeed!", responseData.results.toString())
} else {
Log.e("Response Failed!", responseData.results.toString())
}
LoadResult.Page(
data = responseData.results,
prevKey = if (position == INITIAL_PAGE_INDEX) null else position - 1,
nextKey = if (responseData.results.isNullOrEmpty()) null else position + 1
)
} catch (exception: Exception) {
return LoadResult.Error(exception)
}
}
override fun getRefreshKey(state: PagingState<Int, Result>): Int? {
return state.anchorPosition?.let { anchorPosition ->
val anchorPage = state.closestPageToPosition(anchorPosition)
anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
}
}
}
class PokemonRepository(private val apiService: PokeAPI) {
fun getAllPokemon(): LiveData<PagingData<Result>>{
return Pager(
config = PagingConfig(
pageSize = 10
),
pagingSourceFactory = {
PokePagingSource(apiService)
}
).liveData
}
}
object Injection {
private val api by lazy { RetrofitClient().endpoint }
fun provideRepository(): PokemonRepository {
return PokemonRepository(api)
}
}
class PokemonViewModel(pokemonRepository: PokemonRepository) : ViewModel() {
val allPokemonList: LiveData<PagingData<Result>> =
pokemonRepository.getAllPokemon().cachedIn(viewModelScope)
}
class ViewModelFactory : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(PokemonViewModel::class.java)) {
#Suppress("UNCHECKED_CAST")
return PokemonViewModel(Injection.provideRepository()) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
`class PokemonPagingAdapter(private val context: Context) :
PagingDataAdapter<Result, PokemonPagingAdapter.ViewHolder>(DIFF_CALLBACK) {
private var onItemClick: OnAdapterListener? = null
fun setOnItemClick(onItemClick: OnAdapterListener) {
this.onItemClick = onItemClick
}
class ViewHolder(val binding: AdapterPokemonBinding) : RecyclerView.ViewHolder(binding.root) {
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
AdapterPokemonBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val pokemonData = getItem(position)
if (pokemonData != null) {
holder.binding.apply {
val number = if (pokemonData.url.endsWith("/")) {
pokemonData.url.dropLast(1).takeLastWhile { it.isDigit() }
} else {
pokemonData.url.takeLastWhile { it.isDigit() }
}
val url = "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${number}.png"
Glide.with(context)
.load(url)
.transition(DrawableTransitionOptions.withCrossFade())
.centerCrop()
.circleCrop()
.into(ivPokemon)
tvNamePokemon.text = pokemonData.name
btnDetail.setOnClickListener {
onItemClick?.onClick(pokemonData, pokemonData.name, url)
}
}
}
}
companion object {
val DIFF_CALLBACK = object : DiffUtil.ItemCallback<Result>() {
override fun areItemsTheSame(
oldItem: Result,
newItem: Result
): Boolean {
return oldItem == newItem
}
override fun areContentsTheSame(
oldItem: Result,
newItem: Result
): Boolean {
return oldItem.name == newItem.name
}
}
}
interface OnAdapterListener {
fun onClick(data: Result, name: String, url: String)
}
}`
class FragmentPokemon: Fragment(R.layout.fragment_pokemon) {
private var _binding : FragmentPokemonBinding? = null
private val binding get() = _binding!!
private lateinit var dataPagingAdapter: PokemonPagingAdapter
private val viewModel: PokemonViewModel by viewModels {
ViewModelFactory()
}
private lateinit var comm: Communicator
override fun onStart() {
super.onStart()
getData()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding = FragmentPokemonBinding.bind(view)
val toolBar = requireActivity().findViewById<View>(R.id.tool_bar)
toolBar.visibility = View.VISIBLE
val navBar = requireActivity().findViewById<BottomNavigationView>(R.id.bottom_navigation)
navBar.visibility = View.VISIBLE
comm = requireActivity() as Communicator
setupListPokemon()
}
private fun setupListPokemon(){
dataPagingAdapter = PokemonPagingAdapter(requireContext())
dataPagingAdapter.setOnItemClick(object: PokemonPagingAdapter.OnAdapterListener{
override fun onClick(data: Result, name: String, url: String) {
comm.passDataCom(name, url)
}
})
binding.apply {
rvPokemon.layoutManager = LinearLayoutManager(context)
rvPokemon.setHasFixedSize(true)
rvPokemon.adapter = dataPagingAdapter
}
}
private fun getData(){
viewModel.allPokemonList.observe(viewLifecycleOwner){
dataPagingAdapter.submitData(lifecycle, it)
binding.btnCoba.setOnClickListener { btn ->
if (it == null){
Log.e("ResponseFailed", it.toString())
} else Log.e("ResponseSucceed", it.toString())
}
}
}
}
What's the reason? I have followed the step by step implementation of paging 3 but the data still doesn't appear either.
I don't know the API you are using, but it seems that you are using it incorrectly. The getAllPokemon method has limit and offset parameters and you are calling it like apiService.getAllPokemon(position, params.loadSize), so you are using position as a limit and params.loadSize as an offset.
You should pass params.loadSize as a limit, rename INITIAL_PAGE_INDEX to INITIAL_OFFSET and set it to 0, since your API uses offsets instead of pages (at least it seems so from what you provided). The load function should then look something like this:
// get current offset
val offset = params.key ?: INITIAL_OFFSET
val responseData = apiService.getAllPokemon(limit = params.loadSize, offset = offset)
val prevKey = offset - params.loadSize
val nextKey = offset + params.loadSize

Android Paging 3.0 load method not working

i am trying to implement the android paging 3.0 library and i am having an issue with the .load method not working, at first i thought the adapter submitData or layoutManager was not working cause i did get response from LoadResult.Page, but now when looking further it kind of stopped working at all
here's my code
1- Fragment
private fun initAdapter() {
searchAdapter = SearchItemsAdapter {
//item clicked
}
val gridLayoutManager = GridLayoutManager(requireContext(), 2)
binding?.layoutManager = gridLayoutManager
binding?.searchAdapter = searchAdapter
}
private fun startSearch() {
searchJob?.cancel()
searchJob = viewLifecycleOwner.lifecycleScope.launch {
searchViewModel?.getSearchResults("phone")?.collectLatest {
searchAdapter?.submitData(it)
}
}
}
2- ViewModel
fun getSearchResults(query: String): Flow<PagingData<ItemModel>> {
val lastResult = currentSearchResult
if (query == currentQueryValue && lastResult != null) {
return lastResult
}
currentQueryValue = query
val newResult: Flow<PagingData<ItemModel>> = searchRepository.getSearchResults(query)
.cachedIn(viewModelScope)
currentSearchResult = newResult
return newResult
}
3- SearchRepository
fun getSearchResults(query: String): Flow<PagingData<ItemModel>> {
return Pager(
config = PagingConfig(
pageSize = 10
),
pagingSourceFactory = { SearchPagingSource(client, query) }
).flow
}
4- PagingSource
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, ItemModel> {
val position: Int = params.key ?: 1
return try {
val response: SearchResponse = service.getSearchResult(query, position)
val items = response.metadata.results
val nextKey = if (items.isEmpty()) {
null
} else {
position + 1
}
LoadResult.Page(
data = items,
prevKey = if (position == 1) null else position - 1,
nextKey = nextKey
)
} catch (exception: IOException) {
return LoadResult.Error(exception)
} catch (exception: HttpException) {
return LoadResult.Error(exception)
}
}
5- Adapter
override fun onBindViewHolder(holder: SearchItemsViewHolder, position: Int) {
val item = getItem(position)
if (item != null) {
holder.binding.item = item
holder.itemView.setOnClickListener {
itemClickCallback(item)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchItemsViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding = SearchItemLayoutBinding.inflate(layoutInflater, parent, false)
return SearchItemsViewHolder(binding)
}
companion object {
private val diffCallBack = object : DiffUtil.ItemCallback<ItemModel>() {
override fun areItemsTheSame(oldItem: ItemModel, newItem: ItemModel): Boolean =
oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: ItemModel, newItem: ItemModel): Boolean =
oldItem == newItem
}
}
i found the issue that i had
1- i was not calling the startSearch method from the onViewCreated method of the fragment, that didn't help with the API calling
2- the recyclerView in the XML had wrap_content instead of 0dp as height
For me, my data class was
data class Score(
val userId: String,
val username: String,
val avatarPath: String,
val score: Long,
)
I changed it to
data class Score(
var userId: String? = null,
var username: String? = null,
var avatarPath: String? = null,
var score: Long? = null,
)
and now it works !!

How to reset the scroll position after process death when using Paging 3 with RemoteMediator

I have set up Paging 3 with offline caching using a RemoteMediator. After process death, the RecyclerView immediately restores the correct scrolling position. However, since we need to send the search query again it triggers a LoadType.REFRESH which clears the current search results from the cache and replaces them with new values. This brings us back to the start of the list.
My RemoteMediator:
private const val NEWS_STARTING_PAGE_INDEX = 1
class SearchNewsRemoteMediator(
private val searchQuery: String,
private val newsDb: NewsArticleDatabase,
private val newsApi: NewsApi
) : RemoteMediator<Int, NewsArticle>() {
private val newsArticleDao = newsDb.newsArticleDao()
override suspend fun load(
loadType: LoadType,
state: PagingState<Int, NewsArticle>
): MediatorResult {
val page = when (loadType) {
LoadType.REFRESH -> {
val remoteKeys = getRemoteKeyClosestToCurrentPosition(state)
remoteKeys?.nextKey?.minus(1) ?: NEWS_STARTING_PAGE_INDEX
}
LoadType.PREPEND -> {
val remoteKeys = getRemoteKeyForFirstItem(state)
?: throw InvalidObjectException("Remote key should not be null for $loadType")
val prevKey = remoteKeys.prevKey
if (prevKey == null) {
return MediatorResult.Success(endOfPaginationReached = true)
}
remoteKeys.prevKey
}
LoadType.APPEND -> {
val remoteKeys = getRemoteKeyForLastItem(state)
if (remoteKeys == null || remoteKeys.nextKey == null) {
throw InvalidObjectException("Remote key should not be null for $loadType")
}
remoteKeys.nextKey
}
}
return try {
delay(2000)
val apiResponse = newsApi.searchNews(searchQuery, page, state.config.pageSize)
val serverSearchResults = apiResponse.articles
val endOfPaginationReached = serverSearchResults.isEmpty()
val bookmarkedArticles = newsArticleDao.getAllBookmarkedArticles().first()
val cachedBreakingNewsArticles = newsArticleDao.getCachedBreakingNews().first()
val searchResults = serverSearchResults.map { serverSearchResultArticle ->
val bookmarked = bookmarkedArticles.any { bookmarkedArticle ->
bookmarkedArticle.url == serverSearchResultArticle.url
}
val inBreakingNewsCache =
cachedBreakingNewsArticles.any { breakingNewsArticle ->
breakingNewsArticle.url == serverSearchResultArticle.url
}
NewsArticle(
title = serverSearchResultArticle.title,
url = serverSearchResultArticle.url,
urlToImage = serverSearchResultArticle.urlToImage,
isBreakingNews = inBreakingNewsCache,
isBookmarked = bookmarked,
isSearchResult = true
)
}
newsDb.withTransaction {
if (loadType == LoadType.REFRESH) {
newsDb.searchRemoteKeyDao().clearRemoteKeys()
newsArticleDao.resetSearchResults()
newsArticleDao.deleteAllObsoleteArticles()
}
val prevKey = if (page == NEWS_STARTING_PAGE_INDEX) null else page - 1
val nextKey = if (endOfPaginationReached) null else page + 1
val remoteKeys = serverSearchResults.map { article ->
SearchRemoteKeys(article.url, prevKey, nextKey)
}
newsDb.searchRemoteKeyDao().insertAll(remoteKeys)
newsDb.newsArticleDao().insertAll(searchResults)
}
MediatorResult.Success(endOfPaginationReached)
} catch (exception: IOException) {
MediatorResult.Error(exception)
} catch (exception: HttpException) {
MediatorResult.Error(exception)
}
}
private suspend fun getRemoteKeyForLastItem(state: PagingState<Int, NewsArticle>): SearchRemoteKeys? =
state.pages.lastOrNull { it.data.isNotEmpty() }?.data?.lastOrNull()
?.let { article ->
newsDb.searchRemoteKeyDao().getRemoteKeyFromArticleUrl(article.url)
}
private suspend fun getRemoteKeyForFirstItem(state: PagingState<Int, NewsArticle>): SearchRemoteKeys? =
state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()
?.let { article ->
newsDb.searchRemoteKeyDao().getRemoteKeyFromArticleUrl(article.url)
}
private suspend fun getRemoteKeyClosestToCurrentPosition(
state: PagingState<Int, NewsArticle>
): SearchRemoteKeys? =
state.anchorPosition?.let { position ->
state.closestItemToPosition(position)?.url?.let { articleUrl ->
newsDb.searchRemoteKeyDao().getRemoteKeyFromArticleUrl(articleUrl)
}
}
}
The repository method that instantiates it:
fun getSearchResults(query: String): Flow<PagingData<NewsArticle>> =
Pager(
config = PagingConfig(pageSize = 20, enablePlaceholders = false),
remoteMediator = SearchNewsRemoteMediator(query, newsArticleDatabase, newsApi),
pagingSourceFactory = { newsArticleDatabase.newsArticleDao().getSearchResultsPaged() }
).flow
The ViewModel that triggers the query. currentQuery is restored after process death and therefore calls getSearchResults immediately with the old query.
class SearchNewsViewModel #ViewModelInject constructor(
private val repository: NewsRepository,
#Assisted state: SavedStateHandle
) : ViewModel() {
private val currentQuery = state.getLiveData<String?>("currentQuery")
val newsArticles = currentQuery.switchMap { query ->
repository.getSearchResults(query).asLiveData().cachedIn(viewModelScope)
}
fun searchArticles(query: String) {
currentQuery.value = query
}
}

Recycler View in Fragment shows no data although PagingSource loggs correct data

I am trying to display RESTful Api in recycler view in fragment. When app runs it shows no error but loads nothing in recycler view. I logged response in PagingSource file and it shows correct data but still nothing is displayed in recycler view.
This is my Api interface:
interface Api {
companion object {
const val BASE_URL = "http://be7c232bf30e.ngrok.io"
}
#GET("/posts")
suspend fun searchPosts(
#Query("_page") page: Int,
#Query("_limit") perPage: Int
): List<SocialNetworkPost>
}
This is my PagingSource file:
private const val STARTING_PAGE_INDEX = 1
class PagingSource(private val api: Api) : PagingSource<Int, SocialNetworkPost>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, SocialNetworkPost> {
val position = params.key ?: STARTING_PAGE_INDEX
return try {
val response = api.searchPosts(position, params.loadSize)
LoadResult.Page(
data = response,
prevKey = if (position == STARTING_PAGE_INDEX) null else position - 1,
nextKey = if (response.isEmpty()) null else position + 1
)
} catch (exception: IOException) {
LoadResult.Error(exception)
} catch (exception: HttpException) {
LoadResult.Error(exception)
}
}
}
This is my repository:
#Singleton
class Repository #Inject constructor(private val api: Api) {
fun getPostsResults() = Pager(
config = PagingConfig(
pageSize = 20,
maxSize = 100,
enablePlaceholders = false
),
pagingSourceFactory = { PagingSource(api) }
).liveData
}
This is Fragment in which recyclerView is:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val postAdapter = PostsAdapter(this)
binding.apply {
recyclerView.apply {
setHasFixedSize(true)
itemAnimator = null
adapter = postAdapter.withLoadStateHeaderAndFooter(
header = PostsLoadStateAdapter { postAdapter.retry() },
footer = PostsLoadStateAdapter { postAdapter.retry() }
)
buttonRetry.setOnClickListener {
postAdapter.retry()
}
}
}
viewModel.posts.observe(viewLifecycleOwner) {
postAdapter.submitData(viewLifecycleOwner.lifecycle, it)
}
postAdapter.addLoadStateListener { loadState ->
binding.apply {
progressBar.isVisible = loadState.source.refresh is LoadState.Loading
recyclerView.isVisible = loadState.source.refresh is LoadState.NotLoading
buttonRetry.isVisible = loadState.source.refresh is LoadState.Error
textViewError.isVisible = loadState.source.refresh is LoadState.Error
// empty view
if (loadState.source.refresh is LoadState.NotLoading && loadState.append.endOfPaginationReached && postAdapter.itemCount < 1) {
recyclerView.isVisible = false
textViewEmpty.isVisible = true
} else {
textViewEmpty.isVisible = false
}
}
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
override fun onItemClick(post: SocialNetworkPost) {
val action = HomeFragmentDirections.actionHomeFragmentToDetailsFragment(post)
findNavController().navigate(action)
}
}
This is my repository:
class HomeViewModel #ViewModelInject constructor(private val repository: Repository) : ViewModel() {
val posts = repository.getPostsResults().cachedIn(viewModelScope)
}
This is my adapter:
class PostsAdapter(private val listener: OnItemClickListener) : PagingDataAdapter<SocialNetworkPost, PostsAdapter.PostViewHolder>(POSTS_COMPARATOR) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PostViewHolder {
val binding = ItemPostBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return PostViewHolder(binding)
}
override fun onBindViewHolder(holder: PostViewHolder, position: Int) {
val currentItem = getItem(position)
if (currentItem != null) {
Log.d("PostAdapter", "onBindViewHolder: if")
holder.bind(currentItem)
} else {
Log.d("PostAdapter", "onBindViewHolder: else")
}
}
inner class PostViewHolder(private val binding: ItemPostBinding) : RecyclerView.ViewHolder(binding.root) {
init {
binding.root.setOnClickListener {
val position = bindingAdapterPosition
if (position != RecyclerView.NO_POSITION) {
val item = getItem(position)
if (item != null) listener.onItemClick(item)
}
}
}
fun bind(post: SocialNetworkPost) {
Log.d("PostAdapter", "bind: $post")
binding.apply {
Glide.with(itemView)
.load(post.accountIcon)
.centerCrop()
.transition(DrawableTransitionOptions.withCrossFade())
.error(R.drawable.ic_account)
.into(imageViewProfilePicture)
textViewName.text = post.accountName
textViewDescription.text = post.description
Glide.with(itemView)
.load(post.descriptionImage)
.centerCrop()
.transition(DrawableTransitionOptions.withCrossFade())
.error(R.drawable.empty)
.into(imageViewDescription)
textViewLikesAmount.text = "Likes: ${post.likesAmount}"
textViewCommentsAmount.text = "Comments: ${post.commentsAmount}"
}
}
}
interface OnItemClickListener {
fun onItemClick(post: SocialNetworkPost)
}
companion object {
private val POSTS_COMPARATOR = object : DiffUtil.ItemCallback<SocialNetworkPost>() {
override fun areItemsTheSame(oldItem: SocialNetworkPost, newItem: SocialNetworkPost) = oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: SocialNetworkPost, newItem: SocialNetworkPost) = oldItem == newItem
}
}
}
RecyclerView needs a layoutManager, you can set it in the xml:
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
or programatically:
recyclerView.layoutManager = LinearLayoutManager(requireContext())

android: kotlin: Data not loading in adapter while implementing paging 3

I was implementing paging 3 in my app.
I tried to follow google codelabs and overview on paging 3 from documentation
However it fails to display the data in the adapter, I put logs in the adapter and no logs from adapter show up on logcat.
I tried to debug the codelabs code and data flow was exactly as mine, since I can't( or don't know yet) how to intervene paging which is received in flow in fragment, I am unable to tell if my fragment receives the data. Also I have never used adapter.submitdata() and new to coroutines, I am not sure if it could be the scope which is the problem.
Any help is appreciated. Thanks in advance.
Have a nice day.
fragment
transactionsAdapterPaging = TransactionsPagedListAdapter()
homeFragmentDataBinding.transactionsRV.apply {
layoutManager = LinearLayoutManager(requireContext())
setHasFixedSize(true)
adapter = transactionsAdapterPaging
}
homeFragmentDataBinding.transactionsRV.adapter = transactionsAdapterPaging
lifecycleScope.launch {
mainViewModel.getDataFromRemote().collectLatest {
Log.e(TAG, "initializeTransactionAdapter: ++++++++====== " + it.toString())
transactionsAdapterPaging.submitData(it)
}
}
viewmodel
private var currentSearchResult: Flow<PagingData<TransactionDetail>>? = null
fun getDataFromRemote(): Flow<PagingData<TransactionDetail>> {
val lastResult = currentSearchResult
val newResult: Flow<PagingData<TransactionDetail>> = mainRepository.getDataFromRemote()
.cachedIn(viewModelScope)
currentSearchResult = newResult
return newResult
}
repository
fun getDataFromRemote(): Flow<PagingData<TransactionDetail>> {
return Pager(
config = PagingConfig(pageSize = 1, enablePlaceholders = false),
pagingSourceFactory = { TransactionsDataSource() }
).flow
}
data source
class TransactionsDataSource() : PagingSource<Int, TransactionDetail>() {
private val TAG = "UserDataSource"
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, TransactionDetail> {
val position = params.key ?: 1
return try {
lateinit var transactionDetail: List<TransactionDetail>
var response: Response<Any?>? = NewAPIServiceClient.getAllTransactionsPaging(
Application.getInstance(),
Prefs.getString(Constants.CARD_UUID_IN_USE, ""),
position,
null
)
Log.e(TAG, "load: " + response)
if (response != null && response.isSuccessful) {
if (response?.body() != null) {
val gson = Gson()
try {
val jsonObject1 = JSONObject(gson.toJson(response?.body()))
val transactionsGson = jsonObject1.getJSONArray(Constants.PN_DATA_NEW)
transactionDetail = gson.fromJson(
transactionsGson.toString(),
object : TypeToken<List<TransactionDetail>?>() {}.type
)
} catch (e:Exception){
}
}
}
val repos = transactionDetail
Log.e(TAG, "load: ===== " + repos.toString() )
return LoadResult.Page(
data = repos,
prevKey = if (position == 1) null else position - 1,
nextKey = if (repos.isEmpty()) null else position + 1
)
} catch (exception: IOException) {
Log.e(TAG, "load: 11111111111111111 " + exception )
LoadResult.Error(exception)
} catch (exception: HttpException) {
Log.e(TAG, "load: 11111111111111111 " + exception )
LoadResult.Error(exception)
}
}
}
adapter
class TransactionsPagedListAdapter() :
PagingDataAdapter<TransactionDetail, RepoViewHolder>(
DIFF_CALLBACK
) {
private val TAG = "AdapterPaging"
private var serviceListFiltered: List<TransactionDetail>? = null
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): RepoViewHolder {
Log.e(TAG, "onCreateViewHolder: ")
val itemBinding: RvListItemTransactionsHomeBinding =
RvListItemTransactionsHomeBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
return RepoViewHolder(itemBinding)
}
override fun onBindViewHolder(
holder: RepoViewHolder,
position: Int
) {
Log.e(TAG, "onBindViewHolder: ")
val repoItem = getItem(position)
if (repoItem != null) {
(holder).bind(repoItem, position)
}
}
override fun getItemCount(): Int {
return if (serviceListFiltered == null) {
0
} else serviceListFiltered!!.size
}
companion object {
val DIFF_CALLBACK = object : DiffUtil.ItemCallback<TransactionDetail>() {
override fun areItemsTheSame(oldItem: TransactionDetail, newItem: TransactionDetail) =
oldItem.uuid == newItem.uuid
override fun areContentsTheSame(
oldItem: TransactionDetail,
newItem: TransactionDetail
) =
oldItem == newItem
}
}
}
class RepoViewHolder internal constructor(itemBinding: RvListItemTransactionsHomeBinding) :
RecyclerView.ViewHolder(itemBinding.getRoot()), View.OnClickListener {
private val mDataBinding: RvListItemTransactionsHomeBinding = itemBinding
var rootView: View
fun bind(invoice: TransactionDetail, position: Int) {
rootView.transaction_text_title.text = invoice.merchant?.merchantName
var amountWithSymbol =
Utilities.getFormattedAmount(
Application.getInstance()?.applicationContext,
invoice.amount.toString()
)
rootView.transaction_amount.text = amountWithSymbol
rootView.transactions_date.text = invoice.timestamp
}
override fun onClick(view: View) {
if (adapterPosition > RecyclerView.NO_POSITION) {
}
}
init {
val itemView: View = itemBinding.getRoot()
rootView = mDataBinding.constraintContainer
itemView.setOnClickListener(this)
}
}
`override fun getItemCount(): Int {
return if (serviceListFiltered == null) {
0
} else serviceListFiltered!!.size
}`
Don't override getItemCount in your recyclerview adapter.

Categories

Resources