Skip to main content
Android UI Design with Kotlin
CHAPTER 17 Beginner

Custom UI Components and Reusable Design

Updated: May 31, 2026
6 min read

# CHAPTER 17

Custom UI Components and Reusable Design

1. Introduction

Imagine building an app with 20 screens. If you manually code the exact same "Submit" button with its specific margins, paddings, rounded corners, and text style 20 different times, you have created a maintenance nightmare. What if the client wants to change the corner radius? You would have to update 20 files! The solution is building Reusable Custom Components. In this chapter, we will learn how to build Compound Views and implement a true Design System in Android.

2. Learning Objectives

By the end of this chapter, you will be able to:
  • Understand the DRY (Don't Repeat Yourself) principle in UI design.
  • Use the <include> tag to reuse XML layouts.
  • Build a Custom Compound View using Kotlin and XML.
  • Define custom XML attributes (declare-styleable).
  • Understand the basics of a Design System.

3. Reusing Layouts with <include>

The simplest way to reuse UI is the <include> tag. If you have a custom Toolbar or a specific Card design that appears on multiple screens, define it once in a separate file.

Step 1: Create layout/mycustomcard.xml:

xml
123
<com.google.android.material.card.MaterialCardView ...>
    <TextView android:text="Shared Content" />
</com.google.android.material.card.MaterialCardView>

Step 2: Use it anywhere:

xml
123
<LinearLayout ...>
    <include layout="@layout/my_custom_card" />
</LinearLayout>

*Tip: You can override android:id, layoutwidth, and layoutheight directly in the <include> tag.*

4. Custom Compound Views

<include> is great for static layouts, but what if you want a custom component that has complex logic attached to it? A Compound View is a custom Kotlin class that inflates a layout and handles its own logic.

Let's build a ProfileInfoRow (an icon on the left, title text, and subtitle text).

Step 1: The XML (viewprofilerow.xml)

xml
12345
<merge xmlns:android="http://schemas.android.com/apk/res/android">
    <ImageView android:id="@+id/rowIcon" ... />
    <TextView android:id="@+id/rowTitle" ... />
    <TextView android:id="@+id/rowSubtitle" ... />
</merge>

*Note: We use <merge> instead of a Layout to prevent unnecessary nesting when we inflate it.*

Step 2: The Kotlin Class

kotlin
1234567891011121314151617181920212223242526272829
class ProfileInfoRow @JvmOverloads constructor(
    context: Context, 
    attrs: AttributeSet? = null, 
    defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr) {

    private val icon: ImageView
    private val title: TextView
    private val subtitle: TextView

    init {
        // Inflate the XML
        LayoutInflater.from(context).inflate(R.layout.view_profile_row, this, true)
        orientation = HORIZONTAL
        
        icon = findViewById(R.id.rowIcon)
        title = findViewById(R.id.rowTitle)
        subtitle = findViewById(R.id.rowSubtitle)
        
        // Custom logic can go here!
    }
    
    // Methods to change data dynamically
    fun setRowData(titleText: String, subtitleText: String, iconRes: Int) {
        title.text = titleText
        subtitle.text = subtitleText
        icon.setImageResource(iconRes)
    }
}

Step 3: Using it in XML

xml
1234
<com.yourpackage.ProfileInfoRow
    android:id="@+id/phoneRow"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

5. Design Systems

A Design System is a collection of reusable components, guided by clear standards, that can be assembled together to build any number of applications. By creating Compound Views for your Buttons, Inputs, Cards, and Headers, you are effectively building your own Design System on top of Android's framework. This ensures that a button looks exactly the same on the Settings screen as it does on the Checkout screen.

6. Defining Custom XML Attributes

To make your Compound View truly reusable, you can pass parameters to it directly from XML. Create res/values/attrs.xml:
xml
123456
<resources>
    <declare-styleable name="ProfileInfoRow">
        <attr name="rowTitle" format="string" />
        <attr name="rowIcon" format="reference" />
    </declare-styleable>
</resources>

Then, in your Kotlin init block, you read these attributes using context.obtainStyledAttributes and apply them immediately!

7. Common Mistakes

  • Not using <merge>: If your custom Kotlin class extends LinearLayout, and you inflate an XML file that has a LinearLayout root, you end up with a LinearLayout inside a LinearLayout. Using <merge> as the root of the inflated XML prevents this.
  • Memory Leaks: If your custom view registers listeners to singletons or static variables, remember to unregister them in onDetachedFromWindow.

8. Best Practices

  • Start small. Don't build a massive custom view framework from day one. When you notice you are copying and pasting the same XML for a 3rd time, extract it into a custom component.
  • Use Android's styles.xml to globally style standard widgets before resorting to custom Kotlin classes.

9. Exercises

  1. 1. Extract a complex button layout you made in a previous chapter into its own XML file and use the <include> tag to place it on a different screen.
  1. 2. In the ProfileInfoRow example, add a function hideSubtitle() that sets the subtitle's visibility to GONE.

10. UI Design Challenges

Challenge: Create a custom Compound View called QuantitySelector. It should consist of a "Minus" button, a "Number" TextView, and a "Plus" button. Handle the click logic internally within the Kotlin class so that clicking Plus increases the number, and Minus decreases it (but not below 0).

11. MCQ Quiz with Answers

Question 1

What is the primary benefit of creating Custom Compound Views?

Question 2

Which XML tag should you use as the root of a layout being inflated by a custom ViewGroup class to avoid unnecessary layout nesting?

12. Interview Questions

  • Q: Explain how you would pass a custom XML attribute (like app:customColor) into a custom Kotlin View.
  • Q: What is the difference between styling a standard Button via styles.xml and creating a custom Compound View button?

13. FAQs

Q: With Jetpack Compose (Kotlin UI), are Custom Views obsolete? A: In the new Jetpack Compose paradigm, building custom components is significantly easier (it's just a Kotlin function). However, millions of apps still use XML, and understanding Compound Views is essential for maintaining or integrating with legacy codebases.

14. Summary

Reusability is the hallmark of a professional developer. By leveraging <include>, styles, and Custom Compound Views, we can build a robust Design System. This not only makes our app visually consistent but dramatically reduces the time it takes to build new features.

15. Next Chapter Recommendation

Our UI is beautiful and reusable, but is it usable by *everyone*? In Chapter 18: Accessibility and Mobile UX Best Practices, we will learn how to design apps that cater to users with visual impairments, motor difficulties, and how to improve overall User Experience.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·