Issue
I am trying to display data from an API. Because of a HttpLoggingInterceptor, I am sure that I receive the data, but it doesn't get displayed in my recyclerview. Is there anyone that knows what I might be doing wrong?
I have also tried just putting a textview in the recyclerview, but even that doesn't show.
After some more debugging, I found out that "adapter.submitlist(it!!.docs)" in BookListFragment.kt never happens. Why would that be?
I would greatly appreciate any help, thanks in advance!
fragment_book_list.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="booklist.BookListFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvBooks"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="1dp"
android:layout_marginTop="1dp"
android:layout_marginEnd="1dp"
android:layout_marginBottom="1dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>
<TextView
android:id="@+id/notfounderror"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="185dp"
android:layout_marginTop="250dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
book_in_list.xml
<layout xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="book"
type="be.nienke.eindopdracht.api.Q" />
<variable
name="clicklistener"
type="be.nienke.eindopdracht.book.BookClickListener" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{() -> clicklistener.onClick(book)}">
<TextView
android:id="@+id/bookTitleTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="@{book.title}"
android:textSize="34sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/authorTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="30dp"
android:layout_marginTop="8dp"
android:text="@string/auteur"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/bookTitleTv" />
<TextView
android:id="@+id/bookAuthorTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_marginTop="8dp"
android:text="@{book.author_name[0]}"
android:textSize="12sp"
app:layout_constraintStart_toEndOf="@+id/authorTv"
app:layout_constraintTop_toBottomOf="@+id/bookTitleTv" />
<TextView
android:id="@+id/numberOfPagesTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_marginTop="8dp"
android:text="@string/aantal_pagina_s"
app:layout_constraintStart_toEndOf="@+id/bookAuthorTv"
app:layout_constraintTop_toBottomOf="@+id/bookTitleTv" />
<TextView
android:id="@+id/bookNumberOfPagesTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_marginTop="8dp"
android:text="@{book.number_of_pages_median.toString()}"
android:textSize="12sp"
app:layout_constraintStart_toEndOf="@+id/numberOfPagesTv"
app:layout_constraintTop_toBottomOf="@+id/bookTitleTv" />
<TextView
android:id="@id/languageTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_marginTop="8dp"
android:text="@string/taal"
app:layout_constraintStart_toEndOf="@+id/bookNumberOfPagesTv"
app:layout_constraintTop_toBottomOf="@+id/bookTitleTv"/>
<TextView
android:id="@+id/bookLanguageTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_marginTop="8dp"
android:text="@{book.language[0].toString()}"
android:textSize="12sp"
app:layout_constraintStart_toEndOf="@+id/languageTv"
app:layout_constraintTop_toBottomOf="@+id/bookTitleTv" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
BookListFragment.kt
package be.nienke.eindopdracht.booklist
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import be.nienke.eindopdracht.R
import be.nienke.eindopdracht.book.BookAdapter
import be.nienke.eindopdracht.book.BookClickListener
import be.nienke.eindopdracht.databinding.FragmentBookListBinding
import be.nienke.eindopdracht.user.UserSingelton
class BookListFragment : Fragment() {
private lateinit var binding: FragmentBookListBinding
private lateinit var viewmodel: BookListViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_book_list, container, false)
val user = UserSingelton.instance().user
val search = BookListFragmentArgs.fromBundle(requireArguments()).search
val viewModelFactory = BookListViewModelFactory(user!!, search)
viewmodel = ViewModelProvider(this, viewModelFactory).get(BookListViewModel::class.java)
binding.lifecycleOwner = this
val adapter = BookAdapter(BookClickListener {
viewmodel.onBookClicked(it)
})
binding.rvBooks.adapter = adapter
val manager = LinearLayoutManager(activity)
binding.rvBooks.layoutManager = manager
viewmodel._nonFoundError.observe(viewLifecycleOwner, Observer {
it?.let {
binding.notfounderror.setError(it)
}
})
viewmodel._baseBookTitle.observe(viewLifecycleOwner, Observer {
it?.let {
adapter.submitList(it!!.docs)
}
})
viewmodel._book.observe(viewLifecycleOwner, Observer {
it?.let {
requireView().findNavController().navigate(BookListFragmentDirections.actionBookListFragmentToBookFragment(it))
viewmodel.navigateToBookFinished()
}
})
binding.setLifecycleOwner(this)
return binding.root
}
}
BookListViewModel.kt
import android.app.Application
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import be.nienke.eindopdracht.BookAPI
import be.nienke.eindopdracht.Database.User
import be.nienke.eindopdracht.api.Base_BookTitle
import be.nienke.eindopdracht.api.Q
import kotlinx.coroutines.launch
import java.lang.Exception
class BookListViewModel(__user: User, __search : String): ViewModel() {
var _user = MutableLiveData<User>()
val user : LiveData<User>
get() {
return _user
}
var _search = MutableLiveData<String>()
val search : LiveData<String>
get(){
return _search
}
var _baseBookTitle = MutableLiveData<Base_BookTitle?>()
val baseBooktitle : LiveData<Base_BookTitle?>
get(){
return _baseBookTitle
}
var _nonFoundError = MutableLiveData<String?>()
val nonFoundError : LiveData<String?>
get() {
return _nonFoundError
}
var _book = MutableLiveData<Q?>()
val book : LiveData<Q?>
get() {
return _book
}
init {
_user.value = __user
_search.value = __search
_nonFoundError.value = null
viewModelScope.launch {
try {
_baseBookTitle.value = BookAPI.retrofitService.getBooksByTitle(_search.value!!)
if(_baseBookTitle.value!!.numFound == 0){
_nonFoundError.value == "Er zijn geen boeken gevonden voor deze zoekopdracht."
}
}
catch (e: Exception){
print(e.localizedMessage)
}
}
}
fun onBookClicked(book: Q){
_book.value = book
}
fun navigateToBookFinished(){
_book.value = null
}
}
BookAdapter.kt
package be.nienke.eindopdracht.book
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import be.nienke.eindopdracht.api.Q
import be.nienke.eindopdracht.databinding.BookInListBinding
import be.nienke.eindopdracht.databinding.FragmentBookBinding
class BookAdapter(val clickListener: BookClickListener ): androidx.recyclerview.widget.ListAdapter<Q, BookAdapter.ViewHolder>(BookDiffCallBack()) {
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(getItem(position)!!, clickListener)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder.from(parent)
}
class ViewHolder private constructor( val binding: BookInListBinding): RecyclerView.ViewHolder(binding.root){
fun bind(item: Q, clickListener: BookClickListener){
binding.book = item
binding.clicklistener = clickListener
binding.executePendingBindings()
}
companion object{
fun from(parent: ViewGroup): ViewHolder{
val layoutInflater = LayoutInflater.from(parent.context)
val binding = BookInListBinding.inflate(layoutInflater, parent, false)
return ViewHolder(binding)
}
}
}
}
class BookDiffCallBack : DiffUtil.ItemCallback<Q>(){
override fun areItemsTheSame(oldItem: Q, newItem: Q): Boolean {
return oldItem.key== newItem.key
}
override fun areContentsTheSame(oldItem: Q, newItem: Q): Boolean {
return oldItem == newItem
}
}
class BookClickListener(val clicklistener: (q: Q)-> Unit){
fun onClick(q: Q)= clicklistener(q)
}
Logcat when I test the API
D/OkHttp: <-- 200 OK http://openlibrary.org/search.json?q=Is%20er%20iets (738ms)
D/OkHttp: Server: nginx/1.18.0 (Ubuntu)
Date: Sun, 09 Jan 2022 18:11:27 GMT
Content-Type: application/json
Transfer-Encoding: chunked
Connection: keep-alive
Access-Control-Allow-Origin: *
Access-Control-Allow-Method: GET, OPTIONS
Access-Control-Max-Age: 86400
D/OkHttp: X-OL-Stats: "SR 1 0.117 TT 0 0.118"
Referrer-Policy: no-referrer-when-downgrade
D/OkHttp: {
D/OkHttp: "numFound": 1,
"start": 0,
"numFoundExact": true,
"docs": [
{
"key": "/works/OL3764515W",
D/OkHttp: "type": "work",
"seed": [
"/books/OL1286559M",
"/works/OL3764515W",
D/OkHttp: "/subjects/history",
D/OkHttp: "/subjects/television_broadcasting",
"/subjects/television_programs",
"/subjects/place:netherlands",
"/authors/OL662730A"
D/OkHttp: ],
"title": "Is er nog iets leuks vanavond?",
"title_suggest": "Is er nog iets leuks vanavond?",
"has_fulltext": false,
"edition_count": 1,
"edition_key": [
"OL1286559M"
],
"publish_date": [
"1991"
D/OkHttp: ],
D/OkHttp: "publish_year": [
1991
],
D/OkHttp: "first_publish_year": 1991,
"number_of_pages_median": 216,
D/OkHttp: "lccn": [
"92145492"
],
"publish_place": [
D/OkHttp: "Utrecht"
D/OkHttp: ],
"oclc": [
D/OkHttp: "27188277"
],
"lcc": [
"PN-1992.30000000.N4 V43 1991"
],
"isbn": [
"9027428840",
"9789027428844"
D/OkHttp: ],
"last_modified_i": 1605489709,
"ebook_count_i": 0,
"publisher": [
"Spectrum"
],
"language": [
"dut"
],
D/OkHttp: "author_key": [
"OL662730A"
],
D/OkHttp: "author_name": [
"Bert van der Veer"
],
"place": [
"Netherlands"
],
D/OkHttp: "subject": [
"History",
D/OkHttp: "Television broadcasting",
"Television programs"
],
"id_goodreads": [
"6427531"
],
"id_librarything": [
"5541711"
],
"publisher_facet": [
"Spectrum"
D/OkHttp: ],
"place_key": [
"netherlands"
],
"subject_facet": [
"History",
"Television broadcasting",
D/OkHttp: "Television programs"
],
"_version_": 1715091264038240257,
"place_facet": [
"Netherlands"
],
"lcc_sort": "PN-1992.30000000.N4 V43 1991",
"author_facet": [
"OL662730A Bert van der Veer"
],
"subject_key": [
"history",
"television_broadcasting",
"television_programs"
]
}
],
"num_found": 1,
D/OkHttp: "q": "Is er iets",
"offset": null
}
D/OkHttp: <-- END HTTP (2865-byte body)
Base_BookTitle.kt
package be.nienke.eindopdracht.api
data class Base_BookTitle (
val numFound : Int?,
val start : Int?,
val numFoundExact : Boolean?,
val docs : List<Q?>,
val num_found : Int?,
val q : String?,
val offset : String?
)
Q
package be.nienke.eindopdracht.api
import android.os.Parcelable
import kotlinx.android.parcel.Parcelize
@Parcelize
data class Q (
val key : String?,
val type : String?,
val seed : List<String?>,
val title : String?,
val title_suggest : String?,
val has_fulltext : Boolean?,
val edition_count : Int?,
val edition_edition_key : List<String?>,
val publish_date : List<String?>,
val publish_year : List<Int?>,
val first_publish_year : Int?,
val number_of_pages_median : Int?,
val lccn : List<Int?>,
val publish_place : List<String?>,
val oclc : List<Int?>,
val contributor : List<String?>,
val lcc : List<String?>,
val ddc : List<Double?>?,
val isbn : List<Int?>,
val last_modified_i : Int?,
val ebook_count_i : Int?,
val ia : List<String?>,
val public_scan_b : Boolean?,
val ia_collection_s : String?,
val lending_edition_s : String?,
val lending_identifier_s : String?,
val printdisabled_s : String?,
val cover_edition_cover_edition_key : String?,
val cover_i : Int?,
val publisher : List<String?>,
val language : List<String?>,
val author_author_key : List<String?>,
val author_name : List<String?>,
val author_alternative_name : List<String?>,
val person : List<String?>,
val place : List<String?>,
val subject : List<String?>,
val time : List<String?>,
val id_alibris_id : List<Int?>,
val id_amazon : List<String?>,
val id_canadian_national_library_archive : List<Int?>,
val id_depósito_legal : List<String?>,
val id_goodreads : List<Int?>,
val id_google : List<String?>,
val id_librarything : List<Int?>,
val id_overdrive : List<String?>,
val id_paperback_swap : List<Int?>,
val id_wikidata : List<String?>,
val ia_loaded_id : List<String?>,
val ia_box_id : List<String?>,
val publisher_facet : List<String?>,
val person_person_key : List<String?>,
val place_place_key : List<String?>,
val time_facet : List<String?>,
val person_facet : List<String?>,
val subject_facet : List<String?>,
val _version_ : Int?,
val place_facet : List<String?>,
val lcc_sort : String?,
val author_facet : List<String?>,
val subject_subject_key : List<String?>,
val ddc_sort : Double?,
val time_time_key : List<String?>
): Parcelable
I got the Base_BookTitle.kt and Q.kt from an online json to kotlin converter.
Solution
I finally found what the problem was. I set a breakpoint at print(e.localizedMessage)
to find the problem.
Apparently Q.kt class that I generated, was wrong.
Q.kt
package be.nienke.eindopdracht.api
import android.os.Parcelable
import kotlinx.android.parcel.Parcelize
@Parcelize
data class Q (
val key : String?,
val type : String?,
val seed : List<String?>,
val title : String?,
val title_suggest : String?,
val has_fulltext : Boolean?,
val edition_count : Int?,
val edition_edition_key : List<String?>,
val publish_date : List<String?>,
val publish_year : List<Int?>,
val first_publish_year : Int?,
val number_of_pages_median : Int?,
val lccn : List<Int?>,
val publish_place : List<String?>,
val oclc : List<Int?>,
val contributor : List<String?>,
val lcc : List<String?>,
val ddc : List<Double?>?,
val isbn : List<Int?>,
val last_modified_i : Int?,
val ebook_count_i : Int?,
val ia : List<String?>,
val public_scan_b : Boolean?,
val ia_collection_s : String?,
val lending_edition_s : String?,
val lending_identifier_s : String?,
val printdisabled_s : String?,
val cover_edition_cover_edition_key : String?,
val cover_i : Int?,
val publisher : List<String?>,
val language : List<String?>,
val author_author_key : List<String?>,
val author_name : List<String?>,
val author_alternative_name : List<String?>,
val person : List<String?>,
val place : List<String?>,
val subject : List<String?>,
val time : List<String?>,
val id_alibris_id : List<Int?>,
val id_amazon : List<String?>,
val id_canadian_national_library_archive : List<Int?>,
val id_depósito_legal : List<String?>,
val id_goodreads : List<Int?>,
val id_google : List<String?>,
val id_librarything : List<Int?>,
val id_overdrive : List<String?>,
val id_paperback_swap : List<Int?>,
val id_wikidata : List<String?>,
val ia_loaded_id : List<String?>,
val ia_box_id : List<String?>,
val publisher_facet : List<String?>,
val person_person_key : List<String?>,
val place_place_key : List<String?>,
val time_facet : List<String?>,
val person_facet : List<String?>,
val subject_facet : List<String?>,
val _version_ : Int?,
val place_facet : List<String?>,
val lcc_sort : String?,
val author_facet : List<String?>,
val subject_subject_key : List<String?>,
val ddc_sort : Double?,
val time_time_key : List<String?>
): Parcelable
See the thing was, when the generator thought that a List<Int>
was being returned, it was actually a list of Strings.
So, I changed every List<Int>
to List<String>
Solution:
package be.nienke.eindopdracht.api
import android.os.Parcelable
import kotlinx.android.parcel.Parcelize
@Parcelize
data class Q (
val key : String?,
val type : String?,
val seed : List<String?>?,
val title : String?,
val title_suggest : String?,
val has_fulltext : Boolean?,
val edition_count : Int?,
val edition_edition_key : List<String?>??,
val publish_date : List<String?>?,
val publish_year : List<Int?>?,
val first_publish_year : Int?,
val number_of_pages_median : Int?,
val lccn : List<String?>?,
val publish_place : List<String?>?,
val oclc : List<String?>?,
val contributor : List<String?>?,
val lcc : List<String?>?,
val ddc : List<String?>?,
val isbn : List<String?>?,
val last_modified_i : Int?,
val ebook_count_i : Int?,
val ia : List<String?>?,
val public_scan_b : Boolean?,
val ia_collection_s : String?,
val lending_edition_s : String?,
val lending_identifier_s : String?,
val printdisabled_s : String?,
val cover_edition_cover_edition_key : String?,
val cover_i : Int?,
val publisher : List<String?>?,
val language : List<String?>?,
val author_author_key : List<String?>?,
val author_name : List<String?>?,
val author_alternative_name : List<String?>?,
val person : List<String?>?,
val place : List<String?>?,
val subject : List<String?>?,
val time : List<String?>?,
val id_alibris_id : List<String?>?,
val id_amazon : List<String?>?,
val id_canadian_national_library_archive : List<String?>?,
val id_depósito_legal : List<String?>?,
val id_goodreads : List<String?>?,
val id_google : List<String?>?,
val id_librarything : List<String?>?,
val id_overdrive : List<String?>?,
val id_paperback_swap : List<String?>?,
val id_wikidata : List<String?>?,
val ia_loaded_id : List<String?>?,
val ia_box_id : List<String?>?,
val publisher_facet : List<String?>?,
val person_person_key : List<String?>?,
val place_place_key : List<String?>?,
val time_facet : List<String?>?,
val person_facet : List<String?>?,
val subject_facet : List<String?>?,
val _version_ : Long?,
val place_facet : List<String?>?,
val lcc_sort : String?,
val author_facet : List<String?>?,
val subject_subject_key : List<String?>?,
val ddc_sort : String?,
val time_time_key : List<String?>?
): Parcelable
And now my data gets displayed like it should.
Answered By - Nienke
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.