Как можно реализовать загрузку 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) } |
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) } } } |
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 } } } |
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 } } }
При открытии фрагмента - он подлагивает из-за адаптера (блокируется поток, анимация открытия фрагмента из-за этого становится не плавной, рваной, а если элементов в списке много - то и вовсе пропускается), как мне это исправить? Я бы хотел, чтобы при открытии фрагмента, пока адаптер не загрузится, отображалась загрузка, а после готовности списка он отображался на экране, чтобы все это было без лагов.
Я почти уверен, что наделал ошибки в коде, может эффективнее использовать совсем другой подход? Много искал информацию про загрузку адаптера, но ничего не нашел, фрагмент все еще лагает. Прошу помощи, пожалуйста подскажите и дайте направление, в чем моя ошибка.
Дополнительно:
Опишите проблему, и специалист поможет с настройкой, исправлением ошибки или доработкой сайта. Подберём понятный план работ без лишней переписки.
Пока нет других ответов. Будьте первым, кто поможет автору.
Ответить на вопрос
Для реализации загрузки RecyclerView в приложении Android можно использовать адаптеры и менеджеры разметки.
Во-первых, необходимо создать класс адаптера, который будет преобразовывать данные в элементы, отображаемые в RecyclerView. Пример кода адаптера для RecyclerView в языке программирования Java:
public class MyAdapter extends RecyclerView.Adapter { private List mData; public MyAdapter(List data) { this.mData = data; } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_layout, parent, false); ViewHolder viewHolder = new ViewHolder(view); return viewHolder; } @Override public void onBindViewHolder(ViewHolder holder, int position) { String item = mData.get(position); holder.textView.setText(item); } @Override public int getItemCount() { return mData.size(); } public static class ViewHolder extends RecyclerView.ViewHolder { public TextView textView; public ViewHolder(View itemView) { super(itemView); textView = itemView.findViewById(R.id.text_view); } } }
Затем необходимо создать макет элемента списка (item_layout.xml) с нужными элементами, например, TextView:
Далее, инициализируем RecyclerView в активности или фрагменте:
RecyclerView recyclerView = findViewById(R.id.recycler_view); recyclerView.setLayoutManager(new LinearLayoutManager(this)); List data = new ArrayList(); // добавляем данные в список MyAdapter adapter = new MyAdapter(data); recyclerView.setAdapter(adapter);
Теперь у вас должен отображаться список элементов в RecyclerView. Для загрузки данных по мере прокрутки можно использовать пагинацию или асинхронную загрузку данных.
Надеюсь, эта информация поможет вам реализовать загрузку RecyclerView в вашем приложении Android.