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<AvailableTicket>! Found: Unit
我收到第二个错误,因为availableTicketsRepo.refreshAvailableTickets()
什么也没返回,请参阅问题
理想情况下,一旦您的数据更新,尝试在 repo 中返回最新数据,然后您可以在 Fragment 中观察并更新列表
但我关注 google codelabs,availableTicketsRepo.refreshAvailableTickets()
无效,因为数据是按房间自动更新的..
哦,是的,对不起,您需要在 ViewModel 中观察工单,更新代码以上是关于Android recyclerView 和数据绑定的主要内容,如果未能解决你的问题,请参考以下文章