Support different screen sizes

Android devices come in all shapes and sizes, so your app's layout needs to be flexible. That is, instead of defining your layout with rigid dimensions that assume a certain screen size and aspect ratio, your layout should gracefully respond to different screen sizes and orientations.

By supporting as many screens as possible, your app can be made available to the greatest number of users with different devices, using a single APK. Additionally, making your app flexible for different screen sizes ensures that your app can handle window configuration changes on the device, such as when the user enables multi-window mode. The same principle applies when your app is running on a foldable device where the screen size and aspect ratio may change while your app is running.

This page shows you how to support different screen sizes with the following techniques:

  • Use view dimensions that allow the layout to resize
  • Create alternative UI layouts according to the screen configuration
  • Provide bitmaps that can stretch with the views

But be aware that adapting to different screen sizes doesn't necessarily make your app compatible with all Android form factors. You should take additional steps to support Android Wear, TV, Auto, and Chrome OS devices.

For design guidelines for building UI for different screens, see the material design guidelines for responsive layout.

Create a flexible layout

No matter what hardware profile you want to support first, you need to create a layout that is responsive to even small variations in screen size.

Use ConstraintLayout

The best way to create a responsive layout for different screen sizes is to use ConstraintLayout as the base layout in your UI. ConstraintLayout allows you to specify the position and size for each view according to spatial relationships with other views in the layout. This way, all the views can move and stretch together as the screen size changes.

The easiest way to build a layout with ConstraintLayout is to use the Layout Editor in Android Studio. It allows you to drag new views to the layout, attach their constraints to the parent view and other sibling views, and edit the view's properties, all without editing any XML by hand (see figure 1).

For more information, see Build a Responsive UI With ConstraintLayout.

Figure 1. The Layout Editor in Android Studio showing a ConstraintLayout file

But ConstraintLayout won't solve every layout scenario (especially for dynamically-loaded lists, for which you should use RecyclerView), but no matter what layout you use, you should always avoid hard-coding layout sizes (see the next section).

Avoid hard-coded layout sizes

To ensure that your layout is flexible and adapts to different screen sizes, you should use "wrap_content" or "match_parent" for the width and height of most view components, instead of hard-coded sizes.

"wrap_content" tells the view to set its size to whatever is necessary to fit the content within that view.

"match_parent" makes the view expand to as much as possible within the parent view.

For example:

<TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/lorem_ipsum" />

Although the actual layout for this view depends on other attributes in its parent view and any sibling views, this TextView intends to set its width to fill all available space (match_parent) and set its height to exactly as much space is required by the height of the text (wrap_content). This allows the view to adapt to different screen sizes and different lengths of text.

Figure 2 shows how the width of the text view using "match_parent" adjusts as the screen width changes with device orientation.

Figure 2. A flexible text view

If you're using a LinearLayout, you can also expand the child views with layout weight so that each view fills the remaining space proportional to their weight value. However, using weights in a nested LinearLayout requires the system to perform multiple layout passes to determine the size for each view, slowing your UI performance. Fortunately, ConstraintLayout can achieve nearly all layouts possible with LinearLayout without the performance impacts, so you should try converting your layout to ConstraintLayout. Then, you can define weighted layouts with constraint chains.

Use SlidingPaneLayout for list/detail UIs

A list/detail UI may need to behave differently for different screen sizes. When running on a large display, there is plenty of space to have the list and detail panes side-by-side. Clicking on an item in the list displays its details in the detail pane. However, on smaller screens, this can become too crowded. Instead of displaying both panes, it can be better to display them one at a time. Initially, the list pane is shown filling the window. When the user taps an item the list pane is replaced by the detail pane for that item, which also fills the window.

You can use SlidingPaneLayout to manage the logic for determining which of these two user experiences is appropriate for the current window size:

<?xml version="1.0" encoding="utf-8"?>
<androidx.slidingpanelayout.widget.SlidingPaneLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="280dp"
        android:layout_height="match_parent"
        android:layout_gravity="start" />

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="300dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        app:defaultNavHost="true"
        app:navGraph="@navigation/item_navigation" />

</androidx.slidingpanelayout.widget.SlidingPaneLayout>

The widths and weight here are the key factors that determine the behavior. If the window is large enough to display both views (at least 580dp) then the side- by-side UI is used. However, if it is smaller than that, then the full-screen list replaced by full-screen detail UI is used instead.

When in side-by-side mode, the window may be larger than the minimum requirement of 580dp in this example. Weight values will be used to size the two panes proportionally. In the example, the list pane will always be 280dp and the detail pane will fill the remaining space. The one exception to this is when using SlidingPaneLayout V1.2.0-alpha01 and later on foldable devices. In these cases SlidingPaneLayout will automatically adjust the size of the panes so that they are on either side of any fold or hinge.

Create alternative layouts

Although your layout should always respond to different screen sizes by stretching the space within and around its views, that might not provide the best user experience for every screen size. For example, the UI you designed for a phone, probably doesn't offer a good experience on a tablet. Therefore, your app should also provide alternative layout resources to optimize the UI design for certain screen sizes.

Figure 3. The same app on different screen sizes uses a different layout for each

You can provide screen-specific layouts by creating additional res/layout/ directories—one for each screen configuration that requires a different layout—and then append a screen configuration qualifier to the layout directory name (such as layout-w600dp for screens that have 600dp of available width).

These configuration qualifiers represent the visible screen space available for your app UI. The system takes into account any system decorations (such as the navigation bar) and window configuration changes (such as when the user enables multi-window mode) when selecting the layout from your app.

To create an alternative layout in Android Studio (using version 3.0 or higher), proceed as follows:

  1. Open your default layout and then click Orientation for Preview in the toolbar.
  2. In the drop-down list, click to create a suggested variant such as Create Landscape Variant or click Create Other.
  3. If you selected Create Other, the Select Resource Directory appears. Here, select a screen qualifier on the left and add it to the list of Chosen qualifiers. When you're done adding qualifiers, click OK. (See the following sections for information about screen size qualifiers.)

This creates a duplicate layout file in the appropriate layout directory so you can begin customizing the layout for that screen variant.

Use the smallest width qualifier

The "smallest width" screen size qualifier allows you to provide alternative layouts for screens that have a minimum width measured in density-independent pixels(dp or dip).

By describing the screen size as a measure of density-independent pixels, Android allows you to create layouts that are designed for very specific screen dimensions while avoiding any concerns you might have about different pixel densities.

For example, you can create a layout named main_activity that's optimized for handsets and tablets by creating different versions of the file in directories as follows:

res/layout/main_activity.xml           # For handsets (smaller than 600dp available width)
res/layout-sw600dp/main_activity.xml   # For 7” tablets (600dp wide and bigger)

The smallest width qualifier specifies the smallest of the screen's two sides, regardless of the device's current orientation, so it's a simple way to specify the overall screen size available for your layout.

Here's how other smallest width values correspond to typical screen sizes:

  • 320dp: a typical phone screen (240x320 ldpi, 320x480 mdpi, 480x800 hdpi, etc).
  • 480dp: a large phone screen ~5" (480x800 mdpi).
  • 600dp: a 7” tablet (600x1024 mdpi).
  • 720dp: a 10” tablet (720x1280 mdpi, 800x1280 mdpi, etc).

Figure 4 provides a more detailed view of how different screen dp widths generally correspond to different screen sizes and orientations.

Figure 4. Recommended width breakpoints to support different screen sizes

Remember that all the figures for the smallest width qualifier are density-independent pixels, because what matters is the amount of screen space available after the system accounts for pixel density (not the raw pixel resolution).

Use the available width qualifier

Instead of changing the layout based on the smallest width of the screen, you might want to change your layout based on how much width or height is currently available. For example, if you have a two-pane layout, you might want to use that whenever the screen provides at least 600dp of width, which might change depending on whether the device is in landscape or portrait orientation. In this case, you should use the "available width" qualifier as follows:

res/layout/main_activity.xml         # For handsets (smaller than 600dp available width)
res/layout-w600dp/main_activity.xml  # For 7” tablets or any screen with 600dp
                                     #   available width (possibly landscape handsets)

If the available height is a concern for you, then you can do the same using the "available height" qualifier. For example, layout-h600dp for screens with at least 600dp of screen height.

Add orientation qualifiers

Although you may be able to support all size variations using only combinations of the "smallest width" and "available width" qualifiers, you might also want to change the user experience when the user switches between portrait and landscape orientations.

For that you can add the port or land qualifiers to your resource directory names. Just be sure these come after the other size qualifiers. For example:

res/layout/main_activity.xml                # For handsets
res/layout-land/main_activity.xml           # For handsets in landscape
res/layout-sw600dp/main_activity.xml        # For 7” tablets
res/layout-sw600dp-land/main_activity.xml   # For 7” tablets in landscape

For more information about all the screen configuration qualifiers, see table 2 in the guide to Providing Resources.

Modularize UI components with fragments

When designing your app for multiple screen sizes you want to make sure you aren't needlessly duplicating your UI behavior across your activities. So you should use fragments to extract your UI logic into separate components. Then, you can then combine fragments to create multi-pane layouts when running on a large screen or place in separate activities when running on a handset.

For example, a news app on a tablet might show a list of articles on the left side and a full article on the right side—selecting an article on the left updates the article view on the right. On a handset, however, these two components should appear on separate screens—selecting an article from a list changes the entire screen to show that article.

To learn more, see Building a Dynamic UI with Fragments.

Support Android 3.1 with legacy size qualifiers

If your app supports Android 3.1 (API level 12) or lower, you need to use the legacy size qualifiers in addition to the smallest/available width qualifiers from above.

From the example above, if you want a two pane layout on larger devices you need to use the "large" configuration qualifier to support version 3.1 and lower. So, to implement these layouts on those older versions, you might have the following files:

res/layout/main_activity.xml           # For handsets (smaller than 640dp x 480dp)
res/layout-large/main_activity.xml     # For small tablets (640dp x 480dp and bigger)
res/layout-xlarge/main_activity.xml    # For large tablets (960dp x 720dp and bigger)

Use layout aliases

When supporting both pre- and post-3.2 Android versions you have to use both the smallest-width and large qualifiers for your layouts. So, you would have a file named res/layout-large/main.xml which might be identical to res/layout-sw600dp/main.xml.

To avoid this duplication of the same file, you can use alias files. For example, you can define the following layouts:

res/layout/main.xml            # single-pane layout
res/layout/main_twopanes.xml   # two-pane layout

And add these two files:

  • res/values-large/layout.xml:
    <resources>
        <item name="main" type="layout">@layout/main_twopanes</item>
    </resources>
    
  • res/values-sw600dp/layout.xml:
    <resources>
        <item name="main" type="layout">@layout/main_twopanes</item>
    </resources>
    

These two files have identical content, but they don’t actually define the layout. They merely set up main to be an alias to main_twopanes. Since these files have large and sw600dp selectors, they are applied to large screens regardless of Android version (pre-3.2 tablets and TVs match large, and post-3.2 will match sw600dp).

Jetpack Compose

The principles shown so far also apply when using Jetpack Compose, although the application of them may be a little different.

Create a flexible layout

You can easily achieve flexible layouts in Jetpack Compose by using weights in much the same way that weights are used for traditional views. For more complex layouts, you can also use ConstraintLayout in Compose.

Create alternative layouts

Jetpack Compose doesn't use resources to represent layouts, so you can't use resource qualifiers to provide alternate layouts. Instead, you can build this logic into your Composables. A LocalConfiguration instance provides information about the current device configuration. This is broadly in line with the qualifiers used to specify alternate resources in traditional View-based UIs. For example, the screen orientation, size, whether the keyboard is visible, and so on. can be determined from the following sample:

@Composable
fun DynamicLayout() {
    val configuration = LocalConfiguration.current
    if (configuration.smallestScreenWidthDp < 580 ) {
         SmallLayout()
     } else {
         LargeLayout()
     }
}

If you handle configuration changes yourself then this composable is recomposed when the configuration changes. Otherwise your activity is recreated as usual on configuration changes causing a new composition to be created.

Sometimes components might depend on the size allocated to them, rather than the entire screen. In this case, use BoxWithConstraints. This defers composition until the after the measurement phase and the allocated size and other constraints are known. These constraints are available in the scope of the lambda:

@Composable
fun AdaptiveLayout() {
    BoxWithConstraints {
        if (maxWidth < 580.dp) {
            SmallLayout()
        } else {
            LargeLayout()
        }
    }
}

In this example, different layouts are emitted depending on whether the maximum width of the AdaptiveLayout composable is greater than or less than 580dp.

This area is one that is still under active development and additional APIs might be added in future Compose releases.

Create stretchable nine-patch bitmaps

If you use a bitmap as the background in a view that changes size, you will notice Android scales your images as the view grows or shrinks based on the size of the screen or content in the view. This often leads to visible blurring or other scaling artifacts. The solution is using nine-patch bitmaps, which are specially formatted PNG files that indicate which areas can and cannot be stretched.

A nine-patch bitmap is basically a standard PNG file, but with an extra 1px border that indicates which pixels should be stretched (and with a .9.png extension instead of just .png). As shown in figure 5, the intersection between the black lines on the left and top edge is the area of the bitmap that can be stretched.

Optionally, you can also define the safe region where content should go inside the view by similarly adding lines on the right and bottom edges.

Figure 5. A nine-patch image (button.9.png)

When you apply a nine-patch as the background to a view, the framework stretches the image correctly to accommodate the size of the button.

For help creating a nine-patch image from a bitmap, see Create Resizable Bitmaps.

Test on all screen sizes

It's important to test your app on a variety of screen sizes so you can ensure your UI scales correctly. If you don't have access to physical devices for all the different screen sizes, you can use the Android Emulator to emulate any screen size.

If you would rather test on a physical device, but don't want to buy the devices, you can use Firebase Test Lab to access devices in a Google data center.

Declare specific screen size support

If you decide that you don't want your app to run at certain screen sizes, you can set limits for how much your screen should resize or even restrict which devices can install your app based on their screen configuration. For more information, see Declare Restricted Screen Support.