无法使用 Kotlin 协同程序解析 recyclerview 中的数据
Unable to parse data in recycler view using Kotlin co-routines
我正在从服务器获取数据并尝试使用 coroutines.Though 在回收站视图中解析它 数据已成功获取,但我无法从我的嵌套 json.[= 解析特定键17=]
这里是 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"
}
]
}
]
}
我想获取类别。
下面是数据class
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 中显示它。
您必须将 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
}
})
在您的 HomeFragment 中,您需要将 Adapter 设置为您的 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
}
}
我想最好的方法是从 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 接收到的数据传递给适配器。
是的,正如@dhiraj-uchil 提到的,这里的问题不是设置丢失的适配器。我也会制作 binding
lateinit 或本地。我也会提取 context
,并在其他地方使用它。
val binding = FragmentHomeBinding.inflate(inflater, container, false)
val context = requireContext()
binding.parentRecycler.adapter= CategoryAdapter(context, t)
关于从 JSON 制作数据 class 对象,我制作了一个工具,除其他外,生成这样的 classes:
我正在从服务器获取数据并尝试使用 coroutines.Though 在回收站视图中解析它 数据已成功获取,但我无法从我的嵌套 json.[= 解析特定键17=]
这里是 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"
}
]
}
]
}
我想获取类别。
下面是数据class
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 中显示它。
您必须将 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
}
})
在您的 HomeFragment 中,您需要将 Adapter 设置为您的 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
}
}
我想最好的方法是从 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 接收到的数据传递给适配器。
是的,正如@dhiraj-uchil 提到的,这里的问题不是设置丢失的适配器。我也会制作 binding
lateinit 或本地。我也会提取 context
,并在其他地方使用它。
val binding = FragmentHomeBinding.inflate(inflater, container, false)
val context = requireContext()
binding.parentRecycler.adapter= CategoryAdapter(context, t)
关于从 JSON 制作数据 class 对象,我制作了一个工具,除其他外,生成这样的 classes: