Overdraw and Optimizing layouts in Android

VenkateshPrasad
Wenable
Published in
6 min readMar 25, 2019

While developing an Android Application, the UI is one of the most critical parts, if not the most critical. Any jerks or lag or performance issues tend to antagonize a user from using the App. An excellent mechanism to identify problems that may occur with the UI can be addressed by Overdraw Tool. This article focuses on the “Overdraw” issue which challenges every developer while profiling the UI. As all Android developers know, that GPU(Graphics Processing Unit) does a lot of work to draw the views on the screen — to test the performance of GPU, Android provides “GPU overdraw tool” by abstracting the background process. Let’s see what is the overdraw and how we can eliminate it.

A pixel on the screen can be drawn multiple times during the process of showing the exact layout of the UI in a frame, These multiple draws on pixel called as overdraw, represented in four stages which are 1x, 2x, 3x and 4x. It doesn’t mean that x number of nested view groups will cause x number of overdraws. In other words, we may have a complicated nested view group, but there may be no overdraws at all. It is not about the nested view groups but the pixel that is used to draw the UI element. If a pixel is drawn for the first time we call it a “true color”, if the same pixel is drawn the second time, it is called as 1x overdraw. As many times as a pixel has drawn on top of an existing pixel, that many overdraws are made. So drawing it 1 time after the original drawing means the UI has 1x overdraws, 2 times means 2x overdraws and so on. Overdraw is unavoidable sometimes, but it has to be kept at a minimum.

Official color codes for the overdraw provided in Android Developer site

We can enable this tool through Settings -> Developer Options -> Debug GPU overdraw -> Show overdraw areas. Once you enable it, the screen starts showing colors, don’t worry it is the expected behavior.

Enable GPU overdraw tool

An example will best illustrate what we have discussed so far. The UI below shows a list of details, every detail is a single item of a list. A recycler view has been considered, as most of the times, card views are used to make UI look beautiful. Note that the card view increases one layer of overdraw and there are layers which are added on top of another as displayed below:

A.Details recycler view.
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/list_item_details_root"
android:layout_width="match_parent"
android:layout_height="@dimen/conference_item_height"
android:clickable="true"
android:elevation="@dimen/card_elevation_large"
app:cardUseCompatPadding="true">
<LinearLayout
android:id="@+id/list_item_details_actions_rl"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/conference_list_item_background"
android:orientation="vertical"
android:visibility="visible"
android:weightSum="1">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_margin="@dimen/marginSizeMicroPlusPlus"
android:layout_weight="0.75">
<ImageView
android:id="@+id/list_item_details_lv"
android:layout_width="@dimen/conference_image_width"
android:layout_height="match_parent"
android:src="@mipmap/ic_launcher"
android:scaleType="fitXY" />
<TextView
android:id="@+id/list_item_details_address_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/marginSizeMicroPlusPlus"
android:layout_toEndOf="@+id/list_item_details_lv"
android:maxLines="1"
android:text="Title"
android:textSize="@dimen/textSizeSmallPlus"
android:textStyle="bold" />
<TextView
android:id="@+id/list_item_details_date_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/list_item_details_address_tv"
android:layout_marginStart="@dimen/marginSizeMicroPlusPlus"
android:layout_marginTop="@dimen/marginSizeNano"
android:layout_toEndOf="@+id/list_item_details_lv"
android:maxLines="1"
android:text="date"
android:paddingEnd="@dimen/paddingSizeSmall"
android:paddingStart="@dimen/paddingSizeSmall"
android:textSize="@dimen/textSizeMicro" />
<TextView
android:id="@+id/list_item_details_title_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/list_item_details_date_tv"
android:layout_marginStart="@dimen/marginSizeMicroPlusPlus"
android:layout_toEndOf="@+id/list_item_details_lv"
android:ellipsize="end"
android:text="desc"
android:maxLines="2"
android:textSize="@dimen/textSizeSmall" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/list_item_details_actions_rl"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="0.25"
android:background="@color/white"
android:paddingEnd="@dimen/marginSizeMicroPlusPlus"
android:paddingStart="@dimen/marginSizeMicroPlusPlus">
<ImageView
android:id="@+id/list_item_details_favourite_iv"
android:layout_width="@dimen/stdBtnHeightplus"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:visibility="gone"
android:src="@drawable/ic_favorite" />
<ImageView
android:id="@+id/list_item_details_share_iv"
android:layout_width="@dimen/stdBtnHeightplus"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toEndOf="@+id/list_item_details_favourite_iv"
android:src="@drawable/ic_share" />
<ImageView
android:id="@+id/list_item_details_calender_iv"
android:layout_width="@dimen/stdBtnHeightplus"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toEndOf="@+id/list_item_details_share_iv"
android:padding="@dimen/_5sdp"
android:src="@drawable/ic_calendar" />
<TextView
android:id="@+id/list_item_details_status_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:textAllCaps="true"
android:text="Status"
android:textStyle="bold" />
</RelativeLayout>
</LinearLayout>
</android.support.v7.widget.CardView>

The image below explains how GPU draws the UI, step by step, to achieve the above layout shown in image(A). I have captured the images by making the views visible and gone to explain it better.

Image B: GPU draws this by default based on the theme of the activity, so this is called “True Color.”

Image C: On top of true color (Ref Image B), GPU draws the card view, so it is 1x overdraw.

Image D(Layout ids appear bold in the code):

  1. In the code, LinearLayout (@+id/list_item_details_main_ll) has background light gray, so GPU draws this color on top of card view, this becomes 2x overdraw.
  2. Inside of LinearLayout (@+id/list_item_details_main_ll), there is a relative layout, it has an image, title etc. These are 3x overdraws.

Image E(Layout ids appear as bold in the code):

Inside of LinearLayout (@+id/list_item_details_main_ll), There is RelativeLayout(@+id/list_item_details_actions_rl), which has background white, so that is also 3x overdraw and on top of that you can find action like calendar and share, these become 4x overdraw.

Almost all Android UIs make use of views, transitions or animations and/or a combination of all these. These components are potential areas where problems of overdraw may occur which in turn may affect the performance of the GPU. According to Gooogle blog on “Frame Rate” mentioned here, Android has to do render within 16ms (Frame Refresh Rate = 60 frames per second). Ideally, the application should be optimized as much as possible. An increase in the number of overdraws will definitely cause the user to experience “lag” or “jank” which is bad UX.

Most cases of overdraw occur with the background color or with complex view hierarchy. In the case demonstrated above, a decrease of overdraw can be achieved by removing the background for LinearLayout(list_item_details_actions_rl) and adding it to root CardView(list_item_details_root). Now since the base layer itself is drawn in the color of the base layer, it decreases the overdraw by 1 layer. The image below shows, overdraw scale after making the changes.

F.Overdraw optimized

When both image E and F are compared, one layer of overdraw has been eliminated.

Another way of optimizing layouts is by using ConstraintLayout to eliminate nested view groups.

Constraint layout is a part of Android JetPack. As you know, Constraint layout is a “ViewGroup” that we have in the Android. Clean and fast Android UI development through the Android Studio UI is the dream of all Android developers and this nifty feature has satiated that particular need by introducing “Constraint Layout” and “Layout Editor”. Constraint layout can be understood by any developer really quickly and there are many advantages using it, of which, few of them are listed here

Advantages with the “ConstraintLayout”:

  1. Avoid the nested ViewGroups.
  2. Decreases the UI development time by providing the large attribute set.
  3. Reduces efforts to write the XML code because we do almost everything through the layout editor

There are three mains steps involved while drawing view onto to screen, which are measure, layout and draw. Android does expensive calculations and executions in these phases. If we used nested view groups while making a complex UI, it directly impacts the CPU performance, so it better to avoid nested view groups, can be easily achieved by using the constraint layout. I have optimized the same layout XML code, after using constraint layout, after that XML has zero nested view groups.

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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="wrap_content"
android:padding="@dimen/paddingSizeMediumPlus"
android:elevation="3dp">
<android.support.v7.widget.CardView
android:id="@+id/list_item_conference_root"
android:layout_width="match_parent"
android:layout_height="@dimen/conference_item_height"
android:clickable="true"
app:cardBackgroundColor="@color/light_gray"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1.0" />
<ImageView
android:id="@+id/list_item_conference_lv"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:elevation="2dp"
android:scaleType="fitXY"
app:layout_constraintBottom_toTopOf="@+id/guideline4"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/list_item_conference_root"
app:srcCompat="@mipmap/ic_launcher_copy" />
<android.support.constraint.Guideline
android:id="@+id/guideline4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.7" />
<TextView
android:id="@+id/list_item_conference_status_tv"
android:layout_width="0dp"
android:layout_height="0dp"
android:elevation="2dp"
android:text="Status"
android:paddingRight="@dimen/paddingSizeSmall"
android:textAllCaps="true"
android:gravity="center_vertical|end"
android:textStyle="bold"
android:background="@color/white"
app:layout_constraintBottom_toBottomOf="@id/list_item_conference_root"
app:layout_constraintEnd_toEndOf="@+id/list_item_conference_root"
app:layout_constraintStart_toStartOf="@+id/list_item_conference_root"
app:layout_constraintTop_toTopOf="@+id/guideline4" />
<ImageView
android:id="@+id/list_item_conference_share_iv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:elevation="2dp"
app:layout_constraintBottom_toBottomOf="@+id/list_item_conference_root"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/guideline4"
app:srcCompat="@drawable/ic_share" />
<ImageView
android:id="@+id/list_item_conference_calender_iv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:elevation="2dp"
app:layout_constraintBottom_toBottomOf="@+id/list_item_conference_share_iv"
app:layout_constraintStart_toEndOf="@+id/list_item_conference_share_iv"
app:layout_constraintTop_toTopOf="@+id/list_item_conference_share_iv"
app:srcCompat="@drawable/ic_calendar" />
<TextView
android:id="@+id/list_item_conference_address_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:elevation="2dp"
android:text="Title"
android:textStyle="bold"
app:layout_constraintStart_toEndOf="@+id/list_item_conference_lv"
app:layout_constraintTop_toTopOf="@+id/list_item_conference_lv" />
<TextView
android:id="@+id/list_item_conference_date_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:elevation="2dp"
android:text="TextView"
app:layout_constraintStart_toStartOf="@+id/list_item_conference_address_tv"
app:layout_constraintTop_toBottomOf="@+id/list_item_conference_address_tv" />
<TextView
android:id="@+id/list_item_conference_title_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:elevation="2dp"
android:text="Des"
app:layout_constraintStart_toStartOf="@+id/list_item_conference_date_tv"
app:layout_constraintTop_toBottomOf="@+id/list_item_conference_date_tv" />
<ImageView
android:id="@+id/list_item_conference_favourite_iv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:elevation="2dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@+id/list_item_conference_calender_iv"
app:layout_constraintStart_toEndOf="@+id/list_item_conference_calender_iv"
app:layout_constraintTop_toTopOf="@+id/list_item_conference_calender_iv"
app:srcCompat="@drawable/ic_favorite" />
</android.support.constraint.ConstraintLayout>

Conclusion:

The nice visual representation and a seamless transition is a precursor to a successful and popular mobile application. Any issues like lag, cause a bad UI experience for the user and should be avoided. In this article, we have concentrated on using overdraw tool, the pitfalls of having too much Overdraw in the UI and the common ways of avoiding the same and layout optimization with the constraint layout. This feature was identified while working on WeGuard and after using this feature significant performance improvement was noticed on all our applications.

--

--