Как можно реализовать загрузку 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
}
}
}
При открытии фрагмента - он подлагивает из-за адаптера (блокируется поток, анимация открытия фрагмента из-за этого становится не плавной, рваной, а если элементов в списке много - то и вовсе пропускается), как мне это исправить? Я бы хотел, чтобы при открытии фрагмента, пока адаптер не загрузится, отображалась загрузка, а после готовности списка он отображался на экране, чтобы все это было без лагов.
Я почти уверен, что наделал ошибки в коде, может эффективнее использовать совсем другой подход? Много искал информацию про загрузку адаптера, но ничего не нашел, фрагмент все еще лагает. Прошу помощи, пожалуйста подскажите и дайте направление, в чем моя ошибка.