【问题标题】:Unable to parse data in recycler view using Kotlin co-routines无法使用 Kotlin 协程解析 recyclerview 中的数据
【发布时间】:2021-07-20 06:01:06
【问题描述】:

我正在从服务器获取数据并尝试使用协程在回收器视图中对其进行解析。虽然数据已成功获取,但我无法从嵌套的 json 中解析特定键。

这是 JSON 响应:

{
"status": "200",
"message": "Success",
"result": [
    {
        "_id": "60f516fa846e059e2f19c50c",
        "category": "Shirts",
        "sku": [
            {
                "name": "Oxford shirt",
                "brand": "John players",
                "price": "25",
                "color": "Blue",
                "img": "https://firebasestorage.googleapis.com/v0/b/koovs-1ff31.appspot.com/o/shi1.jpg?alt=media&token=64779194-e3b5-484f-a610-c9a20648b64c"
            },
            {
                "name": "Buttoned down",
                "brand": "Gap originals",
                "price": "45",
                "color": "Pink",
                "img": "https://firebasestorage.googleapis.com/v0/b/koovs-1ff31.appspot.com/o/shi2.jpg?alt=media&token=0b207b90-f1bc-4771-b877-809648e6bdc1"
            },
            {
                "name": "Collared",
                "brand": "Arrow",
                "price": "30",
                "color": "White",
                "img": "https://firebasestorage.googleapis.com/v0/b/koovs-1ff31.appspot.com/o/shi3.jpg?alt=media&token=2c1bb3f8-e739-4f09-acbc-aa11fed795e3"
            },
            {
                "name": "Printed",
                "brand": "John players",
                "price": "30",
                "color": "Olive green",
                "img": "https://firebasestorage.googleapis.com/v0/b/koovs-1ff31.appspot.com/o/shi4.jpg?alt=media&token=666f94bf-4769-44fe-a909-3c81ca9262f7"
            },
            {
                "name": "Hoodie",
                "brand": "Levis",
                "price": "44",
                "color": "Yellow",
                "img": "https://firebasestorage.googleapis.com/v0/b/koovs-1ff31.appspot.com/o/shi5.jpg?alt=media&token=65fccef4-a882-4278-b5df-f00eb2785bf1"
            }
        ]
    },
    {
        "_id": "60f51c37846e059e2f19c50f",
        "category": "Shoes",
        "sku": [
            {
                "name": "Sneakers",
                "brand": "Puma",
                "price": "35",
                "color": "Black and white",
                "img": "https://firebasestorage.googleapis.com/v0/b/koovs-1ff31.appspot.com/o/sho1.jpg?alt=media&token=d078988d-9e85-4313-bb4a-c9d46e09f0b9"
            },
            {
                "name": "Running shoe",
                "brand": "Nike",
                "price": "99",
                "color": "Multicolor",
                "img": "https://firebasestorage.googleapis.com/v0/b/koovs-1ff31.appspot.com/o/sho2.jpg?alt=media&token=ed2e7387-3cf6-44df-9f7d-69843eb0bcdf"
            },
            {
                "name": "Yezzy",
                "brand": "Adidas",
                "price": "349",
                "color": "Gray",
                "img": "https://firebasestorage.googleapis.com/v0/b/koovs-1ff31.appspot.com/o/sho3.jpg?alt=media&token=2c37ef76-37bb-4bdd-b36c-dea32857291f"
            },
            {
                "name": "Sneakers",
                "brand": "Puma",
                "price": "79",
                "color": "Black",
                "img": "https://firebasestorage.googleapis.com/v0/b/koovs-1ff31.appspot.com/o/sho4.jpg?alt=media&token=4acd763e-8b93-47cd-ba45-92f34af4cf83"
            },
            {
                "name": "Joyride running",
                "brand": "Nike",
                "price": "80",
                "color": "White",
                "img": "https://firebasestorage.googleapis.com/v0/b/koovs-1ff31.appspot.com/o/sho5.jpg?alt=media&token=e3780dcc-52cb-49d5-9791-e0a44870716c"
            }
        ]
    
       }
    ]
}

我想获取类别

下面是数据类

Product.kt

data class Product(
  val message: String,
  val result: List<Result>,
  val status: String
)

Result.kt

data class Result(
  val _id: String,
  val category: String,
  val sku: List<Sku>
)

ApiService.kt

interface ApiService {

  @GET("getProducts")
  suspend fun getCategories(): Response<Product>
}

CategoriesViewModel.kt

class CategoriesViewModel: ViewModel() {

private var categoryList: MutableLiveData<List<Result>> = MutableLiveData()

fun getAllCategory(): LiveData<List<Result>> {

    viewModelScope.launch(Dispatchers.IO) {

        val retrofit = RetrofitClient.getRetrofitClient().create(ApiService::class.java)
        val response = retrofit.getCategories()

        if (response.isSuccessful) {
            categoryList.postValue(response.body()!!.result)
        }
    }
    return categoryList
  }
}

CategoryAdapter.kt

class CategoryAdapter(private val context: Context,private val categories:List<Result>): RecyclerView.Adapter<CategoryAdapter.ViewHolder>() {

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CategoryAdapter.ViewHolder {
   return ViewHolder(ParentRowBinding.inflate(LayoutInflater.from(parent.context),parent,false))
}

override fun onBindViewHolder(holder: ViewHolder, position: Int) {

    val model = categories[position]
    holder.binding.catTitle.text = model.category
}

override fun getItemCount(): Int {
    return categories.size
}

class ViewHolder(val binding:ParentRowBinding): RecyclerView.ViewHolder(binding.root)
}

HomeFragment.kt

class HomeFragment : Fragment() {

private var binding: FragmentHomeBinding? = null
private lateinit var adapter: CategoryAdapter

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    // Inflate the layout for this fragment
    binding = FragmentHomeBinding.inflate(inflater,container,false)

    binding!!.parentRecycler.setHasFixedSize(true)
    binding!!.parentRecycler.layoutManager = LinearLayoutManager(activity)

    val viewModel = ViewModelProvider(this).get(CategoriesViewModel::class.java)

    viewModel.getAllCategory().observe(viewLifecycleOwner, Observer { t ->

        val len = t.size

        if(len > 0){

            for(i in 0 until len){
                Log.d("hell", t[i].category.toString())
            }
        }
    })

    return binding!!.root
}

override fun onDestroy() {
    super.onDestroy()
    binding = null
}
}

在这里,我以某种方式获取了类别,但我不知道如何将其传递给适配器,以便可以在 recyclerview 中显示它。

【问题讨论】:

  • 你能为response.body()!!.result做logcat吗?检查是否有值

标签: android json kotlin kotlin-coroutines android-mvvm


【解决方案1】:

您必须将Adapter 分配给getAllCategory 观察者内的RecyclerView,如下所示

viewModel.getAllCategory().observe(viewLifecycleOwner, Observer {
    if(!it.isNullOrEmpty()){
        binding!!.parentRecycler.adapter = CategoryAdapter(requireContext(), it)
        // This will bind the Result list received to the recycler view
    }
})

【讨论】:

    【解决方案2】:

    在您的 HomeFragment 中,您需要将适配器设置为您的 RecyclerView

     binding!!.parentRecycler.adapter= CategoryAdapter(requireContext(),t) 
    

    HomeFragment 的完整代码如下所示

    class HomeFragment : Fragment() {
    
    private var binding: FragmentHomeBinding? = null
    private lateinit var adapter: CategoryAdapter
    
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        binding = FragmentHomeBinding.inflate(inflater,container,false)
    
        binding!!.parentRecycler.setHasFixedSize(true)
        binding!!.parentRecycler.layoutManager = LinearLayoutManager(activity)
    
        val viewModel = ViewModelProvider(this).get(CategoriesViewModel::class.java)
    
        viewModel.getAllCategory().observe(viewLifecycleOwner, Observer { t ->
    
            val len = t.size
    
            if(len > 0){
                //add this line
                binding!!.parentRecycler.adapter= CategoryAdapter(requireContext(),t) 
    
                for(i in 0 until len){
                    Log.d("hell", t[i].category.toString())
                }
            }
        })
    
        return binding!!.root
    }
    
    override fun onDestroy() {
        super.onDestroy()
        binding = null
    }
    }
    

    【讨论】:

      【解决方案3】:

      我想最好的方法是从ListAdapter 继承你的适配器。因为与RecyclerView.Adapter 不同,它内置了对提交新列表的支持。

      这样更高效、更快捷,并且不需要创建新的适配器对象或完全更新 RecyclerView 中的所有项目。因为DiffUtil,只会更新实际更改的项目。

      所以你CategoryAdapter应该变成:

      class CategoryAdapter : ListAdapter<Result, CategoryAdapter.ViewHolder>(DIFF_CALLBACK) {
      
          companion object {
              val DIFF_CALLBACK = object : DiffUtil.ItemCallback<Result>() {
                  override fun areItemsTheSame(oldItem: Inspection.Type, newItem: Inspection.Type) = oldItem._id == newItem._id
                  override fun areContentsTheSame(oldItem: Inspection.Type, newItem: Inspection.Type) = oldItem == newItem
              }
          }
      
      override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CategoryAdapter.ViewHolder {
         return ViewHolder(ParentRowBinding.inflate(LayoutInflater.from(parent.context),parent,false))
      }
      
      override fun onBindViewHolder(holder: ViewHolder, position: Int) {
      
          val model = getItem(position)
          holder.binding.catTitle.text = model.category
      }
      
      class ViewHolder(val binding:ParentRowBinding): RecyclerView.ViewHolder(binding.root)
      }
      

      在你的 onCreateView 中:

      override fun onCreateView(
          inflater: LayoutInflater, container: ViewGroup?,
          savedInstanceState: Bundle?
      ): View? {
          // Inflate the layout for this fragment
          binding = FragmentHomeBinding.inflate(inflater,container,false)
      
          binding!!.parentRecycler.setHasFixedSize(true)
          binding!!.parentRecycler.layoutManager = LinearLayoutManager(activity)
          adapter = CategoryAdapter()
          binding!!.parentRecycler.adapter = adapter
      
          val viewModel = ViewModelProvider(this).get(CategoriesViewModel::class.java)
      
          viewModel.getAllCategory().observe(viewLifecycleOwner, Observer { categories ->
              adapter.submitList(categories)
          })
      
          return binding!!.root
      }
      

      请注意,在上面的代码中,您根本没有创建适配器实例,也没有将其分配给回收器。所以它根本不会显示任何东西。

      在我的示例中,我在onCreateView 中创建了一个适配器并将其分配给回收器。在 Observer 中,我们将从 ViewModel 接收到的数据传递给适配器。

      【讨论】:

        【解决方案4】:

        是的,正如@dhiraj-uchil 提到的,这里的问题不是设置缺少的适配器。我也会制作bindinglateinit 或本地。我还会提取context,并在其他地方也使用它。

         val binding = FragmentHomeBinding.inflate(inflater, container, false)
         val context = requireContext()
         binding.parentRecycler.adapter= CategoryAdapter(context, t) 
        

        关于从 JSON 制作数据类对象,我制作了一个工具,其中包括生成这样的类:

        https://kt.academy/json

        【讨论】:

          猜你喜欢
          • 2021-12-16
          • 2021-09-08
          • 1970-01-01
          • 1970-01-01
          • 2015-01-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多