I checked the previous solutions for paging in this chapter. either it was deleted (deleted link) or i didn’t understand anything in it… in addition that it was made with paging 2 which is deprecated now…
I spent days learning about paging 3, and I created the needed files/codes, but i’m getting an empty recycler view only… nothing loads in it…
I posted the issue on Stackoverflow… no one mentioned any solution.
I posted the issue on fiverr the programmers after reading the code didn’t want to work on it. for any reason they had (after investigating the code), one of them said that the code is good but have to debug it and know what is the problem.
finally one programmer agreed to do it… we spent hours together (he used remote control to do the work on my pc, so that i can learn what is the issue). but he couldn’t find the problem.
after all he suggested that he do a separate code for me that applies paging 3 and take the data from API directly not from the repository in photogallery… but that is not the solution i need.
please check the code and tell me if there is something wrong in it or any possible fixes…
I added the page parameter to the link in the PhotoInterceptor.kt
so that i can link it with the PhotoPaging.kt
class, and added a variable for page number to call next/previous page as follows.
package com.bignerdranch.android.photogallery.api
import android.app.Application
import okhttp3.HttpUrl
import okhttp3.Interceptor
import okhttp3.Request
import okhttp3.Response
private const val API_KEY = "a4c9*****ac78"
class PhotoInterceptor : Interceptor { //page. 540
var page = 1
override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest: Request = chain.request()
System.out.println("here is the page : " + page)
val newUrl: HttpUrl = originalRequest.url().newBuilder()
.addQueryParameter("api_key", API_KEY)
.addQueryParameter("format", "json")
.addQueryParameter("nojsoncallback", "1")
.addQueryParameter("extras", "url_s")
.addQueryParameter("safesearch", "1")
/**
* added tha page to the link Query to use it in paging.
*/
.addQueryParameter("page", "$page")
.build()
val newRequest: Request = originalRequest.newBuilder()
.url(newUrl)
.build()
return chain.proceed(newRequest)
}
}
then i created PhotoPaging.kt
that had the loader
package com.bignerdranch.android.photogallery
import android.app.Application
import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Transformations
import androidx.paging.PagingSource
import androidx.paging.PagingState
import com.bignerdranch.android.photogallery.api.*
import kotlin.Exception
private const val TAG = "PhotoPaging"
class PhotoPaging(private val app : Application) : PagingSource<Int, GalleryItem>() {
override fun getRefreshKey(state: PagingState<Int, GalleryItem>): Int? {
// Try to find the page key of the closest page to anchorPosition, from
// either the prevKey or the nextKey, but you need to handle nullability
// here:
// * prevKey == null -> anchorPage is the first page.
// * nextKey == null -> anchorPage is the last page.
// * both prevKey and nextKey null -> anchorPage is the initial page, so
// just return null.
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, GalleryItem> {
return try {
//val page= params.key?: 1
val page = PhotoInterceptor().page
val response =PhotoGalleryViewModel(app).galleryItemLiveData.value
Log.d(TAG, "$response")
LoadResult.Page(
data = response!!,
prevKey = if (page == 1) null else page.minus(1),
nextKey = if (page == 5) null else page.plus(1),
)
}catch (e: Exception){
LoadResult.Error(e)
}
}
}
inside the PhotoGalleryViewModel.kt
i added the flow
to link the recyclerviewer with the PhotoPaging.kt
package com.bignerdranch.android.photogallery
import android.app.Application
import android.content.Context
import androidx.lifecycle.*
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.cachedIn
import retrofit2.http.Query
class PhotoGalleryViewModel(private val app : Application) : AndroidViewModel(app){
//page 550
val galleryItemLiveData : LiveData<List<GalleryItem>>
private val flickrFetchr = FlickrFetchr() //page 546
private val mutableSearchTerm = MutableLiveData<String>()
val searchTerm : String //page 552
get() = mutableSearchTerm.value ?: ""
init {
mutableSearchTerm.value=QueryPreferences.getStoredQuery(app)
galleryItemLiveData = /*FlickrFetchr().searchPhotos("Birds")*/
Transformations.switchMap(mutableSearchTerm){ searchTerm ->
if (searchTerm.isBlank()) flickrFetchr.fetchPhotos() else flickrFetchr.searchPhotos(searchTerm) //page.551
}
}
fun fetchPhotos(query: String = ""){
QueryPreferences.setStoredQuery(app, query)
mutableSearchTerm.value = query
}
/**
* Set up a stream of PagingData
*/
val flow = Pager(
// Configure how data is loaded by passing additional properties to
// PagingConfig, such as prefetchDistance.
PagingConfig(pageSize = 5)
) {
PhotoPaging(app)
}.flow
.cachedIn(viewModelScope)
}
after that in the PhotoGalleryFragment.kt
I changed the PhotoAdapter and added the launcher of the paging3
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
photoGalleryViewModel.galleryItemLiveData.observe(
viewLifecycleOwner,
Observer { galleryItems ->
Log.d(TAG, "Have gallery items from view model $galleryItems")
// photoRecyclerView.adapter = PhotoAdapter(galleryItems)
})
photoRecyclerView.adapter = PhotoAdapter(PhotoComparator)
val pagingAdapter = PhotoAdapter(PhotoComparator)
lifecycleScope.launch {
photoGalleryViewModel.flow.collectLatest { pagingData ->
pagingAdapter.submitData(pagingData)
}
}
photoRecyclerView.viewTreeObserver.apply {
if (isAlive) {
addOnGlobalLayoutListener {
GridLayoutManager.LayoutParams.MATCH_PARENT
}
}
}
}
PhotoAdapter and PhotoComparator
private inner class PhotoAdapter(diffCallback: DiffUtil.ItemCallback<GalleryItem>) :
PagingDataAdapter<GalleryItem, PhotoHolder>(diffCallback){
override fun onBindViewHolder(holder: PhotoHolder, position: Int) {
// val galleryItem = galleryItems[position]
// holder.bindGalleryItem(galleryItem)
val item: GalleryItem? = getItem(position)
holder.bindGalleryItem(item!!)
val placeholder: Drawable = ContextCompat.getDrawable(
requireContext(),
R.drawable.hala_atamleh
) ?: ColorDrawable()
holder.bindDrawable(placeholder)
thumbnailDownloader.queueThumbnail(holder, item.url)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PhotoHolder {
val view = layoutInflater.inflate(
R.layout.list_item_gallery,
parent,
false
) as ImageView
return PhotoHolder(view)
}
}
object PhotoComparator : DiffUtil.ItemCallback<GalleryItem>() {
override fun areItemsTheSame(oldItem: GalleryItem, newItem: GalleryItem): Boolean {
// Id is unique.
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: GalleryItem, newItem: GalleryItem): Boolean {
return oldItem == newItem
}
}
I have no errors… yet i only get an empty view
PLEASE HELP!