问题描述
我有一个 Android 应用程序,我在其中将服务列表绑定到 RecyclerView:
片段.kt
override fun onCreateView(
inflater: LayoutInflater,container: ViewGroup?,savedInstanceState: Bundle?
): View {
mBinding = FragmentAllServicesBinding.inflate(inflater,container,false)
mviewmodel = viewmodelProvider(this).get(AllServicesviewmodel::class.java)
binding.viewmodel = viewmodel
binding.lifecycleOwner = this
return binding.root
}
override fun onViewCreated(view: View,savedInstanceState: Bundle?) {
super.onViewCreated(view,savedInstanceState)
subscribeServices()
}
// Private Functions
private fun subscribeServices(){
val adapter = ServiceAdapter()
binding.RecyclerViewServices.apply {
/*
* State that layout size will not change for better performance
*/
setHasFixedSize(true)
/* Bind the layout manager */
layoutManager = linearlayoutmanager(requireContext())
this.adapter = adapter
}
viewmodel.services.observe(viewLifecycleOwner,{ services ->
if(services != null){
lifecycleScope.launch {
adapter.submitList(services)
}
}
})
}
viewmodel.kt
package com.th3pl4gu3.mes.ui.main.all_services
import android.app.Application
import androidx.lifecycle.*
import com.th3pl4gu3.mes.api.ApiRepository
import com.th3pl4gu3.mes.api.Service
import com.th3pl4gu3.mes.ui.utils.extensions.lowercase
import kotlinx.coroutines.launch
import kotlin.collections.ArrayList
class AllServicesviewmodel(application: Application) : Androidviewmodel(application) {
// Private Variables
private val mServices = mutablelivedata<List<Service>>()
private val mMessage = mutablelivedata<String>()
private val mloading = mutablelivedata(true)
private var mSearchQuery = mutablelivedata<String>()
private var mRawServices = ArrayList<Service>()
// Properties
val message: LiveData<String>
get() = mMessage
val loading: LiveData<Boolean>
get() = mloading
val services: LiveData<List<Service>> = Transformations.switchMap(mSearchQuery) { query ->
if (query.isEmpty()) {
mServices.value = mRawServices
} else {
mServices.value = mRawServices.filter {
it.name.lowercase().contains(query.lowercase()) ||
it.identifier.lowercase().contains(query.lowercase()) ||
it.type.lowercase().contains(query.lowercase())
}
}
mServices
}
init {
loadServices()
}
// Functions
internal fun loadServices() {
// Set loading to true to
// notify the fragment that loading
// has started and to show loading animation
mloading.value = true
viewmodelScope.launch {
//Todo("Ensure connected to internet first")
val response = ApiRepository.getInstance().getServices()
if (response.success) {
// Bind raw services
mRawServices = ArrayList(response.services)
// Set the default search string
mSearchQuery.value = ""
} else {
mMessage.value = response.message
}
}.invokeOnCompletion {
// Set loading to false to
// notify the fragment that loading
// has completed and to hide loading animation
mloading.value = false
}
}
internal fun search(query: String) {
mSearchQuery.value = query
}
}
ServiceAdapter.kt
class ServiceAdapter : listadapter<Service,ServiceViewHolder>(
diffCallback
) {
companion object {
private val diffCallback = object : DiffUtil.ItemCallback<Service>() {
override fun areItemsTheSame(oldItem: Service,newItem: Service): Boolean {
return oldItem.identifier == newItem.identifier
}
override fun areContentsTheSame(oldItem: Service,newItem: Service): Boolean {
return oldItem == newItem
}
}
}
override fun onBindViewHolder(holder: ServiceViewHolder,position: Int) {
holder.bind(
getItem(position)
)
}
override fun onCreateViewHolder(parent: ViewGroup,viewType: Int): ServiceViewHolder {
return ServiceViewHolder.from(
parent
)
}
}
ServiceViewHolder.kt
class ServiceViewHolder private constructor(val binding: CustomrecyclerviewServiceBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(
service: Service?
) {
binding.service = service
binding.executePendingBindings()
}
companion object {
fun from(parent: ViewGroup): ServiceViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding =
CustomrecyclerviewServiceBinding.inflate(layoutInflater,parent,false)
return ServiceViewHolder(
binding
)
}
}
}
这里的问题是,数据不会显示在屏幕上。
出于某些原因,如果我将片段的代码更改为:
viewmodel.services.observe(viewLifecycleOwner,{ services ->
if(services != null){
lifecycleScope.launch {
adapter.submitList(services)
// Add this code
binding.RecyclerViewServices.adapter = adapter
}
}
})
然后数据显示在屏幕上。
有谁知道为什么我需要将适配器设置两次才能使其工作?
我有另一个应用程序,我不必在其中设置两次,而且它工作正常。出于某种原因,此应用程序无法运行。 (另一个应用程序和这个应用程序之间的唯一区别是,这个应用程序从 API 中获取数据,而另一个应用程序从 Room (sqlite) 数据库中获取数据)
解决方法
尝试在 adapter.notifyDataSetChanged()
之后调用 adapter.submitList(services)
,而不是再次添加适配器
内部
binding.RecyclerViewServices.apply {
...
}
将 this.adapter = adapter
改为 this.adapter = this@YourFragmentName.adapter
原因是,您将适配器变量命名为 "adapter"
,这与 RecyclerView.adapter
的属性名称冲突。您实际上并不是第一次设置适配器。这是非常狡猾的,因为 lint 没有给出任何警告并且代码编译没有错误......
或者您可以将片段中的 "adapter"
变量重命名为类似 "servicesAdapter"
的名称,以便于使用
binding.RecyclerViewServices.apply {
adapter = servicesAdapter
}