嘿,我正在android kotlin中学习状态流。我正在reyclerview的帮助下创建反向对话日历视图。在我的mainactivity
中有一个fragment
,在它里面有reyclerview
。我的目标是在我的回收视图中执行分页操作,所以我会先加载几个月,而不是借助这个答案在我的回收视图中添加越来越多的数据。我成功地做到了。但问题是,当我达到阈值时,它会触发新数据。我的整个列表被引用了,它没有被更新,它删除了以前的数据。我不明白为什么会在MutableStateFlow中发生这种情况,而且我是流的新手。我的Github项目链接在这里。请引导我在这里做错了什么。我的目标是准备stateFlow数据。感谢
ConversationFragment.kt
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()
}
}
FragmentViewModel.kt
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)
}
}
我的模拟api响应链接
问题描述
加载应用程序时,将出现片段屏幕,并在屏幕上显示一些日期。我将startDate设置为2021-12-01,将endDatetartDate作为2021-11-01和endDate021-11-30。它已添加到列表中,但删除了以前的数据。我不明白为什么会发生这种事。我正在附加我的YouTube链接
这是一个庞大的代码,我无法理解你想要做的一切。你没有展示CustomManager.loadMoreData()
的功能。但我假设这是一个当你滚动到底部时加载额外项目的列表?如果您逐步了解onScrolled
函数的逻辑,它似乎没有意义。比如为什么totalItemCount
会小于previousTotalItemCount
?
如果你向下滚动到@Minh Nguyen对你链接的问题的回答,这是一个简单得多的解决方案。我会更进一步,创建一个助手类,这样您就可以保持Fragment代码的整洁。我添加了一个功能,当新数据到达适配器时,它会自动注册以重置自己注意,我还没有测试过这个类
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)
}
}
现在,在Fragment中,您所需要做的就是将此类的一个实例设置为滚动侦听器,并在需要加载新数据时向其传递一个回调:
addOnScrollListener(BottomReachedListener {
customManager.loadMoreData() // for example
})
我不知道你的下游逻辑是否还有其他问题。如果没有实际的应用程序进行调试,也不知道它应该做什么,那么太多的代码就无法推理