@UBERS

Как можно реализовать загрузку recycler view?

Как можно реализовать загрузку recycler view?
В фрагменте HistoryFragment я инициализирую адаптер:
override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        Log.d("MyLog", "onCreateView - History")
        binding = FragmentHistoryBinding.inflate(inflater, container, false)
        initRcView()
//...
}

private fun initRcView() = with(binding) {
        rcViewHistory.layoutManager = LinearLayoutManager(activity)
        adapter = RecyclerViewAdapter(this@HistoryFragment, requireContext())
        rcViewHistory.adapter = adapter
        //Cache elements of list
        rcViewHistory.setItemViewCacheSize(120)
    }

После этого я вызываю observer, достающий данные из БД и передающие их в adapter:
private fun observer(month: String, currentMonth: String) {
        mainViewModel.getMonthNotes(month).observe(viewLifecycleOwner) {
            if (it == null && month == currentMonth) {
                Log.d("MyLog", "List is empty")
                binding.rcViewHistory.visibility = View.GONE

                binding.EmptyView.emptyView.visibility = View.VISIBLE
                val openAnim = AnimationUtils.loadAnimation(context, R.anim.enter_empty_view)
                binding.EmptyView.emptyView.startAnimation(openAnim)
            } else {
                binding.rcViewHistory.visibility = View.VISIBLE
                binding.EmptyView.emptyView.visibility = View.GONE
                adapter.submitList(it)
            }
        }
    }

И вот код самого адаптера:
class RecyclerViewAdapter(
    private val listener: Listener, private val context: Context
) : ListAdapter<ItemsList, RecyclerViewAdapter.ItemHolder>(AsyncDifferConfig.Builder(ItemComparator()).build()) {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemHolder {
        return ItemHolder.create(parent)
    }

    override fun onBindViewHolder(holder: ItemHolder, position: Int) {
        if (position != 0) {
            val previousItem = getItem(position - 1)
            holder.setData(getItem(position), listener, context, position, previousItem)
        }

        else {
            holder.setData(getItem(position), listener, context, position, null)
        }
    }

    fun onSwipedRc(position: Int) {
        val item = getItem(position)
        val id = item.id
        Log.d("MyLog", "Deleted item id was $id")

        listener.deleteItem(id!!, position)
    }

    class ItemHolder(view: View) : ViewHolder(view) {
        private val binding = ListItemBinding.bind(view)
        private var isAnim = false

        fun setData(note: ItemsList, listener: Listener, context: Context,
                    position: Int, previousItem: ItemsList?) = with(binding) {
            val name = note.categoryName!!
            val nameResID = R.string::class.java.getField(name).getInt(null)

            tvCategoryTitle.setText(nameResID)
            Glide.with(context).load(R.drawable::class.java.getField(formatIcPath(name)).getInt(null))
                .error(R.drawable.ic_others)
                .placeholder(R.drawable.ic_others).into(categoryIv)
            tvPurchaseTitle.text = note.purchaseName
            tvDescription.text = note.description
            if (tvDescription.text.isNotEmpty()) {
                ivArrow.visibility = View.VISIBLE
            }
            tvAmount.text = formatTrim(String.format("%.2f", note.amount))

            //Make date to string and set day of week
            val date = note.displayed_date
            if (date != null) {
                if (position == 0 || date != previousItem?.displayed_date) {
                    tvDay.setText(getDayOfWeek(date))
                    tvDate.text = date.substring(0, 2)
                    binding.tvDate.visibility = View.VISIBLE
                    binding.tvDay.visibility = View.VISIBLE
                } else {
                    binding.tvDate.visibility = View.GONE
                    binding.tvDay.visibility = View.GONE
                }
            } else {
                Log.d("MyLog", "Date is null")
                binding.tvDate.visibility = View.GONE
                binding.tvDay.visibility = View.GONE
            }

            if (!note.description.isNullOrEmpty()) {
                itemView.setOnClickListener {
                    Log.d("MyLog", "Clicked on item")
                    if (isAnim) {
                        return@setOnClickListener
                    }

                    if (BottomList.visibility == View.GONE && tvDescription.text.isNotEmpty()) {
                        Log.d("MyLog", "VISIBLE description")
                        val expItem = Item
                        val expHeight = BottomList

                        expandView(expItem, expHeight, context)

                        val animRotation = RotateAnimation(
                            0f, 180f,
                            Animation.RELATIVE_TO_SELF, 0.5f,
                            Animation.RELATIVE_TO_SELF, 0.5f
                        )
                        animRotation.duration = 500
                        animRotation.fillAfter = true

                        ivArrow.startAnimation(animRotation)
                    } else if (BottomList.visibility == View.VISIBLE) {
                        Log.d("MyLog", "GONE description")

                        val collItem = Item
                        val collHeight = BottomList

                        collHeight.visibility = View.GONE
                        collapseView(collItem, collHeight)

                        val animRotation = RotateAnimation(
                            180f, 0f,
                            Animation.RELATIVE_TO_SELF, 0.5f,
                            Animation.RELATIVE_TO_SELF, 0.5f
                        )
                        animRotation.duration = 250
                        animRotation.fillAfter = true

                        ivArrow.startAnimation(animRotation)
                    }
                }
            }

            itemView.setOnLongClickListener {
                listener.onLongClickItem(note, itemView)

                true
            }
        }

        private fun expandView(expItem: View, expHeight: View, context: Context) {
            isAnim = true

            val initHeight = expItem.height
            expHeight.measure(
                View.MeasureSpec.makeMeasureSpec(expItem.width, View.MeasureSpec.EXACTLY),
                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
            )
            val targetHeight = initHeight + expHeight.measuredHeight

            val animator = ValueAnimator.ofInt(initHeight, targetHeight)
            animator.addUpdateListener { animation ->
                val value = animation.animatedValue as Int
                val layoutParams = expItem.layoutParams
                layoutParams.height = value
                expItem.layoutParams = layoutParams
            }
            animator.duration = 200

            animator.start()

            animator.addListener(object: AnimatorListenerAdapter() {
                override fun onAnimationEnd(animation: Animator) {
                    super.onAnimationEnd(animation)
                    isAnim = false
                    val animFadeIn = AnimationUtils.loadAnimation(context, R.anim.fade_in)
                    expHeight.startAnimation(animFadeIn)
                    expHeight.visibility = View.VISIBLE
                }
            })
        }

        private fun collapseView(collItem: View, collHeight: View) {
            isAnim = true

            val initHeight = collItem.height
            collHeight.measure(
                View.MeasureSpec.makeMeasureSpec(collItem.width, View.MeasureSpec.EXACTLY),
                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
            )
            val targetHeight = initHeight - collHeight.measuredHeight

            val animator = ValueAnimator.ofInt(initHeight, targetHeight)
            animator.addUpdateListener { animation ->
                val value = animation.animatedValue as Int
                val layoutParams = collItem.layoutParams
                layoutParams.height = value
                collItem.layoutParams = layoutParams
            }
            animator.duration = 200
            animator.start()
            animator.addListener(object: AnimatorListenerAdapter() {
                override fun onAnimationEnd(animation: Animator) {
                    super.onAnimationEnd(animation)
                    isAnim = false
                }
            })
        }
        //...
            companion object {
            fun create(parent: ViewGroup): ItemHolder {
                return ItemHolder(
                    LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false)
                )
            }
        }
    }

    class ItemComparator : DiffUtil.ItemCallback<ItemsList>() {
        override fun areItemsTheSame(oldItem: ItemsList, newItem: ItemsList): Boolean {
            return oldItem.id == newItem.id
        }

        override fun areContentsTheSame(oldItem: ItemsList, newItem: ItemsList): Boolean {
            return oldItem == newItem
        }
    }
}

При открытии фрагмента - он подлагивает из-за адаптера (блокируется поток, анимация открытия фрагмента из-за этого становится не плавной, рваной, а если элементов в списке много - то и вовсе пропускается), как мне это исправить? Я бы хотел, чтобы при открытии фрагмента, пока адаптер не загрузится, отображалась загрузка, а после готовности списка он отображался на экране, чтобы все это было без лагов.
Я почти уверен, что наделал ошибки в коде, может эффективнее использовать совсем другой подход? Много искал информацию про загрузку адаптера, но ничего не нашел, фрагмент все еще лагает. Прошу помощи, пожалуйста подскажите и дайте направление, в чем моя ошибка.
  • Вопрос задан
  • 37 просмотров
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы