Android recyclerView 和数据绑定

Posted

技术标签:

【中文标题】Android recyclerView 和数据绑定【英文标题】:Android recyclerView and data binding 【发布时间】:2021-05-24 13:22:54 【问题描述】:

我在 Kotlin 中制作了一个 android 应用程序,我想从网络服务器获取一些信息并使用数据绑定放入一个回收器视图。我使用存储库模式,所以每次我发出获取请求时,我都会将信息保存在房间数据库中,并使用实时数据从中获取数据。出于某种原因,我的 ryclerview 没有显示任何内容,并且我没有收到任何错误。请求有效,我得到了数据,但 recyclerview 不起作用。 我的视图模型:

class HomeViewModel(application: Application) : ViewModel() 
    private val availableTicketsRepo = AvailableTicketsRepository(getInstance(application))

    val tickets = availableTicketsRepo.availableTickets

    init 
        refreshDataFromRepository()
    

    private fun refreshDataFromRepository() 
        viewModelScope.launch 
            try 
                availableTicketsRepo.refreshAvailableTickets()
             catch (ex: Exception) 
                ex.message?.let  Resource.error(data = null, message = it) 
                Log.i("Home", "*** Eccezione: $ex ***")
            
        
    

我的片段类(带有适配器和视图):

package com.example.ticketapp.ui.home

class HomeFragment : Fragment() 

    private val viewModel: HomeViewModel by lazy 
        val activity = requireNotNull(this.activity) 
            "You can only access the viewModel after onActivityCreated()"
        
        ViewModelProvider(this, HomeViewModel.Factory(activity.application))
            .get(HomeViewModel::class.java)
    

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View 
        val binding = DataBindingUtil.inflate<FragmentHomeBinding>(
            inflater,
            R.layout.fragment_home,
            container,
            false
        )

        binding.lifecycleOwner = this
        binding.viewModel = viewModel

        binding.availableTicketsList.adapter = AvailableTicketsAdapter()


        return binding.root
    


class AvailableTicketsAdapter() :
    ListAdapter<AvailableTicket, AvailableTicketsAdapter.AvailableTicketViewHolder>(DiffCallback) 

    class AvailableTicketViewHolder(private var binding: AvailableTicketItemsBinding) :
        RecyclerView.ViewHolder(binding.root) 
        fun bind(tickets: AvailableTicket) 
            binding.ticket = tickets
            binding.executePendingBindings()
        
    

    companion object DiffCallback : DiffUtil.ItemCallback<AvailableTicket>() 
        override fun areItemsTheSame(oldItem: AvailableTicket, newItem: AvailableTicket): Boolean 
            return oldItem === newItem
        

        override fun areContentsTheSame(
            oldItem: AvailableTicket,
            newItem: AvailableTicket
        ): Boolean 
            return oldItem.Id == newItem.Id
        
    

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AvailableTicketViewHolder 
        return AvailableTicketViewHolder(
            AvailableTicketItemsBinding.inflate(
                LayoutInflater.from(
                    parent.context
                )
            )
        )
    


    override fun onBindViewHolder(holder: AvailableTicketViewHolder, position: Int) 
        val tickets = getItem(position)
        holder.bind(tickets)
    

我的仓库:

class AvailableTicketsRepository(private val database: TicketDatabase) 
    val availableTickets: LiveData<List<AvailableTicket>> =
        Transformations.map(database.ticketDatabaseDao.getAvailableTickets()) 
            it.asDomainModel()
        


    suspend fun refreshAvailableTickets() 
        withContext(Dispatchers.IO) 
            val tickets = RetrofitBuilder.apiService.getAvailableTickets()
            database.ticketDatabaseDao.insertAllAvailableTickets(tickets.map  it.asDataBaseModel() )
        
    

recylerview 中项目的 xml:

<?xml version="1.0" encoding="utf-8"?>
<layout 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">

    <data>
        <import type="android.view.View"/>
        <variable
            name="ticket"
            type="com.example.ticketapp.model.AvailableTicket" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_
        android:layout_>

        <TextView
            android:id="@+id/titleText"
            android:layout_
            android:layout_
            android:ellipsize="end"
            android:maxLength="50"
            android:text="@ticket.title"
            android:singleLine="true"
            android:textAlignment="center"
            android:textSize="16sp"
            android:textStyle="bold"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:text="Titolo di max 50 caratteri per non uscire dalla riga" />


        <TextView
            android:id="@+id/customerText"
            android:layout_
            android:layout_
            android:layout_marginTop="5dp"
            android:textSize="14sp"
            android:text="@ticket.customer"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/titleText"
            tools:text="ESEMPIO DI UN REFERENTE" />

        <TextView
            android:id="@+id/dateText"
            android:layout_
            android:layout_
            android:layout_marginTop="5dp"
            android:textAlignment="textEnd"
            android:textSize="14sp"
            android:text="@ticket.date_Time"
            app:layout_constraintBaseline_toBaselineOf="@+id/customerText"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/titleText"
            tools:text="20/01/2021 10:00:00" />

        <ImageView
            android:id="@+id/warningImg"
            style="@style/iconImg"
            android:layout_marginTop="20dp"
            android:visibility="@ticket.triangle ? View.VISIBLE : View.GONE"
            android:foregroundGravity="center"
            app:layout_constraintEnd_toStartOf="@+id/dateText"
            app:layout_constraintStart_toEndOf="@+id/customerText"
            app:layout_constraintTop_toBottomOf="@+id/titleText"
            app:srcCompat="@drawable/ic_warning" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

ryclerview的XML:

<androidx.recyclerview.widget.RecyclerView
           android:id="@+id/availableTicketsList"
           android:layout_
           android:layout_
           android:paddingStart="10dp"
           android:paddingEnd="10dp"
           app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
           app:layout_constraintEnd_toEndOf="parent"
           app:layout_constraintStart_toStartOf="parent"
           app:layout_constraintTop_toBottomOf="@+id/toolbar"
           tools:listitem="@layout/available_ticket_items" />

我的模型类:

@Parcelize
data class AvailableTicket (
   val Id: Int,
   val Title: String,
   val Description: String,
   val Date_Time: String,
   val Customer: String,
   val Field: String,
   val Category: String,
   val Color: String,
   val Author: String,
   val Triangle: Boolean,
   val MyPriority: String
): Parcelable

【问题讨论】:

【参考方案1】:

你应该,当数据被修改时通知适配器,在ViewModel内添加一个LiveData并在availableTicketsRepo.refreshAvailableTickets()之后发布它,然后在收到数据后在适配器内调用submitList

视图模型

class HomeViewModel(application: Application) : ViewModel() 
    private val availableTicketsRepo = AvailableTicketsRepository(getInstance(application))
    val tickets = availableTicketsRepo.availableTickets

    init 
        refreshDataFromRepository()
    

    private fun refreshDataFromRepository() 
        viewModelScope.launch 
            try 
                availableTicketsRepo.refreshAvailableTickets()
             catch (ex: Exception) 
                ex.message?.let  Resource.error(data = null, message = it) 
                Log.i("Home", "*** Eccezione: $ex ***")
            
        
    

然后在Fragment中,一旦获取到列表就提交给适配器

override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View 
        val binding = DataBindingUtil.inflate<FragmentHomeBinding>(
            inflater,
            R.layout.fragment_home,
            container,
            false
        )

        binding.lifecycleOwner = this
        binding.viewModel = viewModel

        binding.availableTicketsList.adapter = AvailableTicketsAdapter()

        viewModel.tickets.observe(this, Observer
            (binding.availableTicketsList.adapter as? AvailableTicketsAdapter)?.submitList(it)
        )
        return binding.root
    

【讨论】:

也可以在_ticketLiveData.postValue(availableTicketsRepo.refreshAvailableTickets())上获得Type mismatch. Required: List&lt;AvailableTicket&gt;! Found: Unit 我收到第二个错误,因为availableTicketsRepo.refreshAvailableTickets() 什么也没返回,请参阅问题 理想情况下,一旦您的数据更新,尝试在 repo 中返回最新数据,然后您可以在 Fragment 中观察并更新列表 但我关注 google codelabs,availableTicketsRepo.refreshAvailableTickets() 无效,因为数据是按房间自动更新的.. 哦,是的,对不起,您需要在 ViewModel 中观察工单,更新代码

以上是关于Android recyclerView 和数据绑定的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Android Studio 中同时从 recyclerview 和数据库中删除数据? [关闭]

Android recyclerView 和数据绑定

Android RecyclerView使用简述

Android RecyclerView使用简述

Android RecyclerView使用简述

RecyclerView Item Click Listener with DataBinding