【发布时间】:2021-02-01 18:37:04
【问题描述】:
我有一个 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) 数据库获取数据)
【问题讨论】:
-
为什么绑定视图一个地方叫RecyclerViewServices,另一个地方叫RecyclerView?
-
抱歉,提问时打错字了,我更正了。
-
您是否尝试过从观察中删除
lifecycleScope.launch? -
是的,同样的问题
-
原因很简单,但非常非常狡猾,因为 lint 没有指出问题所在。您实际上不是第一次设置适配器。在下面检查我的答案)
标签: android kotlin android-recyclerview android-adapter