About 100 million new tablet devices were activated in the past 12 months, and Chrome OS activations increased by 92%, the fastest growing desktop platform. That means there are more than 250 million large-screen devices running Android on tablets, foldables, and Chrome OS devices, and the number of foldables is growing, up more than 250% year-over-year, so," "Large screen" is becoming an important and fastest growing segment of Android devices. This also makes device manufacturers realize that optimizing for large screens is an opportunity to make devices stand out in the high-end mobile phone market segment.
With the rapid development of tablets and foldable devices, it is time to stop thinking about phones and tablets separately, and instead provide applications for an entire ecosystem to increase their influence in the market. In this article we will describe how developers can quickly embrace and enter this market segment with the new APIs and tools we provide.
If you prefer to see this through video, check it out here:
https://www.bilibili.com/video/BV1eq4y1y7NR/?aid=593083784&cid=481421896&page=1
△ Build Android interface for any screen size
User Engagement
In the months following the Android Developer Summit, the Play Store introduced new incentives, including a rating of apps by device type, to encourage developers to focus more on the big screen. So now is the perfect time to meet these changes, not only to meet the market changes in the future, but also to solve the embarrassment of the lack of user experience due to the lack of adaptation to the large screen.
△ Microsoft Outlook application interface optimized for large screens
We also observed that apps optimized for all screen sizes achieved good results in metrics around user engagement, retention, and more. For example, one of the successful cases, Candy Camera, optimizes the layout of foldable devices and large screens, so that users who use these devices spend 10% more time on the application, and the 7-day user retention rate has increased by 14%. And this is not an exception. Another example is Microsoft Outlook, whose recent update takes full advantage of the large screen by using a dual-window layout to view inbox and email content at the same time, with the ability to independently compose in a single window with multiple displays e-mail. These examples speak volumes: it's time to start designing and developing freely without the constraints of a single interface on the phone.
But don't worry too much, we've done a lot of work to make it as easy as possible for you throughout the development cycle, let's take a look at what tools we provide to help you better adapt to large screens Bar.
Window Size Class and Reference Devices
In the diversified device ecosystem, various Android devices have different shapes and sizes, which makes the layout of the application need to be very flexible. Running the same application on different devices should be able to flexibly adapt to the screen size of different devices. To that end, we delved into the Android device market and took some inspiration from the best practices of adaptive and responsive development for the web to build the foundation of a new Android interface that's dynamically resizable, which we call window size kind.
Window size classes are a subjective set of viewport breakpoints against which you can design, develop, and test resizable app layouts. These breakpoints will help you understand the key dimensions to optimize for in order to adapt your application to the entire ecosystem. There are three window size classes, Small, Medium, and Expanded, which are designed to balance simplicity and flexibility of layout to optimize your app for special cases. We recommend that you use window size breakpoints to make advanced app layout decisions, and for layout grid column changes, they also map to Material Design layout breakpoints.
The new WindowSizeClass API, available in Jetpack WindowManager 1.1, will free you from error-prone isTable logic. These new APIs will also eliminate the need for custom logic for devices to switch between landscape and portrait, and in most cases simply design for different window size class breakpoints and the app will adapt to the correct layout and various app states.
class WindowMetrics {
class WindowSizeClass(val name: String) {
companion object {
val COMPACT = WindowSizeClass(“COMPACT”)
val MEDIUM = WindowSizeClass(“MEDIUM”)
val EXPANDED = WindowSizeClass(“EXPANDED”)
}
}
val widthClass: WindowSizeClass
get() {...}
val heightClass: WindowSizeClass
get() {...}
}
One more important point is that starting with Android 12, apps will be allowed to resize arbitrarily, and all apps will be allowed to run in multi-window mode. Looking at the Samsung Galaxy Fold series, the split-screen mode it provides increases screen utilization by 7 times, and split-screen allows users to adjust the size according to their preferences, which further highlights the importance of building a dynamically resizable interface. sex.
Considering the layout from a device and configuration point of view, we let each window size class represent some typical device configuration (as shown below), when you think about designing the layout based on breakpoints, this will be A useful reference. Among them, smaller represents the typical mode of a phone in portrait mode, medium represents the size of most tablets and larger foldable devices, and unfolded represents a tablet or larger foldable device, or The display of desktop devices in landscape mode.
△ Representation of width-based window size classes
In addition to the above three width-based breakpoints, we have also introduced a height-based breakpoint with the same category name in order to be suitable for higher-level layout scenarios and give more flexibility. Suppose we need to use a small height breakpoint to optimize the layout of the landscape mobile interface, although this may sound complicated, but don't worry, according to our in-depth discussions with many Android developers, in most cases, only the width is required. Just do the layout adaptation.
△ Representation of height-based window size classes
All in all, the advent of the window size class represents a major advance in adaptive and responsive layout development for Android, including updated and optimized guidelines, new APIs in Jetpack WindowManager, and new tools in Android Studio.
Speaking of Android Studio, we're introducing a new category of tools in Android Studio Bumblebee, which we'll call Reference Devices, and it was introduced to make Android app builds responsive and adaptable to all device categories. After extensive research on market data, we have provided four Reference Devices representing mobile phones, foldable devices, tablets and desktop devices. They cover both mainstream devices on the market today, as well as fast-growing market segments, and ensure that applications work well in most window size classes.
△ Four Reference Devices
In this article's introduction to large screen adaptation, if you just want to quickly know the points to pay attention to, please keep the following points in mind:
- To ensure that your app displays correctly on different device sizes, optimize the layout for smaller and expanded width size classes first;
- Test your application on all Reference Devices, prioritizing the best layout in the medium size;
- To provide a better user experience, add features that make sense to your app, such as supporting the collapsed state of foldable devices or optimizing for keyboard, mouse, and stylus input support;
For more details on window size classes, see Size Class .
JetNews Compose Example an example of a window size class in action.
large screen
Designing a beautiful and responsive interface is the first step in developing an app, but how to implement and maintain this design is definitely a challenge, and to simplify your work, we will strive to provide efficient tools. Now we'll cover how to update existing apps to optimize for all screen sizes with the new Jetpack API and Android Studio features.
As an example, we'll use Trackr, an open source task management application that we recently updated to better support larger-screen devices. Trackr was developed to demonstrate best practices on how to support accessibility experiences in Android, and with the recent update for larger screens, it's certainly a great example.
△ Change the previous Trackr style
Pictured above is the Trackr style before we made the changes, and you'll notice that no matter what device or screen you're on, there's a single-window task list and a bottom app bar for navigating to archives or settings pages. Trackr has several main pages, including the Task List, Task Details, and Task Creation or Editing pages. Next, let's optimize Trackr for large screens.
NavigationRailView
We are developing a new tool Visual Linting in Android Studio Chipmunk. It can be used to check the interface in Layout Validation and display some warnings and related suggestions. We used Visual Linting to examine Trackr's layout to find some potential large-screen-related issues with the tool. We can open the main_activity layout and then open the Layout Validation tool (this option can also be found via the View - Tools Window path).
△ Check the interface in Layout Validation
In the Layout Validation interface, you will find a new Reference Devices category, through which you can use the new Reference Devices functionality in Android Studio. A warning icon can be found in the upper right corner of Layout Validation, click this icon to open the warning window, click on each warning to show which devices are affected. As you can see in the image above, we'll find two caveats related to larger screens: the bottom app bar is only recommended for smaller screens and the MaterialTextView has some lines that contain more than 120 characters.
△ Warning window
Expand the warning to see whether Android Studio provides modification suggestions. The modification suggestion for the bottom app bar warning here is to use the Navigation Rail, the drawer navigation bar, or use the top app bar instead. For Trackr, I think it's more constructive to use navigation routing. The modification suggestion for MaterialTextView is to either reduce the width of the TextView, or consider using a multi-column layout, which is more suitable for our application. For Trackr, we'll use the typical list-plus-detail window style to address these warnings, for devices with medium or larger widths, we'll use NavRail instead of the bottom app bar, and for devices with expanded widths we'll use Dual-window layout to display tasks and related details.
Let's do the first optimization first, use NavRail instead of the bottom app bar. The first thing we need to consider is the navigation model. Fortunately, we will not change many specific views, only the navigation method, because NavRail will always exist in the entire It can be used to navigate to any other view in the view hierarchy. To implement this pattern, we can add the Navigation Rail View to the main_activity layout as shown in the following code:
// main_activity.xml
<androidX.coordinatorlayout.widget.CorrdinatorLayout
…>
<com.google.android.material.navigationrail.NavigationRailView
android:id="@+id/navigation_rail"
android :layout_width="wrap_content"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app: layout_constraintTop_toTopOf="parent"
app:headerLayout="@layout/navigation_rail_header"
app:labelVisibilityMode="unlabeled"
app:menu="@menu/navigation_rail" />
</androidX.coordinatorlayout.widget.CorrdinatorLayout>
Before that, main_activity consisted only of FragmentContainerView and CoordinatorLayout, and hosted other Fragments through NavHostFragment. And once NavigationRailView is placed at the main_activity layout level, it will persist across all views. Still, I only want the NavigationRail for screen sizes with a width of 600dp or larger, an easy way to do this is to add a resource-qualified main_activity layout in the same FragmentContainerView that contains the NavHostFragment Add NavigationRailView on level:
// w600dp/tasks_fragment.xml
<layout...>
<data.../>
<androidx.coordinatorlayout.widget.CoordinatorLayout...>
<com.google.android.material.appbar.AppBarLayout.../>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/tasks_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingLeft="@dimen/pane_margin"
android:paddingRight="@dimen/pane_margin"
tools:ignore="SpeakableTextPresentCheck"
tools:listitem="@layout/task_summary"/>
- <com.google.android.material.bottomappbar.BottomAppBar.../>
- <com.google.android.material.floatingactionbutton.FloatingActionButton.../>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>
We also need to update tasks_fragments.xml to remove the bottom app bar from displays with a width of 600dp or larger. Similar to how you implemented NavRail, you can add a resource-qualified layout to tasks_fragments, then you can remove the bottom app bar and associated floating action buttons, leaving everything else the same so that the task list continues to work as expected. Finally, after setting the ID of the NavRail menu bar to match the ID of the existing navigation destination view, set the NavController for NavRail in MainActivity:
<!=-NavRail Menu -->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item>
android:id="@+id/nav_tasks"
android:icon="@drawable/ic_task" .../>
<item
android:id="@+id/nav_archives"
android:icon="@drawable/ic_archive" .../>
</menu>
// MainActivity NavController
class MainActivity: AppCompatActivity() {
...
override func onCreate(saveInstanceState: Bundle?) {
...
binding.navigationRail?.apply{
setupWithNavController(navController)
setOnItemReselectedListener()
headerView?.setOnClickListener {
navController.navigate(R.id.nav_task_edit_graph)
}
}
}
}
That's it, you can check in Android Studio to see if the display is all right, and switch back and forth in the various Reference Devices to see if the layout is working as we expected. When looking at the Phone Reference Device, the bottom app bar was still visible, and when we switched to the larger screen, we found that it started using NavRail, and everything worked as we expected.
△ Effect under Phone Reference Device
△ Effect under Tablet Reference Device
SlidingPanelLayout
Next let's move on to implementing a two-window view layout based on an expanded-width device. An easy way to support this layout is to use SlidingPaneLayout, which has the advantage of easily reusing existing layout code. Here is the current updated navigation map:
△ The updated navigation map
We can navigate to any top-level layout of the application through NavigationRailView, but we can still navigate to the Fragment of the details page by selecting a single task in the interface. This pattern will change a bit when implementing SlidingPanelLayout, we will add a new layout TwoPaneTasks to contain SlidingPaneLayout, this layout will contain both task list and detailed Fragment. Updating the app's navigation in this way will have the same navigation map regardless of screen size, meaning resizing the screen will not result in changes to the navigation that can confuse users.
Since the tasks and details are presented in the same new Fragment in the SlidingPaneLayout, we add a new sub-navigation hierarchy specifically for this Fragment's navigation interactions. This way, when I select a task and the app goes from dual window to single window, the item will be at the top of the navigation stack and will be visible.
Briefly, we'll use a SlidingPaneLayout and FragmentContainerView to add a new Fragment to host the task and details pane, without having to do a major refactoring of the existing code.
// tasks_two_pane_fragment.xml
<layout...>
<androidx.slidingpanelayout.widget.SlidingPaneLayout
android:id="@+id/sliding_pane_layout"...>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/list_pane"
android:name="com.example.android.trackr.ui.tasks.TasksFragment"
android:layout_width="@dimen/list_pane_width"
android:layout_height="match_parent"
android:layout_weight="@dimen/list_pane_weight"
tools:layout="@layout/tasks_fragment" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/detail_pane"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="@dimen/detail_pane_width"
android:layout_height="match_parent"
android:layout_weight="@dimen/detail_pane_weight"
app:navGraph="@navigation/task_detail"
tools:layout="@layout/task_detail_fragment" />
</androidx.slidingpanelayout.widget.SlidingPaneLayout>
</layout>
Then, go ahead and update the app's top-level navigation hierarchy to make the new dual-window Fragment the app's starting destination page, and remove the detail destination page from the app's navigation graph.
<!-- 顶层导航图 -->
<navigation app: startDestination="@+id/nav_tasks"...>
<fragment
android:id="@+id/nav_tasks"
android:name="..trackr.ui.tasks.TasksTwoPaneFragment" ...>
<action
android:id="@+id/to_task_edit"
app: destination="@id/nav_task_edit_graph' />
</fragment>
<!--Remove the 'details' destination-->
...
</navigation>
<!-SlidingPaneLayout 导航图-->
<navigation...
app:startDestination="@id/nav_task_detail_placeholder">
<action
android:id="@+id/to_task_detail"
app:destination="@id/nav_task_detail"
app:popUpTo="@id/nav_task_detail"
app :popUpToInclusive="true"
<fragment
android:id="@+id/nav_task_detail"
android:name="..trackr.ui..TaskDetailFragment"...>
<argument
android:name="taskId"
app:argType="long" />
</fragment>
<!-- 为其实目的页面使用一个 placeholder-->
<fragment
android:id="@+id/nav_task_detail_placeholder"
android:name="..trackr.ui.PlaceholderFragment"
tools:layout="@layout/placeholder_fragment"/>
</navigation>
Finally, add a new navigation graph specifically for SlidingPaneLayout and handle the SlidingPaneLayoutNavController configuration logic in the TasksTwoPaneFragment Kotlin code. With these two changes, the layout of the application in different shapes of different devices will be more reasonable. After doing this, we can see that the new layout can be perfectly laid out in all device screens through the Reference Devices tool in Android Studio again. And for testing while the app is running, Android Studio Chipmunk provides a resizing-enabled emulator that lets you switch between the same Reference Devices to quickly verify that your app is laid out correctly.
In addition, SlidingPaneLayout provides another important feature that it is not only suitable for large-screen devices, but also for multi-screen devices. Microsoft recently provided a feature for SlidingPaneLayout to support hinge detection, allowing it to automatically support splitting windows across screens without any code changes. This means that the app's new list/detail layout will work on all devices, including multi-screen devices.
While the methods mentioned above are very useful for optimizing large-screen displays, many developers have applications based on multiple activities, and for these applications, the new Activity Embedding API released in 12L will make it possible to support new interface paradigms such as dual-window views. easy, stay tuned.
Jetpack Compose
When Jetpack Compose released version 1.0 in July 2021, there was a huge response from the Android developer community, with thousands of apps already using Compose in production, including the Play Store app itself. Jetpack Compose itself is a declarative interface toolkit, through which you can describe based on the state of the page, and Compose will make all necessary updates on its own. All interfaces are described in code, which makes it easy to make decisions about interface styles at runtime. In the traditional view system, we compile different screen configurations to realize the view. configuration, there is a huge difference between the two. This also allows Compose to easily account for interface changes that come with different screen sizes.
Next, let's use JetNews to show you how to use Compose to adapt to different screen sizes. The main interface of JetNews shows a long list of scrolling articles. Before it is optimized for large screens, its interface is shown in the figure below. It can be found that the extra screen space is not well utilized.
△ JetNews main interface display
The WindowManager API was introduced earlier, and we are currently integrating it into Compose to make it easier to access this information from within Compose. In the meantime, we can create a composable function to handle the integration with the WindowManager and then easily convert the current Activity's window information into the final window size class, the code looks like this:
@Composable
fun Activity.rememberWindowSizeClass(): WindowSize {
val configuration = LocalConfiguration.current
val windowMetrics = remember (configuration) {
WindowMetricsCalculator.getOrCreate()
.computeCurrentWindowMetrics(this)
}
val windowDpSize = with (LocalDensity.current) {
windowMetrics.bounds.toComposeRect().size.toDpSize()
}
when {
windowDpSize.width < 600.dp -> WindowSize.Compact
windowDpSize.width < 840.dp
else -> WindowSize.Expanded
}
}
The WindowManager library will soon have an API to use these classes directly, and Compose will soon support more convenient functions to do the job, so stay tuned. For now, you can temporarily borrow this code for this functionality.
△ JetNews side drawer navigation bar display
Back to JetNews, we can see that in the large screen state, the side drawer navigation bar will appear modally, but it will extend to the entire screen and there will be a lot of blank space. According to the modification suggestion mentioned above, Navigation Rail is used, and Compose supports it directly, we only need to set it and pass in the content.
NavigationRail(
header = {
JetnewsIcon()
}
) {
Column(verticalArrangement = Arrangement.Center) {
Icon(
icon = Icons.Filled.Home,
action = navigateToHome
)
Icon(
icon = Icons.Filled.ListAlt,
action = navigateToInterests
)
}
}
The header icon and two navigation item icons, one for the main page and one for the Interests page, and add their corresponding navigation actions. To integrate Navigation Rail into the application, we made some changes to the top-level application components. First, we get the current window size class, and display the ModalDrawer on the smaller size, then make sure the ModalDrawer is set up to only respond to gestures in that size. Then place the Navigation Rail side-by-side with the main navigation graph that contains all the screens in the app:
@Composable
fun JetnewsApp() {
val windowSize = rememberWindowSizeState()
val isDrawerActive = windowSize == WindowSize.Compact
ModalDrawer(
gesturesEnabled = isDrawerActive
drawerContent = {...}
) {
val showNavRail = isDrawerActive
Row() {
if (showNavRail) {
AppNavRail()
}
JetnewsNavGraph()
}
}
}
Then we found that because the article list still did not fully utilize the space under the large screen, we decided to build the list/details layout under the large screen. This layout method is one of the recommended layouts for the large screen in Material Design. The list is displayed side by side with the open article. The JetNews application has two components that we can reuse: PostList and PostContent. This splitting the interface into components at the beginning not only makes testing easier, but also allows us to easily improve the layout.
To display the Feed and Post side by side, JetNews simply wraps two components with a Row, the first having a fixed width and the second filling the rest of the screen. The details component is wrapped in a cross-fade animation, which lets users see the transition with animated transitions when they click on the list to open the article.
To properly build the list/detail structure, we need to address several issues in addition to the actual layout. One of the more interesting things to think about is how the app transitions between different size layouts, for example, with a foldable phone, the app might change from a larger screen to a smaller screen.
△ Layout conversion on foldable mobile phones
To properly handle how the list and details windows are collapsed into a single-window hierarchy, when on smaller screens, we need to know which window the user last interacted with, for this we implement a simple custom modifier to record The last interaction, and based on that, decides what should be displayed in the different collapsed states, further increasing the hierarchy.
@Composable
fun HomeFeedWithArticleDetailsScreen(...) {
Row() {
PostList(
modifier
.width(334.dp)
.notifyInput(onInteractWithList))
Crossfade(...) {
PostContent(
modifier
.fillMaxSize()
.notifyInput {
onInteractWithDetail(detailPost.id)
}
)
}
)}
}
We also need to know what size screen we are transitioning from showing only one of the windows at a time to showing the list/details layout. In JetNews we first get the window size class information, show single window in small and medium width, and list/detail layout in expanded width.
val windowSize = rememberWindowSizeState()
val homeScreenType = when (windowSize) {
WindowSize.Compact,
WindowSize.Medium -> HomeScreenType.Feed
WIndowSize.Expanded -> HomeScreenType.FeeWithArticleDetails
}
Then, start making changes to JetNews' navigation. JetNews was originally built with a main page and an article page, each with its own ViewModel, and the integration between the navigation and the ViewModel meant that the two pages were always on different navigation paths. However, in order to reorganize the page into a list/detail layout, we need to display the two screens side by side, and here we have two options. One is to nest NavHost in the details page, and the other solution is to unify the ViewModel. Since there is no next-level navigation entry in the details page and only one open article is displayed, we decided to adopt the second method, combining the two ViewModel is combined into one to simplify the structure.
We created three main interface entry points, one is HomeFeedScreen, which is only responsible for displaying PostList; one is ArticleScreen which is responsible for displaying PostContent; and the new HomeFeedWithArticleDetailsScreen is responsible for displaying list/details layout including PostList and PostContent.
△ Left: Main interface entry point HomeFeedScreen Right: Main interface entry point ArticleScreen
△ Main interface entry point HomeFeedWithArticleDetailsScreen
The above picture shows the page style before and after adaptation. It can be found that the utilization of screen space has been greatly improved. But this change is a screen size decision, can we make individual components themselves have different sizes depending on the page? For example, we have a card, when only the title and subtitle are displayed in the list due to space constraints, and when there is more space, it is adjusted to display the image. For such cases we can use Box With Constraints, which is similar to a box layout and can be used for decisions based on measurements within the range.
for a better user experience
Earlier, we mentioned that in order to provide a better user experience, add features that make sense for your app, such as support for foldable devices. Similar to the WindowManager API, we can easily integrate Compose with the API for foldable devices. Through these APIs, it is possible to obtain whether and when the device has triggered functions such as hinges or folding, and what posture the device is currently in. Compose makes it easy to observe the state given by these APIs, making interface transitions easy. Again, the API for this functionality is coming soon in Compose, so stay tuned.
In addition to the APIs mentioned so far, we've been working hard on Compose's internals to enhance input devices including keyboard and mouse support, which is especially useful for apps running on Chrome OS. For Chrome OS and input details, stay tuned for our recent article releases.
For more details on the Compose example, see Compose example code .
The new Compose and Large Screen Guide - Building Adaptive Layouts , hope it will help you with your development.
Testing and Maintenance
Now you know how to easily update your app to build a new resizable interface. How to test and maintain the project is also a very important topic. Maintaining and supporting interfaces of all different sizes can introduce significant test complexity, and we've worked hard to increase test coverage with new automated testing tools and APIs that allow you to configure more devices without increasing the workload. We will host the device through Gradle, enabling virtual devices to run existing instrumentation tests on various screen sizes and API levels. You only need to describe the configuration of the device on which you want to run the tests, Gradle takes care of the rest, including device pre-configuration and running of the test job.
Just define the device during the build script and add it to the device group:
testOptions
devices {
pixel2api29 (com.android.build.api.dsl.ManagedVirtualDevice) {
nexus9api30 (com.android.build.api.dsl.ManagedVirtualDevice) {
device = "Nexus 9"
apiLevel = 30
systemImageSource = "google"
abi = "×86"
}
}
deviceGroups {
mediumAndExpandedWidth{
targetDevices.addAll(devices.pixel2api29)
targetDevices.addAll(devices.nexus9api30)
}
}
Then use the Gradle managed device group to run the tests:
$ gradlew -Pandroid.experimental.androidTest.useUnifiedTestPlatform=true mediumAndExpandedWidthGroupDebugAndroidTest
Since Gradle manages both device configuration and test jobs, Gradle managed devices also support test sharding, allowing you to split tests across a specified number of identical devices to reduce overall test job time. Just specify the following parameters to specify the number of shards:
$ gradlew -Pandroid.experimental.androidTest.numManagedDeviceShards=2 deviceDebugAndroidTest
But we know that running a large number of virtual devices consumes CPU and memory, which may limit the usefulness of Gradle hosted devices and test shards. To address this issue, Gradle Managed Appliances have introduced a new type of virtual appliance optimized for instrumentation testing, called automated test appliances, which run in headless mode, disabling background processes and services typically not needed for automated testing, reducing The overall CPU and memory usage for each device will allow you to run tests against multiple devices representing different screen sizes at the same time. This feature is currently available on Android 10, and will support higher API levels over time to ensure that existing screenshot tests continue to work with automated test equipment.
We're also working on a new set of AndroidX Testing APIs that will allow you to put your device into different states for testing. For example, you can test how your app responds from being folded flat to half-open, or rotated between portrait or landscape modes.
summary
Today we discussed a lot, from new design guidelines and window size classes, to specific APIs for updating existing apps. Large screens and foldable devices represent a large and growing segment of Android, and to capture this growth opportunity, now is the time to build and design interfaces for these devices for a great experience for users of the most advanced devices .
You are welcome here to submit feedback to us, or share your favorite content and found problems. Your feedback is very important to us, thank you for your support!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。