【问题标题】:Unable to retrieve data in nested recycler view like playstore无法在 playstore 等嵌套回收器视图中检索数据
【发布时间】:2021-07-21 07:24:28
【问题描述】:

在从服务器获取数据后,我试图在嵌套的 recyclerview 中显示数据,例如 play store。我已成功检索并解析父 recyclerview 的 title,但无法显示作为子布局的水平 recyclerview。

在视图模型实例化时,CategortAdapter.kt 出现错误,它在下一行的ViewModelProvider(context) 下显示红线:

val viewModel = ViewModelProvider(context).get(CatImagesViewModel::class.java)

以下是我的 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"
            }
        ]
      }
   ]
}

在垂直的父回收器视图中,我想在 JSON 响应中显示标题 category,在水平回收器视图中显示 sku

在子回收站视图中,我只想显示 sku 数组下的图像。

下面是我的代码:

ApiService.kt

interface ApiService {

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

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

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>
)   

Sku.kt

data class Sku(
  val brand: String,
  val color: String,
  val img: String,
  val name: String,
  val price: String
) 

parent_row.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="12dp">

<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/catTitle"
    android:textStyle="bold"
    android:textSize="18sp"
    android:textColor="#345"/>

<androidx.recyclerview.widget.RecyclerView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/childRecycler"
    android:layout_below="@+id/catTitle"
    android:layout_marginTop="8dp"/>

</RelativeLayout>

child_row.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">

<androidx.cardview.widget.CardView
    android:layout_width="200dp"
    android:layout_height="200dp"
    app:cardCornerRadius="3dp"
    app:cardUseCompatPadding="true">

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/img"/>

    </LinearLayout>

</androidx.cardview.widget.CardView>

</RelativeLayout>

在下面的适配器中,我正在获取类别标题和子回收器视图,因为我已成功检索标题但无法解析回收器视图:

CategoryAdapter.kt

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

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): 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

    holder.binding.childRecycler.setHasFixedSize(true)
    holder.binding.childRecycler.layoutManager = LinearLayoutManager(context)

    val viewModel = ViewModelProvider(context).get(CatImagesViewModel::class.java)

}

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

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

CatImagesViewModel.kt

class CatImagesViewModel: ViewModel() {

private var catImageList:MutableLiveData<List<List<Sku>>> = MutableLiveData()

fun getCatImg(): LiveData<List<List<Sku>>>{

    viewModelScope.launch(Dispatchers.IO) {

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

        if(response.isSuccessful){

            val skus: MutableList<List<Sku>> = mutableListOf() 
            val cnt = response.body()!!.result.size

            for(i in 0 until cnt){
                skus.add(response.body()!!.result[i].sku)
            }
             catImageList.postValue(skus)
        }
    }

    return catImageList
  }
}

下面是在水平视图的子回收器视图中解析图像的适配器:

CatImgAdapter.kt

class CatImgAdapter(private val context: Context,private val imgList:List<Sku>): RecyclerView.Adapter<CatImgAdapter.ViewHolder>() {

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

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

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

    val model = imgList[position]
    Glide.with(context).load(model.img).into(holder.binding.img)
}

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

data class ViewHolder(val binding:ChildRowBinding): RecyclerView.ViewHolder(binding.root)

}

下面是ViewModel 用于获取类别标题或垂直回收器视图:

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
  }
}     

有人告诉我我做错了什么或实现所需布局的最佳方式。

【问题讨论】:

    标签: android kotlin android-recyclerview viewmodel


    【解决方案1】:

    您不应该在recyclerview adapter 中实例化您的ViewModel。相反,您需要将数据传递给您的recyclerview adapter

    您应该观察Viewactivityfragment)中的LiveData,然后将此数据(此处为List&lt;Result&gt;)传递给您的recyclerview adapterViewModel 只能在这些Views 中使用。

    如果您需要更多解释,请告诉我,我会尽力详细解释:)

    编辑

    我还注意到CatImagesViewModel 你有

        if(response.isSuccessful){
    
            val cnt = response.body()!!.result.size
    
            for(i in 0 until cnt){
                catImageList.postValue(response.body()!!.result[i].sku)
            }
        }
    

    postvalue 会将当前resultsku 设置为MutableLiveData,这意味着您将只有1 个category 中的sku (List&lt;Sku&gt;),因为前一个已被丢弃每次迭代。

    你想要做的是,

    class CatImagesViewModel: ViewModel() {
    
      private var catImageList:MutableLiveData<List<List<<Sku>>> = MutableLiveData()
    
      fun getCatImg(): LiveData<List<List<Sku>>>{
    
      viewModelScope.launch(Dispatchers.IO) {
    
          val retrofit = RetrofitClient.getRetrofitClient().create(ApiService::class.java)
          val response = retrofit.getCatImg()
    
          if(response.isSuccessful){
    
            val skus: MutableList<List<List<Sku>>> = mutableListOf()
            val cnt = response.body()!!.result.size
    
            for(i in 0 until cnt){
                skus.add(response.body()!!.result[i].sku)
            }
    
            catImageList.postValue(skus)
        }
    }
    
        return catImageList
       }
    }
    

    最终更新

    好的,这是您在嵌套recyclerview中加载项目所需的代码,如果这些类的名称与您的不同,那么只需创建一个新项目并添加此代码供您参考,我附上一个以下应用程序的工作截图作为提示,希望它现在可以为您解决所有问题:)

    MainActivity.kt

    class MainActivity : AppCompatActivity() {
    
        private val viewModel by viewModels<MainViewModel>()
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            viewModel.getResult()
    
            val adapter = ParentAdapter(this)
            recyclerview_parent.adapter = adapter
    
            viewModel.data.observe(this, {
    
                adapter.setData(it)
            })
        }
    }
    

    MainViewModel.kt

    class MainViewModel : ViewModel() {
    
        private val service = RetrofitHelper().retrofit.create(ApiService::class.java)
    
        private val _data: MutableLiveData<List<Result>> = MutableLiveData()
        val data: LiveData<List<Result>> get() = _data
    
        fun getResult() {
    
            viewModelScope.launch {
    
                val res = service.getProducts()
                _data.postValue(res.body()?.result)
            }
        }
    }
    

    ApiService.kt

    interface ApiService {
    
       @GET("getProducts")
       suspend fun getProducts(): Response<Product>
    }
    

    ParentAdapter.kt

    class ParentAdapter(
        private val context: Context
    ): RecyclerView.Adapter<ParentAdapter.ParentViewHolder>() {
    
        private var data: List<Result>? = null
    
        inner class ParentViewHolder(itemView: View): RecyclerView.ViewHolder(itemView)
    
        fun setData(data: List<Result>) {
            this.data = data
            notifyDataSetChanged()
        }
    
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ParentViewHolder {
    
            return ParentViewHolder(
                LayoutInflater.from(context)
                    .inflate(R.layout.item_parent,parent,false)
            )
        }
    
        override fun onBindViewHolder(holder: ParentViewHolder, position: Int) {
    
            val item = data?.get(position)
    
            if (item != null) {
    
                holder.itemView.category_title.text = item.category
    
                val adapter = ChildAdapter(context)
                adapter.setData(item.sku)
                holder.itemView.recyclerview_child.adapter = adapter
            }
        }
    
        override fun getItemCount(): Int {
            return data?.size ?: 0
        }
    }
    

    ChildAdapter.kt

    class ChildAdapter(
        private val context: Context
    ) : RecyclerView.Adapter<ChildAdapter.ChildViewHolder>() {
    
        private var data: List<Sku>? = null
    
        inner class ChildViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
    
        fun setData(data: List<Sku>) {
            this.data = data
            notifyDataSetChanged()
        }
    
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChildViewHolder {
    
            return ChildViewHolder(
                LayoutInflater.from(context)
                    .inflate(R.layout.item_child, parent, false)
            )
        }
    
        override fun onBindViewHolder(holder: ChildViewHolder, position: Int) {
    
            val item = data?.get(position)
    
            if (item != null) {
                holder.itemView.label.text = item.name
    
                Glide.with(context)
                    .load(item.img)
                    .transition(DrawableTransitionOptions.withCrossFade())
                    .into(holder.itemView.image)
            }
        }
    
        override fun getItemCount(): Int {
            return data?.size ?: 0
        }
    }
    

    RetrofitHelper.kt

    class RetrofitHelper {
    
        val retrofit = Retrofit.Builder()
            .baseUrl("https://koovs18.herokuapp.com/")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }
    

    activity_main.xml

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".ui.MainActivity">
    
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recyclerview_parent"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_margin="16dp"
            app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
            tools:listitem="@layout/item_parent"/>
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    

    item_parent.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    
        <TextView
            android:id="@+id/category_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            tools:text="Category Title"
            android:layout_marginStart="4dp"
            android:layout_marginTop="8dp"
            android:textSize="24sp"
            android:textColor="@color/black"
            />
    
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recyclerview_child"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="8dp"
            android:orientation="horizontal"
            app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
            tools:listitem="@layout/item_child"/>
    
    </LinearLayout>
    

    item_child.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:orientation="vertical"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
    
        <ImageView
            android:id="@+id/image"
            android:layout_width="150dp"
            android:layout_height="160dp"
            android:layout_margin="4dp"/>
    
        <TextView
            android:id="@+id/label"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            tools:text="Product Label"
            android:gravity="center"
            android:textSize="16sp"
            android:textColor="@color/black"
            android:padding="4dp"/>
    
    </LinearLayout>
    

    build.gradle

    中需要的依赖项
    def coroutines_version = "1.4.2"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
    
    def lifecycle_version = "2.3.1"
    implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
    implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
    
    def retrofit_version = "2.9.0"
    implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
    implementation "com.squareup.retrofit2:converter-gson:$retrofit_version"
    
    implementation 'com.squareup.okhttp3:okhttp:4.9.1'
    
    implementation 'com.github.bumptech.glide:glide:4.12.0'
    kapt 'com.github.bumptech.glide:compiler:4.12.0'
    
    implementation "androidx.activity:activity-ktx:1.2.4"
    

    【讨论】:

    • 我想知道为什么在这行private var catImageList:MutableLiveData&lt;List&lt;List&lt;&lt;Sku&gt;&gt;&gt; 为什么我们有两次List
    • 如何将这些skus 列表传递给在父回收器视图中定义的子回收器视图。
    • 您需要将孩子的数据传递给父recyclerview的适配器,然后您可以从那里将数据传递给子适配器:)
    • 根据您的解决方案,我现在可以使用视图模型获得skus,但是现在请让我告诉我如何在我的代码中实现这一点,我已经在上面发布了我的代码。
    • 你能用最新的变化编辑你的问题吗?
    猜你喜欢
    • 2018-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-01
    • 2022-10-26
    • 2016-01-24
    • 2020-09-04
    • 1970-01-01
    相关资源
    最近更新 更多