状态流引用回收式Android Kotlin中的整个数据 [英] stateflow refershing whole data in reyclerview android kotlin

查看:30
本文介绍了状态流引用回收式Android Kotlin中的整个数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

嘿,我正在学习Android Kotlin中的stateflow。我正在使用回收视图创建反向对话日历视图。在我的mainactivity中有一个fragment,在那个里面我有reyclerview。我的目标是在我的回收视图中分页,所以我会提前几个月加载,而不是通过这个answer在我的回收视图中添加越来越多的数据。我成功地做到了这一点。但问题是,当我达到阈值时,它会触发新数据。我的整个列表都被引用了,它没有被更新,它删除了之前的数据。我不明白为什么在MuableStateFlow中会发生这种情况,而且我对这个也是新手。我的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响应link

问题说明

当加载应用程序时,将出现片段屏幕,并在其上显示一些日期。我将startDate设置为2021-12-01,将endDate设置为今天的日期,即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中的整个数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆