1

嘿,我正在学习android kotlin 中的状态流我正在reyclerview的帮助下创建反向对话日历视图。在我mainactivity里面有一个fragment,在里面我有reyclerview。我的目标是在我的recyclerview中进行分页,所以我首先加载几个月,而不是在这个答案的帮助下在我的 reyclerview 中添加越来越多的数据。我成功地实现了它。但问题是当我达到阈值并触发新数据时。我的整个列表被引用并且它没有被更新它删除了以前的数据。我不明白为什么会在MutableStateFlow中发生这种情况,而且我是这个流程的新手. 我的Github项目链接在这里。请指导我在这里做错了什么。我的目标是预先添加 stateFlow 数据。谢谢

对话片段.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()
    }
}

片段视图模型.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并将endDate设置为今天的日期,即2022-01-13。因此,当开始滚动并达到加载更多数据的阈值时。我通过了startDate作为2021-11-01endDate作为2021-11-30。它已添加到列表中,但之前的数据已被删除。我不明白为什么会这样。我附上我的 YouTube链接

4

1 回答 1

1

这是大量的代码,我无法弄清楚你想要做的所有事情。你没有展示什么CustomManager.loadMoreData()。但我假设这是一个在您滚动到底部时加载其他项目的列表?如果你单步执行onScrolled函数的逻辑,它似乎没有任何意义。比如为什么会totalItemCount小于previousTotalItemCount

如果您向下滚动到@Minh Nguyen对您链接的问题的回答,这是一个更简单的解决方案。我会更进一步,创建一个帮助类,这样你就可以保持你的片段代码干净。我添加了一个功能,当新数据到达适配器时,它会自动注册以重置自己。注意,我没有测试过这个类!

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
})

我不知道您的下游逻辑是否还有其他问题。在没有实际的应用程序调试和知道它应该做的一切的情况下,推理的代码太多了。

于 2022-01-14T15:29:35.670 回答