Support different screen sizes

Support for different screen sizes enables the greatest number of users and widest variety of devices to access your app.

To support as many screen sizes as possible, design your app layouts to be responsive and adaptive. Responsive/adaptive layouts provide an optimized user experience regardless of screen size, enabling your app to accommodate phones, tablets, foldable and ChromeOS devices, portrait and landscape orientations, and resizable configurations such as multi-window mode.

Window size classes

Window size classes are a set of opinionated viewport breakpoints that help you design, develop, and test responsive and adaptive application layouts. The breakpoints have been chosen specifically to balance layout simplicity with the flexibility to optimize your app for unique cases.

Window size classes categorize the display area available to your app as compact, medium, or expanded. Available width and height are classified separately, so at any point in time, your app has two window size classes—one for width, one for height. Available width is usually more important than available height due to the ubiquity of vertical scrolling, so the width window size class will likely be more relevant to your app’s UI.

Figure 1. Representations of width-based window size classes.
Figure 2. Representations of height-based window size classes.

As visualized above, the breakpoints allow you to continue thinking about layouts in terms of devices and configurations. Each size class breakpoint represents a majority case for typical device scenarios, which can be a helpful frame of reference as you think about the design of your breakpoint-based layouts.

Size class Breakpoint Device representation
Compact width width < 600dp 99.96% of phones in portrait
Medium width 600dp ≤ width < 840dp 93.73% of tablets in portrait,

Large unfolded inner displays in portrait

Expanded width width ≥ 840dp 97.22% of tablets in landscape,

Large unfolded inner displays in landscape

Compact height height < 480dp 99.78% of phones in landscape
Medium height 480dp ≤ height < 900dp 96.56% of tablets in landscape,

97.59% of phones in portrait

Expanded height height ≥ 900dp 94.25% of tablets in portrait

Although it can be useful to visualize size classes as physical devices, window size classes are explicitly not determined by the size of the device screen. Window size classes are not intended for isTablet-type logic. Rather, window size classes are determined by the window size available to your application regardless of the type of device the app is running on, which has two important consequences:

  • Physical devices do not guarantee a specific window size class. The screen space available to your app can differ from the screen size of the device for many reasons. On mobile devices, split‑screen mode can partition the screen between two applications. On ChromeOS, Android apps can be presented in free‑form windows that are arbitrarily resizable. Foldables can have two different-sized screens individually accessed by folding or unfolding the device.

  • The window size class can change throughout the lifetime of your app. While your app is running, device orientation changes, multitasking, and folding/unfolding can change the amount of screen space available. As a result, the window size class is dynamic, and your app’s UI should adapt accordingly.

Window size classes map to the layout breakpoints in the Material Design responsive layout grid. Use window size classes to make high-level application layout decisions, such as deciding whether to use a specific canonical layout to take advantage of additional screen space.

View-based apps should calculate the window size class based on the current window metrics provided by the Jetpack WindowManager library. The example code below in Views Kotlin and Views Java shows an example of how to calculate the window size class based on the breakpoints and receive updates whenever it changes.

Compose-based apps should use the material3-window-size-class library to calculate a WindowSizeClass based on the current window metrics with calculateWindowSizeClass().

Views

enum class WindowSizeClass { COMPACT, MEDIUM, EXPANDED }

class MainActivity : Activity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // ...

        // Replace with a known container that you can safely add a
        // view to where the view won't affect the layout and the view
        // won't be replaced.
        val container: ViewGroup = binding.container

        // Add a utility view to the container to hook into
        // View.onConfigurationChanged. This is required for all
        // activities, even those that don't handle configuration
        // changes. You can't use Activity.onConfigurationChanged,
        // since there are situations where that won't be called when
        // the configuration changes. View.onConfigurationChanged is
        // called in those scenarios.
        container.addView(object : View(this) {
            override fun onConfigurationChanged(newConfig: Configuration?) {
                super.onConfigurationChanged(newConfig)
                computeWindowSizeClasses()
            }
        })

        computeWindowSizeClasses()
    }

    private fun computeWindowSizeClasses() {
        val metrics = WindowMetricsCalculator.getOrCreate()
            .computeCurrentWindowMetrics(this)

        val widthDp = metrics.bounds.width() /
            resources.displayMetrics.density
        val widthWindowSizeClass = when {
            widthDp < 600f -> WindowSizeClass.COMPACT
            widthDp < 840f -> WindowSizeClass.MEDIUM
            else -> WindowSizeClass.EXPANDED
        }

        val heightDp = metrics.bounds.height() /
            resources.displayMetrics.density
        val heightWindowSizeClass = when {
            heightDp < 480f -> WindowSizeClass.COMPACT
            heightDp < 900f -> WindowSizeClass.MEDIUM
            else -> WindowSizeClass.EXPANDED
        }

        // Use widthWindowSizeClass and heightWindowSizeClass.
    }
}

Views

public enum WindowSizeClass { COMPACT, MEDIUM, EXPANDED }

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // ...

        // Replace with a known container that you can safely add a
        // view to where the view won't affect the layout and the view
        // won't be replaced.
        ViewGroup container = binding.container;

        // Add a utility view to the container to hook into
        // View.onConfigurationChanged. This is required for all
        // activities, even those that don't handle configuration
        // changes. You can't use Activity.onConfigurationChanged,
        // since there are situations where that won't be called when
        // the configuration changes. View.onConfigurationChanged is
        // called in those scenarios.
        container.addView(new View(this) {
            @Override
            protected void onConfigurationChanged(Configuration newConfig) {
                super.onConfigurationChanged(newConfig);
                computeWindowSizeClasses();
            }
        });

        computeWindowSizeClasses();
    }

    private void computeWindowSizeClasses() {
        WindowMetrics metrics = WindowMetricsCalculator.getOrCreate()
                .computeCurrentWindowMetrics(this);

        float widthDp = metrics.getBounds().width() /
                getResources().getDisplayMetrics().density;
        WindowSizeClass widthWindowSizeClass;

        if (widthDp < 600f) {
            widthWindowSizeClass = WindowSizeClass.COMPACT;
        } else if (widthDp < 840f) {
            widthWindowSizeClass = WindowSizeClass.MEDIUM;
        } else {
            widthWindowSizeClass = WindowSizeClass.EXPANDED;
        }

        float heightDp = metrics.getBounds().height() /
                getResources().getDisplayMetrics().density;
        WindowSizeClass heightWindowSizeClass;

        if (heightDp < 480f) {
            heightWindowSizeClass = WindowSizeClass.COMPACT;
        } else if (heightDp < 900f) {
            heightWindowSizeClass = WindowSizeClass.MEDIUM;
        } else {
            heightWindowSizeClass = WindowSizeClass.EXPANDED;
        }

        // Use widthWindowSizeClass and heightWindowSizeClass.
    }
}

Compose

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            val windowSizeClass = calculateWindowSizeClass(this)
            MyApp(windowSizeClass)
        }
    }
}

Once you're observing window size classes in your app, you're ready to start changing your layout based on the current window size class.

To learn how to use window size classes to make layouts responsive, see the following:

Checklist for supporting different window size classes

As you make layout changes, test the layout behavior across all ranges of window sizes, especially at the compact, medium, and expanded breakpoint widths.

If you have an existing layout for compact screens, first optimize your layout for the expanded width size class, as this provides the most space for additional content and UI changes. Then decide what layout makes sense for the medium width size class. Consider adding a specialized layout for the medium width screen size.

Provide an enhanced user experience by adding functionality that applies specifically to your app, such as support for foldable postures or optimizations for keyboard, mouse, and stylus input support.

To learn more about what makes an app great across all devices and screen sizes, see Large screen app quality.

Responsive design

The first step in supporting a variety of device form factors is to create a layout that is responsive to variations in screen size.

ConstraintLayout

The best way to create a responsive layout is to use ConstraintLayout as the base layout in your UI. ConstraintLayout enables you to specify the position and size of each view according to spatial relationships with other views in the layout. All the views can then move and resize together as the screen size changes.

The easiest way to build a layout with ConstraintLayout is to use the Layout Editor in Android Studio. Layout Editor enables you to drag new views to the layout, apply constraints relative to parent and sibling views, and set view properties—all without editing any XML by hand.

Figure 3. The Layout Editor in Android Studio showing a ConstraintLayout.

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

Responsive width and height

To ensure that your layout is responsive to different screen sizes, use wrap_content, match_parent, or 0dp (match constraint) for the width and height of most view components instead of hard-coded values:

  • wrap_content — Enables the view to set its size to whatever is necessary to fit the content contained in the view.
  • match_parent — Enables the view to expand as much as possible within the parent view.
  • 0dp (match constraint) — In a ConstraintLayout, similar to match_parent. Enables the view to take all the available space within the view's constraints.

For example:

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

Figure 4 shows how the width and height of the TextView adjust as the screen width changes with device orientation.

Figure 4. A responsive TextView.

The TextView sets 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 contained text (wrap_content), which enables the view to adapt to different screen sizes and different amounts of text.

If you're using a LinearLayout, you can also expand the child views based on layout weight so that the views proportionally fill the available space. However, using weights in a nested LinearLayout requires the system to perform multiple layout passes to determine the size for each view, slowing UI performance.

ConstraintLayout can create nearly all of the layouts possible with LinearLayout without the performance impact, so you should try converting your nested LinearLayout to ConstraintLayout. Then you can define weighted layouts with constraint chains.

Adaptive design

Your app's layout should always be responsive to different screen sizes. However, even a responsive layout can't provide the best user experience on every device. For example, the UI you designed for a phone, probably doesn't provide an optimal experience on a tablet. Adaptive design provides alternative layouts optimized for different display dimensions.

SlidingPaneLayout for list-detail UIs

A list-detail UI typically provides a different user experience on different-size screens. On large screens, the list and detail panes are usually side by side. When an item in the list is selected, item information is displayed in the detail pane without changing the UI—the two panes remain side by side. However, on small screens, the two panes are displayed separately, each pane occupying the entire display area. When an item in the list pane is selected, the detail pane (containing the selected item's information) replaces the list pane. Back navigation replaces the detail pane with the list.

SlidingPaneLayout manages the logic for determining which of the 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 layout_width and layout_weight attributes of the two views contained in SlidingPaneLayout determine the SlidingPaneLayout behavior. In the example, if the window is large enough (at least 580dp wide) to display both views, the panes are displayed side by side. But, if the window width is smaller than 580dp, the panes slide over one another to individually occupy the entire app window.

If the window width is larger than the total specified minimum width (580dp), layout_weight values can be used to size the two panes proportionally. In the example, the list pane is always 280dp wide because it does not have a weight. The detail pane however, always fills any horizontal space beyond 580dp because of the view's layout_weight.

Alternative layout resources

To adapt your UI design to widely varying screen sizes, use alternative layouts identified by resource qualifiers.

Figure 5. The same app on different screen sizes using a different layout for each.

You can provide adaptive, screen-specific layouts by creating additional res/layout/ directories in your app's source code. Create a directory for each screen configuration that requires a different layout. Then append a screen configuration qualifier to the layout directory name (for example, layout-w600dp for screens that have 600dp of available width).

The 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 multi-window mode) when selecting the layout for your app.

To create an alternative layout in Android Studio (using version 3.0 or higher), do the following:

  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 Variation, or click Create Other.
  3. If you selected Create Other, the Select Resource Directory appears. 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.)

A duplicate of your default layout file is created in the new layout directory so you can begin customizing the layout for that screen variant.

Smallest width qualifier

The smallest width screen size qualifier enables 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 dp, Android enables you to create layouts that are designed for specific screen dimensions without concern for different pixel densities.

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

res/layout/main_activity.xml           # For phones (smaller than 600dp smallest width)
res/layout-sw600dp/main_activity.xml   # For 7” tablets (600dp wide or wider)

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: Typical phone screen (240x320 ldpi, 320x480 mdpi, 480x800 hdpi, etc.)
  • 480dp: Large phone screen ~5" (480x800 mdpi)
  • 600dp: 7” tablet (600x1024 mdpi)
  • 720dp: 10” tablet (720x1280 mdpi, 800x1280 mdpi, etc.)

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

Figure 6. Recommended width breakpoints to support different screen sizes.

Values for the smallest width qualifier are dp, because what matters is the amount of screen space available after the system accounts for pixel density (not the raw pixel resolution).

The sizes you specify using resource qualifiers like smallest width are not the actual screen sizes. Rather, the sizes specify the width or height in dp units that are available to your app's window. The Android system might use some of the screen for system UI (such as the system bar at the bottom of the screen or the status bar at the top), so some of the screen might not be available for your layout. If your app is used in multi-window mode, the app only has access to the size of the window that contains the app. When the window is resized, it triggers a configuration change with the new window size, which enables the system to select an appropriate layout file. So, the resource qualifier sizes you declare should specify only the space needed by your app. The system accounts for any space used by system UI when providing space for your layout.

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, you might want to use a two-pane layout whenever the screen provides at least 600dp of width, which might change depending on whether the device is in landscape or portrait orientation. In that case, you should use the available width qualifier as follows:

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

If available height is a concern for your app, you can use the available height qualifier. For example, layout-h600dp for screens with at least 600dp of screen height.

Orientation qualifiers

Even though 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 layout directory names. Just be sure the orientation qualifiers come after the size qualifiers. For example:

res/layout/main_activity.xml                # For phones
res/layout-land/main_activity.xml           # For phones 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 App resources overview.

Modularized UI components using fragments

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

For example, the list-detail pattern (see SlidingPaneLayout above) could be implemented with one fragment containing the list and another fragment containing the list item details. On large screens the fragments could be displayed side by side; on small screens, individually, filling the screen.

To learn more, see the Fragments overview.

Activity embedding

If your app consists of multiple activities, activity embedding enables you to easily create an adaptive UI.

Activity embedding displays multiple activities or multiple instances of the same activity simultaneously in an application’s task window. On large screens, activities can be displayed side by side; on small screens, stacked one on top of the other.

You determine how your app displays its activities by creating an XML configuration file which the system uses to determine the appropriate presentation based on screen size. Alternatively, you can make Jetpack WindowManager API calls.

Activity embedding supports device orientation changes and foldable devices, stacking and unstacking activities as the device rotates or folds and unfolds.

For more information, see Activity embedding.

Screen sizes and aspect ratios

Test your app on a variety of screen sizes and aspect ratios to ensure your UI scales correctly.

Android 10 (API level 29) and higher support a wide range of aspect ratios. Foldable form factors can vary from tall, narrow screens, such as 21:9 when folded, to a square aspect ratio of 1:1 when unfolded.

To ensure compatibility with as many devices as possible, test your apps for as many of the following screen aspect ratios as you can.

Figure 7. Screen aspect ratios.

If you cannot support some aspect ratios, use maxAspectRatio and minAspectRatio to indicate the highest and lowest ratios your app can handle. In cases where screens exceed these limits, your app might be put in compatibility mode.

If you don't have access to devices for all the different screen sizes you want to test, you can use the Android Emulator to emulate almost any screen size.

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

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 app should resize, or you can even restrict which devices can install your app based on screen configuration. For more information, see Declare restricted screen support.

Additional resources