需要将 Adapter 绑定到 RecyclerView 两次才能显示数据

问题描述

我有一个 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
    } 

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...