With years of experience in project work, our Android team has identified the most common pitfalls developers tend to fall into when implementing the user interface in Android apps.
Every Android developer will know the important role UI implementation plays in the development process. One could even argue that it is the most integral part of building a new app.
The UI allows users to interact with the features, functions, and contents of the application. When designed properly, it is user-centered, user-friendly, consistent, performant, accessible, and, most importantly, solves the problem that the app addresses. At any rate, you don’t want your app to look like it was built in 1990.
This article will point out the usual suspects that can mess up your UI implementation in Android apps – the most common mistakes developers make.
Before we start, we have to put one disclaimer in place. The article mostly focuses on building UI with XML, the old way. Jetpack Compose tends to bring forward its own set of mistakes, which is a story for another day.
Five common mistakes Android developers make and how to avoid them
The mistakes we discuss here represent the most common pitfalls Android developers will run into. We’ve learned to recognize them through years of experience working on various projects within Infinum’s Android team – from legacy ones to those boasting the latest technologies, modern architecture, and best practices.
Mistake #1: Duplicating layout code
Very often, we see developers fail to reuse their layouts. This can easily lead to:
Inconsistency
If several screens require similar layouts, and you create each one from scratch, you’ll easily end up with slight inconsistencies between them.
Maintenance issues
If you have multiple layouts that are similar but not identical, and there is a change to be made, you will need to modify each one individually. This is both time-consuming and increases your chance of error.
How to avoid this:
A good principle that most programmers follow is DRY – don’t repeat yourself. In practice, we apply this by extracting duplicated code into functions or classes and reusing it like this.
But how to achieve the same thing in XML? One way of reusing an XML layout is to use the <include>
tag. First, you need to create the layout that you want to reuse. For example, let’s create one called toolbar.xml
. When you need to reuse it within another layout, you can do it in the following way:
<include layout=”@layout/toolbar”/>
Another way you can avoid duplicating layout code is by using compound views, an aspect of Android UI design that often goes overlooked. Many developers fail to take advantage of these powerful features, which can lead to issues in their app’s UI design.
Compound views allow you to create UI components with custom behavior and appearance. If you don’t use them in your app, your UI may lack modularity, and maintenance and updating can become more challenging as the app grows in size and complexity.
However, there are situations where reusing layouts is not necessarily the best approach. Sometimes, maintenance-wise, duplicated layouts are a better choice. In such cases, it may be a better idea to granulate the UI into smaller reusable bits.
Let’s say you are trying to reuse one layout in a number of situations, but with each case you need to add more configuration parameters to it. This only makes your layout more complex to maintain and understand. In this case, it may be better to duplicate the layout and make the necessary changes instead of reusing and modifying it by passing a bunch of parameters to it. Maintenance also becomes easier later down the line.
Mistake #2: Still using findViewById or Kotlin synthetics
Another common mistake we find is Android developers still using findViewById
or Kotlin synthetics to refer to the elements declared in XML. It’s worth mentioning that Kotlin synthetics were removed in Kotlin 1.8. There are several problems this mistake can lead to:
Performance issues
Both findViewById
and Kotlin synthetics traverse view hierarchy at runtime to find a specified view, and this can lead to slow performance and increased memory usage.
Null Pointer Exceptions
If a view is not found using one of those two approaches, it will return null, which can cause NPE in runtime.
No type safety
The method findViewById
doesn’t provide type safety. This means that you will need to cast types manually, which can lead to ClassCastException
at runtime.
How to avoid this:
Our suggestion is to use View Binding. This is a newer approach for accessing UI elements. It generates a binding class for each XML layout and provides references to all views in the layout. It also offers both null and type safety.
Mistake #3: ConstraintLayout
ConstraintLayout
is one of the most powerful layout managers in Android. It was introduced back in 2017 and went on to become one of the most used layouts of all time. ConstraintLayout allows developers to create complex layouts with a flatter hierarchy.
Failing to use ConstraintLayout can quickly lead to deep hierarchies, which make layouts hard to understand and cause performance issues.
However, there is also an issue on the other end of the spectrum – developers sometimes get so used to ConstraintLayout that they try to use it everywhere. Using it for a simple layout that can easily be done with a single LinearLayout
or FrameLayout
can lead to potential issues. For example:
Longer build times
Using ConstraintLayout increases your app build time. This is especially noticeable when you have multiple simple layouts all using it.
Reduced performance
ConstraintLayout is a complex view group, and it can take longer to measure and layout elements.
MotionLayout
is another powerful view group that is built upon the features of ConstraintLayout and adds support for animations and transitions between layouts. You should still use it with caution – while it is a great tool, it also adds to the complexity of the layout, and potentially increases build times and reduces performance.
Mistake #4: Supporting various screen sizes
One challenge that you are very likely to face during development is supporting the wide variety of screen sizes available on Android devices.
Let’s take image sizes, for example. The lazy solution is to use one small image for all devices, which can make images blurry on high-res devices.
On the other hand, using one large image can cause crashes on older low-end devices that cannot fit the resources into their memory and spend processor time on downscaling them. This issue often goes overlooked since apps are mostly tested on a couple of newer devices. However, as soon as your app is released, it is installed on various devices, from low-end to high-end, using Android versions from 8 to 13.
How to avoid this:
The solution is to either use vector graphics or add multiple versions of each image adapted to different pixel densities.
Additional tips:
If you need to support tablets as well, it might be a good idea to use different dimensions for different screen sizes. For example, by default, the content of the screen would stretch from left to right with a 16dp margin to the left and right. This won’t look good on a tablet, so a bigger margin would help. If you cannot adapt the tablet layout using dimensions only, it might also be a good idea to make two completely separate layouts – one for mobile and one for tablet.
Another tip for easily supporting more screen sizes is to make all your screens optionally scrollable. If the screen is big enough to display the whole content, it won’t be scrollable, but if the screen is smaller, then it can be scrolled to see all the content available.
Finally, it is important to know your target audience. Analytical data on your user base will tell you what devices they mostly own, so you can optimize your layouts accordingly.
Mistake #5: Not using themes and/or styles
The issue that probably every Android developer met at least once is related to handling theming and styles. Using hardcoded values instead of styles or referencing themes can lead to inconsistency between screens, and your app might end up looking unprofessional.
The biggest advantage of using themes is long-term maintenance. For example, if you wanted to change your primary color but didn’t use themes, you would have to change each component that contained it, one by one. When components reference the color from the theme, you only need to change one line of code.
Another great advantage of using a theme is that you can change the visual feel of your app completely with minimal effort. For example, when creating a dark theme, you can just copy your current one, change the colors, and that’s it. Also, with a proper theme setup, creating new screens usually takes less time.
How else to avoid this:
Our additional recommendation is to use theme attributes. This is very useful when you want to define styles that can be applied to multiple themes; there’s no need to create a separate style for each theme. In other words, using attributes makes your styles theme-independent.
When you know what to expect, you’ll know what to avoid
Building and designing great user interfaces can be a challenging task, but it is a crucial step in creating successful, widely used Android apps. Through our years of experience and work on various projects, we’ve identified several common mistakes Android developers tend to make when implementing the UI.
By avoiding the mistakes described in this post, you can create apps that are more intuitive, user-friendly, and offer a better user experience. Not less importantly, they can save you a lot of time and headaches during development.