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

UI Performance Optimization and Best Practices

Updated: May 31, 2026
6 min read

# CHAPTER 19

UI Performance Optimization and Best Practices

1. Introduction

You've designed a gorgeous UI, but when you install it on a 3-year-old budget smartphone, the animations stutter, and scrolling through a list feels like wading through mud. UI performance is critical. Android aims to render UI at 60 frames per second (fps) or higher. That gives you roughly 16 milliseconds to draw each frame. If your UI takes 25ms to draw, frames are dropped, resulting in "jank" (stuttering). In this chapter, we will learn how to optimize our XML and Kotlin code for buttery-smooth performance.

2. Learning Objectives

By the end of this chapter, you will be able to:
  • Identify and eliminate UI Overdraw.
  • Flatten deep layout hierarchies.
  • Understand the cost of layout passes.
  • Optimize RecyclerView performance.
  • Use Android Studio's Profiler tools.

3. Understanding and Reducing Overdraw

Overdraw happens when the system draws a pixel on the screen multiple times in a single frame. For example:
  1. 1. You have a ConstraintLayout with a white background.
  1. 2. Inside it, a LinearLayout with a white background.
  1. 3. Inside that, a TextView with a white background.
The system just painted the exact same white pixel 3 times! This wastes GPU processing power.

How to fix it:

  • Remove unnecessary backgrounds. If the parent layout has a white background, remove the background from the child layouts.
  • You can visualize overdraw on your phone: Developer Options -> "Debug GPU Overdraw" -> "Show overdraw areas". (Blue = 1x overdraw, Green = 2x, Pink = 3x, Red = 4x+). Aim for true color or blue!

4. Flattening Layout Hierarchies

Every nested layout (a layout inside a layout inside a layout) exponentially increases the time it takes Android to "measure" the UI before drawing it. Deeply nested LinearLayouts using layout_weight are notoriously slow because they require multiple measurement passes.

How to fix it:

  • Replace deeply nested LinearLayout and RelativeLayout structures with a single, flat ConstraintLayout. ConstraintLayout was specifically built to solve the measurement performance problem.

5. RecyclerView Optimization

The RecyclerView is already optimized, but bad code can ruin it.
  • Do not put heavy logic in onBindViewHolder: This function is called hundreds of times per second while scrolling. Do not parse dates, format strings, or do math here. Do all formatting in the background before passing the data to the Adapter.
  • Image Loading: Never load raw bitmaps manually on the main thread. Always use a library like Glide or Coil. They handle memory caching, resizing, and background threading automatically.
kotlin
12345
// Efficient Image Loading with Glide
Glide.with(context)
    .load(imageUrl)
    .placeholder(R.drawable.ic_loading)
    .into(holder.imageView)

6. ViewStubs for Lazy Loading

If you have a complex UI element that is only shown *sometimes* (e.g., an error screen, or an expandable details panel), do not put it in your XML and set it to android:visibility="gone". Even if it is GONE, Android still inflates it into memory when the screen loads!

Instead, use a <ViewStub>. A ViewStub is a zero-sized, invisible view that lazy-inflates its layout resource only when you explicitly tell it to.

xml
12345
<ViewStub
    android:id="@+id/errorViewStub"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout="@layout/view_network_error" />
kotlin
12
// Only when an error occurs, inflate it:
findViewById<ViewStub>(R.id.errorViewStub).inflate()

7. Avoiding Allocation Churn

Creating objects takes time, and destroying them (Garbage Collection) pauses the app. If you create new objects (like new Paint(), new Path(), or new Rect()) inside a custom view's onDraw() method, you will trigger massive Garbage Collection pauses, causing severe lag. Always allocate objects in the init block of custom views, never in onDraw.

8. Common Mistakes

  • Transparent overlapping views: Having many layers of semi-transparent (alpha < 1) views forces the GPU to blend colors continuously, which is computationally expensive.
  • Setting static backgrounds in Code: If your window background is solid white, set it in your themes.xml rather than in every single layout file to prevent double-drawing the window background.

9. Best Practices

  • Use the Layout Inspector in Android Studio to view a 3D representation of your view hierarchy and spot unnecessary nesting.
  • When working with SVGs (VectorDrawables), keep the paths simple. Highly complex SVGs with thousands of path points take a long time to render.

10. Exercises

  1. 1. Enable "Debug GPU Overdraw" in your physical device's Developer Options. Open a popular app (like WhatsApp or Twitter) and observe how they handle overdraw. Then open your own app!
  1. 2. Refactor a nested LinearLayout from an earlier project into a single ConstraintLayout.

11. UI Design Challenges

Challenge: Review the UI layout for a complex e-commerce product page. Identify at least three areas where <ViewStub> could be used to improve initial load time (Hint: Out of stock banners, hidden review sections, error states).

12. MCQ Quiz with Answers

Question 1

What tool provides a visual heat-map on your device screen to help you identify areas where pixels are being drawn multiple times?

Question 2

Why is nesting multiple LinearLayouts with layout_weight detrimental to performance?

13. Interview Questions

  • Q: Explain what UI Overdraw is and three ways to prevent it.
  • Q: Why should you avoid instantiating new objects inside a custom view's onDraw() method?
  • Q: What is a ViewStub and when would you use it instead of View.GONE?

14. FAQs

Q: I have a ConstraintLayout, but my app is still lagging. Why? A: Layout hierarchy is just one piece of the puzzle. Lag is often caused by doing heavy work (database queries, network calls, or large JSON parsing) on the Main (UI) Thread. Ensure all non-UI logic is moved to background threads using Coroutines or RxJava.

15. Summary

Performance is a feature. In this chapter, we learned that hitting 60fps requires clean, flat XML hierarchies, the elimination of overdraw, and smart lazy-loading with ViewStub. By respecting the GPU and CPU constraints of mobile devices, we ensure our beautiful designs are actually a joy to use.

16. Next Chapter Recommendation

You have acquired all the knowledge needed to be a professional Android UI Designer. It is time to put it all together. In our final chapter, Chapter 20: Final Project - Design a Complete Android App UI, we will build a full, multi-screen application layout combining everything we've learned.

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: ·