Próbuję wyświetlić spokojny API w widoku recyklejowym w fragmencie. Gdy aplikacja działa nie pokazuje błędu, ale nic nie ładuje w widoku recyklingu. Zarejestrowałem odpowiedź w pliku PagingSource i pokazuje poprawne dane, ale nadal nic nie jest wyświetlane w widoku recyklingu.

To jest mój interfejs API:

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>
}

To jest mój plik pagingsource:

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)
        }
    }
}

To jest moje repozytorium:

@Singleton
class Repository @Inject constructor(private val api: Api) {
    fun getPostsResults() = Pager(
        config = PagingConfig(
            pageSize = 20,
            maxSize = 100,
            enablePlaceholders = false
        ),
        pagingSourceFactory = { PagingSource(api) }
    ).liveData
}

Jest to fragment, w którym recyklerygiew jest:

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)
    }
}

To jest moje repozytorium:

class HomeViewModel @ViewModelInject constructor(private val repository: Repository) : ViewModel() {
    val posts = repository.getPostsResults().cachedIn(viewModelScope)
}

To jest mój 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
        }
    }
}
0
Antonio 23 październik 2020, 16:06

1 odpowiedź

Najlepsza odpowiedź

Recyklerygiew wymaga LayoutManager, możesz go ustawić w XML:

app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"

Lub programowo:

recyclerView.layoutManager = LinearLayoutManager(requireContext())
2
Stachu 23 październik 2020, 13:20