Mobile Development 12 min read

Mastering Android Layout Reuse: From <include> to ViewBinding and Kotlin Extensions

A junior Android developer learns to extract a card UI into a reusable layout, replace repetitive findViewById code with a custom view, then streamline everything further using Jetpack ViewBinding and Kotlin extension functions, dramatically cutting boilerplate and simplifying maintenance.

Sohu Tech Products
Sohu Tech Products
Sohu Tech Products
Mastering Android Layout Reuse: From <include> to ViewBinding and Kotlin Extensions

Layout Reuse with <include>

Define the reusable card UI in a separate layout file card_item.xml. The layout uses androidx.cardview.widget.CardView containing an ImageView and two TextView s.

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="100dp"
    app:cardCornerRadius="5dp"
    android:layout_margin="10dp"
    app:cardElevation="2dp">
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <ImageView
            android:id="@+id/avatar"
            android:layout_width="80dp"
            android:layout_height="90dp"
            android:src="@mipmap/logo"
            android:scaleType="centerCrop"
            android:layout_centerVertical="true"
            android:layout_marginLeft="15dp" />
        <TextView
            android:id="@+id/name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="#333"
            android:textSize="18sp"
            android:layout_toRightOf="@+id/avatar"
            android:layout_marginLeft="5dp"
            android:layout_marginTop="10dp" />
        <TextView
            android:id="@+id/des"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="#999"
            android:textSize="12sp"
            android:layout_below="@+id/name"
            android:layout_toRightOf="@+id/avatar"
            android:layout_marginLeft="5dp"
            android:layout_marginTop="10dp" />
    </RelativeLayout>
</androidx.cardview.widget.CardView>

Include this layout in any fragment layout using the <include> tag:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <include layout="@layout/card_item" />
</LinearLayout>

In the fragment, bind the views with findViewById and set data.

class MyFragment : Fragment() {
    private lateinit var avatar: ImageView
    private lateinit var name: TextView
    private lateinit var desc: TextView

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        val view = inflater.inflate(R.layout.my_fragment, container, false)
        avatar = view.findViewById(R.id.avatar)
        name = view.findViewById(R.id.name)
        desc = view.findViewById(R.id.des)
        return view
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        avatar.setImageResource(R.mipmap.logo)
        name.text = "技术最TOP"
        desc.text = "扒最前沿科技动态,聊最TOP编程技术~"
    }
}

Encapsulating the Card in a Custom View

To avoid repeating view‑lookup code, create a custom view CardItem that inflates card_item.xml internally and provides a setData method.

class CardItem @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {

    private var ivAvatar: ImageView
    private var tvName: TextView
    private var tvDesc: TextView

    init {
        val view = LayoutInflater.from(context).inflate(R.layout.card_item, null, false)
        ivAvatar = view.findViewById(R.id.avatar)
        tvName = view.findViewById(R.id.name)
        tvDesc = view.findViewById(R.id.des)
        addView(view)
    }

    fun setData(imageRes: Int, name: String, desc: String) {
        ivAvatar.setImageResource(imageRes)
        tvName.text = name
        tvDesc.text = desc
    }
}

Use the custom view directly in XML and call setData from the fragment.

<com.jay.jetpack.viewbinding.CardItem
    android:id="@+id/card_item"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    cardItem.setData(R.mipmap.logo, "技术最TOP", "扒最前沿科技动态,聊最TOP编程技术~")
}

Jetpack ViewBinding for Boilerplate Elimination

Enable ViewBinding in the module’s build.gradle:

viewBinding {
    enabled = true
}

Gradle generates a binding class for each layout (e.g., CardItemBinding for card_item.xml and MyFragment2Binding for my_fragment2.xml). The class name is derived from the layout file name by removing underscores and appending Binding.

Replace the custom view usage with an <include> that has an ID, then access its views through the generated binding:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <include android:id="@+id/topCard" layout="@layout/card_item" />
</LinearLayout>

Fragment code using ViewBinding:

class MyFragment2 : Fragment(R.layout.my_fragment2) {
    private lateinit var binding: MyFragment2Binding

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        binding = MyFragment2Binding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding.topCard.apply {
            avatar.setImageResource(R.mipmap.logo)
            name.text = "技术最TOP"
            des.text = "扒最前沿科技动态,聊最TOP编程技术。"
        }
    }
}

Kotlin Extension Function Combined with ViewBinding

Define a concise extension on the generated binding to set all fields in one call.

fun CardItemBinding.bind(imageResId: Int, nameStr: String, descStr: String) {
    avatar.setImageResource(imageResId)
    name.text = nameStr
    des.text = descStr
}

Now the fragment can populate the card with a single line:

binding.topCard.bind(R.mipmap.logo, "技术最TOP Super", "扒最前沿科技动态,聊最TOP编程技术。Super")

This approach reduces the custom view class from dozens of lines to a few lines of extension code, centralises layout changes, and eliminates repetitive findViewById calls across multiple fragments.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

AndroidKotlinCustom ViewViewBindingInclude TagLayout Reuse
Sohu Tech Products
Written by

Sohu Tech Products

A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.