我正在制作一个使用Kotlin和Firebase产品的Android应用程序。我与Firestore建立了成功的连接,并且可以成功地检索到我想要的数据,但是我很难在RecolyerView中显示它。
当应用程序加载并在用户登录之后,我的Firestore查询使用用户的UID来获取其分配的列表。使用日志,我可以看到,当主屏幕加载时,这种情况没有问题。在主屏幕片段中,我为RecyclerView绑定了数据,并设置了ViewModel,让片段观察返回的Firestore数据。
我相信这是我对LiveData如何工作的误解,因为如果我点击主屏幕底部的导航图标触发UI刷新,那么列表就会填充,我就可以根据需要使用该应用程序了。因此,我的Observer/LiveData不能正确设置,因为一旦数据发生更改(null list改为not null list)时,它不会自动刷新。
由于我是编程新手,我确信我已经陷入了许多陷阱,并且做了一些错误的事情,但是我已经在StackOverflow和YouTube上搜索了几个月来关于这个问题的帮助。不幸的是,我没有所有的链接保存到每一个视频和每一个帖子。
我已经尝试将ViewModel和Repository/Database类(singleton)调整到不同的效果,目前我的版本是最好的,只需要点击一下就可以刷新UI。以前它需要多次敲击。
从数据库类
private val assignments = MutableLiveData<List<AssignmentModel>>()
private fun getUserAssignments(c: ClassModel) {
val assignmentQuery = assignmentRef.whereEqualTo("Class_ID", c.Class_ID)
assignmentQuery.addSnapshotListener { documents, _ ->
documents?.forEach { document ->
val a = document.toObject(AssignmentModel::class.java)
a.Assignment_ID = document.id
a.Class_Title = c.Title
a.Formatted_Date_Due = formatAssignmentDueDate(a)
assignmentMap[a.Assignment_ID] = a
}
}
}
fun getAssignments() : LiveData<List<AssignmentModel>> {
assignments.value = assignmentMap.values.toList().filter {
if (it.Date_Due != null) it.Date_Due!!.toDate() >= Calendar.getInstance().time else true }
.sortedBy { it.Date_Due }
return assignments
}
从ViewModel
class AssignmentListViewModel internal constructor(private val myDatabase: Database) : ViewModel() {
private var _assignments: LiveData<List<AssignmentModel>>? = null
fun getAssignments() : LiveData<List<AssignmentModel>> {
var liveData = _assignments
if (liveData == null) {
liveData = myDatabase.getAssignments()
_assignments = liveData
}
return liveData
}
}
从片段
class AssignmentList : Fragment() {
private lateinit var model: AssignmentListViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding = AssignmentListBinding.inflate(inflater, container, false)
val factory = InjectorUtils.provideAssignmentListViewModelFactory()
model = ViewModelProvider(this, factory).get(AssignmentListViewModel::class.java)
val assignmentAdapter = AssignmentAdapter()
binding.assignmentRecycler.adapter = assignmentAdapter
updateUI(assignmentAdapter)
return binding.root
}
private fun updateUI(adapter: AssignmentAdapter) {
model.getAssignments().observe(this, Observer { assignments ->
if (assignments.isNotEmpty()) adapter.submitList(assignments)
})
}
}
同样,我希望一旦来自Firestore的数据出现,RecyclerView会自动填充,但它没有。在我点击“主屏幕”按钮之前,屏幕一直是空的。
这些片段显示了我最近所做的更改。最初,我有Firestore查询函数直接返回LiveData。我还有一个简单得多的ViewModel,比如fun getAssignments()=myDatabase.getAssignments()。
感谢所有的帮助和建议。
在排除此问题时,我建议从两个方面开始。
目标是只要Firebase中的数据更新,您的assignments
LiveData就会更新您的UI。类似于:
因此,在快照侦听器中,您应该更新LiveData,这是我认为您缺少的。所以它会是这样的:
// Where you define your SnapshotListner
assignmentQuery.addSnapshotListener { documents, _ ->
// Process the data
documents?.forEach { document ->
val a = document.toObject(AssignmentModel::class.java)
a.Assignment_ID = document.id
a.Class_Title = c.Title
a.Formatted_Date_Due = formatAssignmentDueDate(a)
assignmentMap[a.Assignment_ID] = a
}
// Update your LiveData
assignments.value = assignmentMap.values.toList().filter {
if (it.Date_Due != null) it.Date_Due!!.toDate() >= Calendar.getInstance().time else true }
.sortedBy { it.Date_Due }
}
现在,每次Firestore更新时,LiveData都会更新,而UI也应该更新。
给定代码更改,getAssignments()
可以只返回赋值。您可以使用Kotlin支持属性来完成此操作,这里介绍了:
private val _assignments = MutableLiveData<List<AssignmentModel>>()
val assignments: LiveData<List<AssignmentModel>>
get() = _assignments
至于它目前无法工作的原因,现在您可以在启动时调用getAssignments()
。这将过滤空的assignmentMap.values
(我相信--可能值得检查),因为当调用它时,Firebase还没有为您获取任何数据。当Firebase获得新数据时,它会触发监听器,但您不会更新LiveData。
LiveData观察器和Firebase侦听器的一个棘手问题是确保只设置它们一次。
对于Firebase侦听器,应该在初始化数据库时设置侦听器,而不是每次调用GetUserAssignments
时设置。那么您就不需要在ViewModel中进行所有的null检查,这基本上确保了ViewModel至少不会两次调用getUserAssignments...但是如果您有其他类与您的数据库交互,它们可能会多次调用getUserAssignments
,那么您就会有大量额外的侦听器。
此外,请确保分离您的侦听器。
在Doug Stevenson的talk Firebase and Android Jetpack:Fit Like a Glove中描述了一种处理方法-talk在这里包含了一个演示代码。与此相关的部分是他如何处理LiveData--注意这个类是如何添加和删除侦听器的。TL;DR是他使用LiveData的生命周期感知来自动完成Firebase监听器的设置和清理。怎么做有点复杂,所以我建议从这里看演讲。
对于您的LiveData,setup/Tear down看起来是正确的,因为它是在onCreateView中进行设置的(并且通过它的生命周期感知的事实自动拆除)。我可以将updateui
重命名为类似于setupuiobservation
的东西,因为updateui
听起来像是您多次调用的东西。与Firebase侦听器一样,您希望确保没有多次设置同一个LiveData观察器。
希望有帮助!
我试图解决一个问题,但没有成功。每当数据库(数据库室)中特定模型的记录发生变化时,我想更新我的回收器视图。我使用ViewModel来处理模型数据,记录列表存储在LiveData中。 数据库 模型 刀 视图模型 碎片 现在,我的问题是为什么观察者只被调用一次(在片段的开始),然后没有被再次调用?我如何让观察者不断地倾听数据库中的变化(插入,更新,删除),以便我的回收器视图可以立即更新?非常感谢任何建
我从这个问题中编辑了一个柱塞
我在我的应用程序中使用导航栏,每次我选择一个项目时,它都会加载一个片段。 我能够使用相同的文本字段和按钮保存片段的状态,但有一个片段可以加载地图、添加标记、集群并执行。 每次我转到另一个菜单项并返回时,它都会重新加载所有内容,例如AsyncTask、Cluster、Markers。我如何停止此片段以不再重新创建地图并在返回时保存状态: Udate 1:我更新了代码,但问题仍然存在 主要活动: 主片
我正在尝试在我正在构建的天气应用程序中实现SwipeToRe新鲜布局。当用户滑动刷新时,ViewModel中的数据应该更新,然后视图应该相应地更新。 这是我的货币天气片段的片段: 我的视图模型: 我的lazyDeferred实现: 目前,通过此设置,forecastRepository。当在应用程序启动或切换到该片段时加载该片段时,会调用ViewModel中的getWeather()函数,但在滑动
我遇到了一个如何在对话框片段中更新片段的问题。 当我单击过滤器菜单按钮时,会显示一个新的对话框片段,其中包括一个无线电组。 我想在单击ok按钮时更新包含位置列表的片段。 它是PlaceActive的代码,其中包含PlaceFraank: 公共类PlaceActive扩展AppCompatActive{ } 以下是PlaceFragment类的代码: 公共类PlaceFragment扩展了片段{ }
我使用setUserVisibleHint()方法加载数据,但是这个方法在oncreateview之前调用,所以我得到的是NPE。 我已经检查了从下面提到的链接堆栈溢出的索洛。 请帮我做这个。我总共有5个碎片。在每个片段中,我都在fragment的onCreateView方法中进行服务器调用。