androidkotlinandroid-recyclerviewandroid-viewbindingandroid-include

ViewBinding not working for layout included inside RecyclerView item


This is my RecyclerView Item Layout

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout 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"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"><!--
    android:background="@color/home_item_bg"-->

    <include
        android:id="@+id/layoutSectionHeader"
        layout="@layout/section_header_rv_item_home"/>

    <androidx.cardview.widget.CardView
        style="@style/Widget.MaterialComponents.CardView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="16dp"
        app:cardElevation="2dp">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal"
            android:orientation="vertical"
            android:paddingStart="16dp"
            android:paddingTop="8dp"
            android:paddingEnd="16dp"
            android:paddingBottom="8dp">

            <TextView
                android:id="@+id/tvMarketAndTerm"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textStyle="bold"
                tools:text="NASDAQ - Medium Term" />

            <com.github.mikephil.charting.charts.PieChart
                android:id="@+id/pieChart"
                android:layout_width="match_parent"
                android:layout_height="@dimen/pie_chart_height" />

        </LinearLayout>
    </androidx.cardview.widget.CardView>
</LinearLayout>

This is section_header_rv_item_home.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/llSectionHeader"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center_vertical"
    android:orientation="horizontal"
    android:paddingStart="16dp"
    android:paddingTop="12dp"
    android:background="?selectableItemBackground"
    android:paddingEnd="16dp"
    android:paddingBottom="12dp"><!--
    android:background="@drawable/ripple_white_light_grey"-->

    <TextView
        android:id="@+id/tvProductName"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:ellipsize="end"
        android:maxLines="1"
        android:textAllCaps="false"
        android:textColor="@color/gray"
        android:textSize="20sp"
        android:textStyle="bold"
        tools:text="@string/stock_exchange_barometer" />

    <TextView
        android:id="@+id/tvSeeAll"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/see_all"
        android:textColor="@color/colorAccent"
        android:textSize="13sp"
        android:textStyle="bold"
        android:visibility="gone"
        tools:visibility="visible" />
</LinearLayout>

This is how'm accessing the views inside the included layout

inner class BarometerVH(private val rvBinding: RvItemHomeSeBarometerBinding) :
            RecyclerView.ViewHolder(rvBinding.root) {
    fun bind(homeItem: HomeItem) {
        rvBinding.layoutSectionHeader.tvProductName.text = homeItem.title
    }
}

I got no compile time error, but getting this exception when I run the app

java.lang.NullPointerException: Missing required view with ID: com.example.appname:id/layoutSectionHeader

Including a layout in Activity or Fragment works for me, but not working when used in Recycler View.

I tried multiple solutions given in this forum for similar exceptions, but none worked for a recycler view and not question regarding using <include inside recycler view.

Edit 1: I've added the overridden methods from the RecyclerView Adapter

override fun onCreateViewHolder(
     parent: ViewGroup,
     viewType: Int
) = MarketCommentaryVH(RvItemHomeMarketCommentaryBinding.bind(parent))

override fun onBindViewHolder(
     holder: RecyclerView.ViewHolder,
     position: Int
) {
    holder.bind(homeItemList[position])
}

Solution

  • A RecyclerView works by creating and reusing a bunch of ViewHolders, which hold a view layout to display the details of an item. You set these up in the onCreateViewHolder method, where you:

    You're not actually inflating the layout though, you're doing

    RvItemHomeMarketCommentaryBinding.bind(parent)
    

    instead of

    RvItemHomeMarketCommentaryBinding.inflate(LayoutInflater.from(context), parent, false)
    

    (you need to pass in the parent while inflating a ViewHolder's layout, so it can lay itself out with the correct dimensions etc)


    The bind function on a Binding component takes an already inflated view hierarchy, and tries to find all the components with the IDs it knows about. So for example it tries to find layoutSectionHeader so it can assign binding.layoutSectionHeader to that view.

    Trouble is, that doesn't exist in the parent layout you're passing in, because that's just the RecyclerView's layout, it doesn't contain the stuff from the ViewHolder layout. So bind doesn't work - the views aren't there to bind to. You need to call inflate instead