Building Layouts for Android 🤖

Hero image for Building Layouts for Android 🤖

Basic rules

View binding

View binding

Read more about the pros and cons of Android view access strategies here

Kotlin Android Extensions (Synthetics)

Synthetics enables effortlessly retrieving views from Activities, Fragments, and Views. The plugin generates additional code, granting developers access to views in the layout XML as if they were properties named after the corresponding IDs used in the layout definition. Additionally, it constructs a local view cache. For further information, refer to this link.

import kotlinx.android.synthetic.main.activity_main.*

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    // tvWelcomeMessage is the id of the UI component
    tvWelcomeMessage.text = "Hello Kotlin!"
}

View layouts

There are a few commonly used layouts which are the LinearLayout, RelativeLayout, and FrameLayout. Most of what can be done in LinearLayout and RelativeLayout can now, and should, be done with a new layout system called ConstraintLayout.

It is important to understand the class hierarchy of these View Layouts. Each of them subclass ViewGroup, which itself subclasses View. ViewGroup also contains the nested static class LayoutParams which is used for creating or editing layouts in code. Keep in mind that each subclass of ViewGroup, such as LinearLayout, has its own nested static class LayoutParams which is a subclass of ViewGroup.LayoutParams. Learn more

Margin / padding

Utilizing margin and padding values for views facilitate the arrangement and spacing of elements within a layout. For additional insights, visit this link.

  • Margin defines the amount of space around the outside of a view
  • Padding defines the amount of space around the contents or children of a view.
<LinearLayout>
   <TextView android:layout_margin="5dp" android:padding="5dp">
   <Button layout_marginBottom="5dp">
</LinearLayout>

Checking the layout bounds? Go to the Developer options in the device’s settings to enable Show layout bounds.

View binding

Resources naming

Basic principle

Basic principle

  • <WHAT> indicates what the resource actually represents; often a standard Android view class. Limited options per resource type.
  • <WHERE> describes where it logically belongs in the app. Resources used in multiple screens use general, all others use the custom part of the Android view subclass they are in.
  • <DESCRIPTION> differentiates multiple elements in one screen.
  • <SIZE> either a precise size or size bucket; optionally used for drawables and dimensions.

Layouts

Layouts are relatively simple, as there are usually only a few layouts per screen.

Where <WHAT> is one of the following:

  • activity_main: content view of the MainActivity
  • fragment_article_detail: view for the ArticleDetailFragment
  • view_menu: layout inflated by custom view class MenuView
  • item_article: list item in ArticleRecyclerView
  • layout_actionbar_backbutton: layout for an action bar with a back button. That kind of layouts is usually used for <include> and <merge> attributes, and hence are reusable.

Strings

The <WHAT> part for Strings is irrelevant, so use <WHERE> to indicate where the string will be used:

  • article_detail_title: title of ArticleDetailFragment
  • feedback_explanation: feedback explanation in FeedbackFragment
  • feedback_name_hint: hint of name field in FeedbackFragment
  • general_done: generic done string

<WHERE> obviously is the same for all resources in the same view.

Drawables

The <WHAT> part for Drawables is the type such as:

  • ic for icon
  • bg for background
  • fg for foreground
  • placeholder for placeholder

<WHERE> is used to indicate where the drawable will be used:

Optionally add a <SIZE> argument, which can be an actual size 24dp or a size qualifier small.

  • placeholder_article_detail: placeholder in ArticleDetailFragment
  • bg_article_detail_banner: background for the banner in article detail
  • ic_general_info: generic info icon
  • ic_general_info_large: large version of generic info icon
  • ic_general_info_24dp: 24dp version of generic info icon

IDs

For IDs, <WHAT> is the class name of the xml element it belongs to. Next is the screen the ID is in, followed by an optional description to distinguish similar elements in one screen. However, because IDs are often referenced in the code, they follow the CamelCase naming convention.

  • tlMain -> TabLayout in MainActivity
  • ivMenuProfile -> profile image in custom MenuView
  • tvArticleDetailTitle -> title TextView in ArticleDetailFragment

Dimensions

Apps should only define a limited set of dimensions, which are constantly reused. This makes most dimensions general by default.

Note that this list only contains the most used <WHAT>s. Other dimensions qualifiers like: rotation, scale, etc. are usually only used in drawables and as such less reused.

  • toolbar_height: height of all toolbars
  • listitem_keyline_text: listitem text is aligned at this keyline
  • text_medium: medium size of all text
  • menu_icon_size: size of icons in menu
  • menu_profile_image_height: height of profile image in menu

XML Attribute Ordering

Where appropriate, XML attributes should appear in the following order:

* `style` attribute (if there is)
* `id` attribute
* `layout_*` attributes
* style attributes such as `gravity` or `textColor`
* value attributes such as `text` or `src`

Within each of these groups, the attributes should be ordered alphabetically.

Best practices

Drawable or Bitmap?

  • Bitmap actually refers to the images like webp, png, jpg, etc.

  • Drawable is something that can be drawn like a layout, vector, image, etc.

    To ensure bitmap images are fully compressed from the start, convert images to WebP files. Make sure to set the encoding quality to 100% to minimize image sizes while maintaining the quality.

    To read an image as a bitstream in order to convert it to a bitmap, put the image in the res/raw/ folder to ensure it will not be optimized by the aapt tool during compile time.

  • Use shapes and selectors instead of images as much as possible.

    Make the project lightweight by drawing basic shapes with XML instead of using images. Almost all basic shapes can be drawn with Shape Drawables. The best part about vectors is that they render neatly in various display densities. This further reduces APK size.

    Basic shapes and gradients can easily be drawn using the <shape /> tag without the use of images. The resulting shapes that are drawn are always sharp and do not need to be created for multiple densities.

    The <selector /> tag can be used to add different visual states (like pressed, disabled, checked) to Views.

    For small icons that need to be shown in different colors, use the property android:tint=”@color/primary_color” on the same image and avoid duplicating images in all quality.

  • Apply Nine-Patch Image for Stretchable backgrounds

    To use an image as background for content where the length of the content is dynamic (e.g., Chat Bubble, Custom Pop Up), create a Nine-Patch image to define the stretchable region inside it. The Nine-Patch can be created with any photo editing tool or the SDK.

Layouts

  • Layout XMLs are code, organise them well

  • Reusable XML

    Write XML Layouts as reusable layouts, which will avoid duplicating the same layout with different names. Use the <include>, <fragment> and <merge> tags for achieving this.

    There are sometimes cases where complex views are rarely used. For example, it could be a view only visible depending on a specific condition. In such a case, use a ViewStub element. It is a lightweight view with no dimensions and cheap to inflate and leave in a view hierarchy.

    <ViewStub
        android:id="@+id/stub_import"
        android:inflatedId="@+id/panel_import"
        android:layout="@layout/progress_overlay"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom" />
    
  • Avoid Weight

    Playing around with LinearLayout and weight is pretty easy, but it makes the layout expensive performance-wise. Avoid using weight is recommended.

    For example, dividing two components equally in a screen can achieve without the use of weight. To accomplish that, place the guideline first in the center by applying the proper constraints on a ConstraintLayout as shown in the figure, and then insert another two buttons on the left and right, respectively by using the proper constraints.

  • Avoid Nested Layouts

    Nesting layouts to deeper level decreases the layout performance. Avoid nested layouts and nested weights.

    Many developers use nested structure with LinearLayout for setting the visibility dynamically. In LinearLayout, the components are arranged in a linear way, so hiding one component will not affect alignment of the rest of the components.

  • Improving and designing high-performance layouts

    The ViewGroup that is used in the layout matters the performance; if the layout hierarchy is too complex, it can cause performance problems when running an Android app. For more insight, visit this link.

    Whenever possible, always favour the ConstraintLayout. As opposed to the other container layouts, the ConstraintLayout offers much-improved performance while still offering similar layout control. Prefer layouts with shallow and wide hierarchy, rather than narrow and deep hierarchy.

    Optimize the layout by using the powerful tool that comes with SDK. Hierarchy Viewer allows to inspect the layout component that helps to debug the layout problems and can increase the layout performance by calculating the inflating time.

  • Support for screens with different form factors

    Since a wide range of devices is running different OS versions, screen sizes, and densities, consider supporting as much as types of devices.

    Learn more about how to build a Responsive UI in Android here

Styling

  • Separate layout and styling elements

    Separate the layout and its styling elements, such as dimens, strings, and colors, to maintain flexibility in the layout to work seamlessly on different screen sizes. Additionally, specify a new set of dimens for tablets by applying qualifiers.

  • Use styles to avoid duplicate attributes in layout XMLs

  • Use multiple style files to avoid a single huge one

  • Keep the colors.xml short and DRY, just define the palette

  • Also keep dimens.xml DRY, define generic constants

    When the only thing that changes within a color’s definition is the alpha value, the naming convention is then to append that value at the end of the color’s name, e.g.

        <color name="black_26">#42000000</color>
        <color name="black_40">#66000000</color>
        <color name="black_73">#BA000000</color>
    

    Cheat sheet: https://gist.github.com/lopspower/03fb1cc0ac9f32ef38f4

    When adding a new color resource, check the existing colors if it’s available already or not to improve reusability and reduce duplication. So, whenever two different color codes look similar to each other, there might be a chance that they’re actually one and the same. To confirm this, make use of: https://www.htmlcsscolor.com/, e.g.

        <color name="black_nero">#1A1A1A</color>
        <color name="green_lime">#3AC538</color>
    

Others

  • Accessing Resources: Load app resources in code (via generated R class) and in XML (via @color, @drawable, @string, etc). Do NOT use global extensions or variables to repeat defining app resources for re-using.

    val Context.mediumPadding: Int
        get() = resources.getDimensionPixelSize(R.dimen.medium_padding)
    
    class SomeClass {
    
      private fun sampleMethod() {
        view.setPadding(context.mediumPadding, 0, 0, 0)
      }
    }
    
    class SomeClass {
    
      private val mediumPadding by lazy { resources.getDimensionPixelSize(R.dimen.medium_padding) }
    
      private fun sampleMethodA() {
        val largePadding = context.resources.getDimensionPixelSize(R.dimen.large_padding)
        viewA.setPadding(mediumPadding, largePadding, 0, 0)
      }
    
      private fun sampleMethodB() {
        viewB.setPadding(mediumPadding, 0, 0, 0)
      }
    }
    
  • Run the lint tool on layout files to search for possible view hierarchy optimisations.

  • Reuse code in different projects by generic naming

  • Third-party libraries

    There are many third-party libraries for Android to build elegant layouts. Several of them are must have libraries that are extremely popular and are often used in almost any Android project. Each has different purposes but all of them make life as a developer much more pleasant. The major libraries are listed below in a few categories. Learn more

    Paris allows defining and applying styles programmatically to Android views, including custom attributes.