在 Kotlin 中将片段解析为 recyclerview 时出错

Error parsing fragment to recyclerview in Kotlin

作为我的一门课程的一部分,我第一次在 Android Studio 中使用 API。为此,我使用 OkHttp。但是,当我启动我的应用程序时出现以下错误。我猜是因为 JSON 没有解析好,但我找不到解决方案。

如果有人能帮助我那就太好了!

谢谢

The error screenshot

MyMarqueRecyclerViewAdapter.kt


import android.content.Intent
import android.os.Bundle
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import com.google.gson.GsonBuilder
import kotlinx.android.synthetic.main.fragment_marque_list.*
import okhttp3.*
import java.io.IOException

/**
 * A fragment representing a list of Items.
 */
class MarqueFragment : Fragment(), OnMarqueClickListener {

    private var columnCount = 1

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        arguments?.let {
            columnCount = it.getInt(ARG_COLUMN_COUNT)
        }

        fetchJson()
    }

    fun fetchJson() {
        val url = "https://tp3.infomobile.app/api/v1/brand"

        val request = Request.Builder().url(url).build()

        val lesmarques = OkHttpClient()
        lesmarques.newCall(request).enqueue(object: Callback {
            override fun onResponse(call: Call, response: Response) {
                val body = response.body?.string()
                println(body)

                val gson = GsonBuilder().create()

                val homeFeed = gson.fromJson(body, HomeFeed::class.java)
                println(homeFeed)
                activity?.runOnUiThread {
                    recyclerView_main.adapter = MyMarqueRecyclerViewAdapter(homeFeed)
                }
            }

            override fun onFailure(call: Call, e: IOException) {
                println("Failed to execute request")
            }
        })
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val view = inflater.inflate(R.layout.fragment_marque_list, container, false)

        // Set the adapter
        if (view is RecyclerView) {
            with(view) {
                layoutManager = when {
                    columnCount <= 1 -> LinearLayoutManager(context)
                    else -> GridLayoutManager(context, columnCount)
                }
                //adapter = MyMarqueRecyclerViewAdapter(homeFeed)
            }
        }
        return view
    }

    override fun onMarqueItemClicked(position: Int) {
        Toast.makeText(this.context, "ça marche", Toast.LENGTH_LONG).show()
        val intent = Intent(this@MarqueFragment.requireContext(),MainActivity2::class.java)
        startActivity(intent)
    }

    companion object {

        // TODO: Customize parameter argument names
        const val ARG_COLUMN_COUNT = "column-count"

        // TODO: Customize parameter initialization
        @JvmStatic
        fun newInstance(columnCount: Int) =
            MarqueFragment().apply {
                arguments = Bundle().apply {
                    putInt(ARG_COLUMN_COUNT, columnCount)
                }
            }
    }
}

class HomeFeed(val marques: List<Marque>)

class Marque(val id: Int, val name: String)

MarqueFragment.kt


import android.content.Intent
import androidx.recyclerview.widget.RecyclerView
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.TextView

import ca.ulaval.ima.tp3.placeholder.PlaceholderContent.PlaceholderItem
import ca.ulaval.ima.tp3.databinding.FragmentMarqueBinding

/**
 * [RecyclerView.Adapter] that can display a [PlaceholderItem].
 * TODO: Replace the implementation with code for your data type.
 */
class MyMarqueRecyclerViewAdapter(val homeFeed: HomeFeed?) : RecyclerView.Adapter<MyMarqueRecyclerViewAdapter.ViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {

        return ViewHolder(
            FragmentMarqueBinding.inflate(
                LayoutInflater.from(parent.context),
                parent,
                false
            )
        )

    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val item = homeFeed?.marques?.get(position)
        holder.contentView.text = item.toString()
        holder.contentView.setOnClickListener{
            val context=holder.contentView.context
            val intent = Intent( context, MainActivity2::class.java)
            intent.putExtra("marque", item.toString())
            context.startActivity(intent)
        }
    }

    override fun getItemCount(): Int {
        return homeFeed?.marques!!?.count()
    }

    inner class ViewHolder(binding: FragmentMarqueBinding) : RecyclerView.ViewHolder(binding.root) {
        val contentView: TextView = binding.content

        override fun toString(): String {
            return super.toString() + " '" + contentView.text + "'"
        }
    }

}

首先,您可以使用日志跟踪崩溃,该日志清楚地表明崩溃发生在 MyMarqueRecyclerViewAdapter.kt 中的第 42 行,在 getItemCount 函数,这意味着这个函数导致它:

override fun getItemCount(): Int {
    return homeFeed?.marques!!?.count()
}

这里发生的事情是,您通过使用“!!”强制 kotlin 将可能为 null 的 属性 视为非 null 属性。运算符,这告诉 kotlin 这个 属性 在任何情况下都不会为 null,而在你的情况下这不是真的,因为它抛出 NullPointerException,你实际需要做的是允许它为 null 并提供一个如果它为 null,则使用 elvis 运算符替代值,如下所示:

override fun getItemCount(): Int {
    return homeFeed?.marques?.count() ?: 0
}

另一件事是您正在使用 count() 函数,我认为您打算使用大小 属性 代替:

override fun getItemCount(): Int {
    return homeFeed?.marques?.size ?: 0
}

其次,我注意到你的 class 声明并将内容与 json 的响应内容进行比较,似乎 json 响应包含一个名为“content”的参数,而 HomeFeed class 中的 属性 称为“marques”,这导致 GSON 不知道从哪里获取“marques”,它只知道 [=中有一个名为“content”的参数=47=] 并且它不知道如何处理它,最好的解决方案是用@SerializedName 注释注释 HomeFeed.marques 属性,并提供相应的 json 参数应该映射到这个 属性,像这样:

class HomeFeed(
    @SerializedName("content")
    val marques: List<Marque>
)

class Marque(
    @SerializedName("id")
    val id: Int,
    @SerializedName("name")
    val name: String
)

希望对您有所帮助!

一些资源:

Null Safety | Kotlin

Difference between list.count() and list.size

Parsing between Kotlin classes and Json objects using GSON