问题描述
嘿,我正在学习Android Kotlin中的stateflow。我正在使用回收视图创建反向对话日历视图。在我的 ConversationFragment.kt FragmentViewModel.kt 我的模拟API响应link 问题说明mainactivity
中有一个fragment
,在那个里面我有reyclerview
。我的目标是在我的回收视图中分页,所以我会提前几个月加载,而不是通过这个answer在我的回收视图中添加越来越多的数据。我成功地做到了这一点。但问题是,当我达到阈值时,它会触发新数据。我的整个列表都被引用了,它没有被更新,它删除了之前的数据。我不明白为什么在MuableStateFlow中会发生这种情况,而且我对这个流也是新手。我的Github项目链接在此处。请告诉我我在这里做错了什么。我的目标是为stateFlow数据添加前缀。谢谢
class ConversationFragment(val customManager: CustomManager) : Fragment() {
companion object {
private const val DATA = "data"
fun create(
data: List<ConversationDate>,
customManager: CustomManager
): ConversationFragment {
val fragment = ConversationFragment(customManager)
val bundle = Bundle()
bundle.putParcelableArrayList(
DATA,
ArrayList<Parcelable>(data)
)
fragment.arguments = bundle
return fragment
}
}
private var _binding: ConversationFragmentLayoutBinding? = null
private val binding get() = _binding!!
private val visibleThreshold = 7
private var previousTotalItemCount = 0
private var loading = true
private val weeklyAdapter = ConversationWeeklyAdapter()
private val viewModel by viewModels<FragmentViewModel>()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
setupViewModel()
_binding = ConversationFragmentLayoutBinding.inflate(inflater, container, false)
setupView()
return binding.root
}
private fun setupViewModel() {
viewModel.data = arguments?.getParcelableArrayList(DATA)
viewModel.startDate = customManager.startDate()
viewModel.endDate = customManager.endDate()
}
private fun setupView() {
viewModel.weeklyFormatData()
activity?.lifecycleScope?.launchWhenCreated {
activity?.repeatOnLifecycle(Lifecycle.State.CREATED) {
viewModel.prepareMutableStateFlow.collectLatest {
weeklyAdapter.submitList(it)
}
}
}
binding.conversationView.apply {
adapter = weeklyAdapter
layoutManager = LinearLayoutManager(context).apply {
orientation = LinearLayoutManager.HORIZONTAL
stackFromEnd = true
}
addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val firstVisibleItemPosition =
(recyclerView.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition()
val totalItemCount = recyclerView.adapter?.itemCount
if (totalItemCount != null) {
if (totalItemCount < previousTotalItemCount) {
previousTotalItemCount = totalItemCount
if (totalItemCount == 0) {
loading = true
}
}
}
if (totalItemCount != null) {
if (loading && (totalItemCount > previousTotalItemCount)) {
loading = false
previousTotalItemCount = totalItemCount
}
}
if (!loading && firstVisibleItemPosition < visibleThreshold) {
customManager.loadMoreData()
loading = true
}
}
})
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
fun loadMoreData(item: List<ConversationDate>) {
viewModel.startDate =
SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).parse("2021-11-01")
viewModel.endDate =
SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).parse("2021-11-30")
val previousData = viewModel.data
if (previousData != null) {
viewModel.data = previousData + item
}
viewModel.weeklyFormatData()
}
}
class FragmentViewModel(app: Application) : AndroidViewModel(app) {
var data: List<ConversationDate>? = null
var startDate: Date? = null
var endDate: Date? = null
private val dateRange: MutableList<Date>
get() {
val datesInRange = mutableListOf<Date>()
val tempCalendar = Calendar.getInstance()
tempCalendar.time = startDate as Date
while (tempCalendar.time <= endDate) {
datesInRange.add(tempCalendar.time)
tempCalendar.add(Calendar.DATE, 1)
}
return datesInRange
}
val prepareMutableStateFlow: MutableStateFlow<List<ConversationCount>> =
MutableStateFlow(emptyList())
fun weeklyFormatData() {
val conversationCount = mutableListOf<ConversationCount>()
viewModelScope.launch {
dateRange.forEachIndexed { index, dateRangeValue ->
val findData = data?.find {
dateRangeValue == it.dateObject
}
conversationCount.add(
ConversationCount(
setDay(dateRangeValue),
index,
findData != null
)
)
}
prepareMutableStateFlow.value = conversationCount
}
}
private fun setDay(date: Date): String? {
return SimpleDateFormat("dd", Locale.getDefault()).format(date)
}
}
2022-01-13
。所以当开始滚动并达到加载更多数据的阈值时。我将startDate传递为2021-11-01,将endDate传递为2021-11-30。它被添加到列表中,但之前的数据被删除。我不明白为什么会发生这种事。我正在附上我的YouTubelink
推荐答案
这是一大堆代码,我无法理解您想要做的每一件事。您没有显示CustomManager.loadMoreData()
的功能。但我假设这是一个当你滚动到底部时加载额外项目的列表?如果单步执行onScrolled
函数的逻辑,它似乎没有任何意义。比如为什么totalItemCount
会小于previousTotalItemCount
?
如果您向下滚动到@Minh Nguyen对您链接的问题的答案,它将是一个简单得多的解决方案。我会更进一步,创建一个Helper类,以便您可以保持片段代码的整洁。我添加了一项功能,当新数据到达适配器时,它会自动注册以重置自身。注意,我还没有测试过这个类!
class BottomReachedListener(
var onBottomReached: () -> Unit = {}
) : RecyclerView.OnScrollListener() {
private var isTriggered = false
private var registeredAdapter: RecyclerView.Adapter<*>? = null
private val observer = object: RecyclerView.AdapterDataObserver() {
override fun onChanged() = reset()
}
fun reset() {
isTriggered = false
}
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
if (isTriggered) {
return
}
val layoutManager = recyclerView.layoutManager as? LinearLayoutManager
?: error("No LinearLayoutManager")
val totalItems = layoutManager.itemCount
val lastVisibleItem = layoutManager.findLastVisibleItemPosition()
if (lastVisibleItem == totalItems - 1) {
registerWithAdapter(recyclerView)
isTriggered = true
onBottomReached()
}
}
private fun registerWithAdapter(recyclerView: RecyclerView) {
registeredAdapter?.unregisterAdapterDataObserver(observer)
registeredAdapter = recyclerView.adapter
registeredAdapter?.registerAdapterDataObserver(observer)
}
}
现在,在您的片段中,您所需要做的就是将此类的一个实例设置为您的滚动侦听器,并向其传递一个回调,以便在需要加载新数据时调用:
addOnScrollListener(BottomReachedListener {
customManager.loadMoreData() // for example
})
我不知道您的下游逻辑中是否还有其他问题。如果没有实际要调试的应用程序并知道它应该做的一切,就无法推理出太多的代码。
这篇关于状态流引用回收式Android Kotlin中的整个数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!