RecyclerView 不显示数据

RecyclerView does not display data

我正在尝试显示来自 API 的数据。 由于 HttpLoggingInterceptor,我确信我收到了数据,但它没有显示在我的 recyclerview 中。有谁知道我可能做错了什么吗?

我也试过在 recyclerview 中放置一个文本视图,但即使那样也没有显示。

经过更多的调试,我发现 BookListFragment.kt 中的“adapter.submitlist(it!!.docs)”从来没有发生过。为什么会这样?

非常感谢任何帮助,提前致谢!

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 当我测试 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

我从在线 json 到 kotlin 转换器获得了 Base_BookTitle.kt 和 Q.kt。

我终于找到问题所在了。我在print(e.localizedMessage)设置断点来查找问题。

显然我生成的 Q.kt class 是错误的。

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

事情是这样的,当生成器认为返回一个 List<Int> 时,它实际上是一个字符串列表。

因此,我将每个 List<Int> 更改为 List<String>

解法:

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

现在我的数据得到了应有的显示。