RecyclerView set items all at once when is inside scrollView

80 Views Asked by At

When you create dynamic list using RecyclerView your aim is to display needed item to user not all at once that causes to wait user to see the list. But i recently noticed that when you put RecyvlerView inside ScrollView or NestedScrollView deveice waits for recyclerview to insert all items and then display recyclerview to user, i suspect that recyclerView doesn't recycle items which makes user wait for almost 1.5 second.

Question: How can i put Recyclerview inside NestedScrollView and have recyclerview normal behaviour which is recycling items not showing them all at once.

RecyclerView implementation codes:

fragment_translate_from.xml Codes:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">

<data>
</data>

<androidx.coordinatorlayout.widget.CoordinatorLayout
    android:id="@+id/main_layout_translate_from"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".features.translate_from_from.ui.translate_fromFromFragment">

    <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/translate_from_action_bar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:elevation="@dimen/_20sdp"
        android:fitsSystemWindows="true">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/translate_from_tool_bar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:layout_gravity="center"
            android:layout_marginStart="@dimen/_minus7sdp"
            android:elevation="@dimen/_10sdp"
            android:gravity="center"
            android:theme="@style/ThemeOverlay.AppCompat.ActionBar"
            app:layout_collapseMode="pin"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light">

            <androidx.constraintlayout.widget.ConstraintLayout
                android:id="@+id/tool_bar_layout"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:layoutDirection="rtl">


                <TextView
                    android:id="@+id/tv_translate_from_title"
                    android:layout_width="0dp"
                    android:layout_height="0dp"
                    android:layout_centerVertical="true"
                    android:fontFamily="@font/per_vazir_regular_font"
                    android:gravity="center"
                    android:text="@string/translate_From_title"
                    android:textColor="?android:textColorPrimary"
                    android:textSize="@dimen/_17ssp"
                    app:layout_constraintBottom_toBottomOf="parent"
                    app:layout_constraintEnd_toStartOf="@+id/iv_translate_from_search"
                    app:layout_constraintStart_toEndOf="@+id/iv_translate_from_back_arrow"
                    app:layout_constraintTop_toTopOf="parent" />

                <com.google.android.material.imageview.ShapeableImageView
                    android:id="@+id/iv_translate_from_back_arrow"
                    android:layout_width="?android:actionBarSize"
                    android:layout_height="?android:actionBarSize"
                    android:layout_marginStart="@dimen/_2sdp"
                    android:background="@drawable/back_arrow_ripple_effect"
                    android:clickable="true"
                    android:scaleType="centerInside"
                    android:src="@drawable/baseline_arrow_back_24"
                    app:layout_constraintBottom_toBottomOf="parent"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintTop_toTopOf="parent" />

                <com.google.android.material.textfield.TextInputLayout
                    android:id="@+id/til_search"
                    android:layout_width="0dp"
                    android:layout_height="0dp"
                    android:visibility="gone"
                    app:endIconDrawable="@drawable/baseline_close_24"
                    app:endIconMode="clear_text"
                    app:hintEnabled="false"
                    app:layout_constraintBottom_toBottomOf="parent"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintStart_toEndOf="@+id/iv_translate_from_back_arrow"
                    app:layout_constraintTop_toTopOf="parent"
                    app:startIconDrawable="@drawable/baseline_search_24">

                    <com.google.android.material.textfield.TextInputEditText
                        android:id="@+id/et_search_input"
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:background="@color/transparent"
                        android:backgroundTint="@color/transparent"
                        android:hint="@string/languages_search_str"
                        android:paddingStart="@dimen/_20sdp"
                        android:paddingEnd="@dimen/_20sdp"
                        android:textColor="@color/enter_text_color"
                        android:textColorHint="@color/enter_text_color"
                        android:textCursorDrawable="@drawable/edit_text_cursor_drawable"
                        android:textSize="@dimen/_12ssp" />
                </com.google.android.material.textfield.TextInputLayout>


                <com.google.android.material.imageview.ShapeableImageView
                    android:id="@+id/iv_translate_from_search"
                    android:layout_width="?android:actionBarSize"
                    android:layout_height="?android:actionBarSize"
                    android:layout_marginStart="@dimen/_2sdp"
                    android:background="@drawable/back_arrow_ripple_effect"
                    android:clickable="true"
                    android:scaleType="centerInside"
                    android:src="@drawable/baseline_search_24"
                    app:layout_constraintBottom_toBottomOf="parent"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintTop_toTopOf="parent" />


            </androidx.constraintlayout.widget.ConstraintLayout>
        </androidx.appcompat.widget.Toolbar>
    </com.google.android.material.appbar.AppBarLayout>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/views_layout_translate_from"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <androidx.core.widget.NestedScrollView
            android:id="@+id/views_scroll_views"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:fillViewport="true"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent">


            <androidx.constraintlayout.widget.ConstraintLayout
                android:id="@+id/inner_views_layout_translate_from"
                android:layout_width="match_parent"
                android:layout_height="wrap_content">


                <TextView
                    android:id="@+id/tv_all_languages"
                    android:layout_width="0dp"
                    android:layout_height="@dimen/_40sdp"
                    android:gravity="top|start"
                    android:paddingStart="@dimen/_10sdp"
                    android:paddingTop="@dimen/_10sdp"
                    android:paddingEnd="@dimen/_10sdp"
                    android:text="@string/all_languages_str"
                    android:textColor="?android:textColorPrimary"
                    android:textSize="@dimen/_14ssp"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintTop_toTopOf="parent" />

                <androidx.recyclerview.widget.RecyclerView
                    android:id="@+id/all_langs_recycler_view"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:nestedScrollingEnabled="false"
                    android:orientation="vertical"
                    app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintTop_toBottomOf="@+id/tv_all_languages" />


            </androidx.constraintlayout.widget.ConstraintLayout>
        </androidx.core.widget.NestedScrollView>
    </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

RecyclerView in fragment:

private fun setAllLanguagesRecyclerView() {
    lifecycleScope.launch(Dispatchers.IO) {
        languagesList = viewModel.getAllLanguagesName().toMutableList()
        val layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
        val position = viewModel.getAllSelectedPosition()

        val adapter = AllLanguagesAdapter { allLangModel ->
            viewModel.insertRecentLanguages(allLangModel)
            sendBackValueToPreviousBackStack(allLangModel.langCode)
        }

        binding.allLangsRecyclerView.layoutManager = layoutManager
        adapter.submitList(languagesList)
        withContext(Dispatchers.Main) {
            binding.allLangsRecyclerView.adapter = adapter
            setBackgroundToLanguagesRecyclerViewItem(
                recyclerView = binding.allLangsRecyclerView,
                position = position
            )
            setAdiveryNativeAd(adapter)
            setSearch(adapter)
        }
    }
}

AllLanguagesAdapter

class AllLanguagesAdapter constructor(private val langOnClick: (AllLangModel) -> Unit) :
ListAdapter<AllLangModel, AllLanguagesAdapter.ViewHolder>(AllLanguagesDiffCallBack) {


class ViewHolder constructor(
    private val binding: TranslateFromLanguageItemBinding,
    private val langOnClick: (AllLangModel) -> Unit
) : RecyclerView.ViewHolder(binding.root) {
    private var currentLangModel: AllLangModel? = null
    val innerBinding: TranslateFromLanguageItemBinding = binding


    init {
        itemView.setOnClickListener {
            currentLangModel?.let { allLangModel ->
                langOnClick(allLangModel)
            }
        }
    }


    fun bind(langModel: AllLangModel) {
        if (langModel.langCode.equals("en", true)){
            langModel.downStatus = true
        }
        this.currentLangModel = langModel
        binding.langModel = langModel
    }
}

override fun onCreateViewHolder(
    parent: ViewGroup,
    viewType: Int
): ViewHolder {
    val inflate = LayoutInflater.from(parent.context)
    val binding: TranslateFromLanguageItemBinding =
        DataBindingUtil.inflate(inflate, R.layout.translate_from_language_item, parent, false)
    return ViewHolder(binding, langOnClick)
}

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    val langModel: AllLangModel = getItem(position)
    holder.bind(langModel = langModel)
}
}

object AllLanguagesDiffCallBack : DiffUtil.ItemCallback<AllLangModel>() {
    override fun areItemsTheSame(oldItem: AllLangModel, newItem: AllLangModel): Boolean {
        return oldItem == newItem
    }

override fun areContentsTheSame(
    oldItem: AllLangModel,
    newItem: AllLangModel
): Boolean {
    return oldItem.hashCode() == newItem.hashCode()
}
}
2

There are 2 best solutions below

1
zaid khan On

Use recycler view inside NestedScrollView and add this property to the recylerview.

app:layout_behavior="@string/appbar_scrolling_view_behavior"

this will manage the rest of the things

2
cactustictacs On

Your RecyclerView is displaying all its contents because you've set its height to wrap_content. You need to constrain its height to get scrolling behaviour when the contents are larger than that height. (And specifically, the scrolling system is where you get the benefits of a RecyclerView, since it swaps a pool of views around to display the visible section of the list, rather than creating views for every item.)

So you should probably set a height for the RecyclerView, or its parent ConstraintLayout while allowing the RecyclerView to fill the available space. But why do you want to put a RecyclerView inside a ScrollView in the first place? They're both ScrollingViews, and a RecyclerView is already handling its specific use case - you don't need to wrap it in another scroller.

If it's because you want that TextView to scroll out of view, like when you scroll to the top of the list it appears, you'll honestly probably have an easier time just displaying it inside the RecyclerView itself, as the first item. Maybe with its own ViewHolder type, or possibly using a ConcatAdapter to have an Adapter that displays that text, and then the "real" adapter, so you can keep using the latter as-is without adding logic to account for the extra dummy item.