头图
Optimize your application for foldable devices and large screen devices

The screen sizes of Android devices are changing with each passing day. With the increasing popularity of tablets and foldable devices, it is particularly important to understand the window size and status of your application when developing responsive user interfaces. Jetpack WindowManager has now entered the beta testing stage. This library provides WindowManager Android framework, including support for responsive UI, callback adapters that detect screen changes, and test window API support. But Jetpack WindowManager also adds support for window environments such as foldable devices and ChromeOS.

The new WindowManager API includes the following:

  • WindowLayoutInfo : Contains the display characteristics of the window, such as whether the window is foldable or includes a hinge
  • FoldingFeature : Allows you to monitor the folding status of the foldable device to determine the posture of the device
  • WindowMetrics : Provides display metrics for the current window or all windows

Jetpack WindowManager is not tied to Android, which allows the API to quickly iterate to support the rapidly evolving market, and also allows developers to get support by updating the library without having to wait for the Android version to update.

Now that the Jetpack WindowManager library has entered the beta testing stage, we encourage all developers to use Jetpack WindowManager. Its device-independent API, testing API and the WindowMetrics it introduces enable your application to easily respond to changes in window size. It has entered the beta testing stage, which means you can focus on creating exciting experiences on these devices with peace of mind. Jetpack WindowManager supports at least API 14.

About Jetpack WindowManager

Jetpack WindowManager is a modern library prioritized by Kotlin. It supports different types of new devices and provides "AppCompat" functions to build applications with responsive UI.

folded state

Supporting foldable devices is the most intuitive feature of Jetpack WindowManager library. When the folding state of the device changes, the application will receive the corresponding event, and then update the UI interface to support the new user interaction.

△ 在 Samsung Galaxy Z Fold2 上运行的 Google Duo

△ Google Duo running on Samsung Galaxy Z Fold2

You can learn how to support foldable devices Google Duo study case

There are two folding states, namely FLAT (flattened) and HALF_OPENED (half open). For FLAT, you can consider the surface to be completely flat and open, although in some cases it may be divided by hinges. For HALF_OPENED, there are at least two logical areas in the window. We use pictures below to illustrate the possible situations of each state.

△ 折叠状态: FLAT 和 HALF-OPENED

△ Folded state: FLAT ) and HALF-OPENED )

When the application is active, events can be collected through the Kotlin data stream to obtain information about changes in the folding state.

We control the start and end of events collected by lifecycleScope, as the article " design repeatOnLifeCycle API story behind " and the sample code:

lifecycleScope.launch(Dispatchers.Main) {
    // 传递给 repeatOnLifecycle 的代码块将在生命周期进入 STARTED 时执行
    // 并在生命周期为 STOPPED 时取消
    // repeatOnLifecycle 将会在生命周期再次进入 STARTED 时自动重启代码块
    lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
        // 当生命周期处于 STARTED 时安全地从 windowInfoRepository 中收集数据
        // 当生命周期进入 STOPPED 时停止收集数据
        windowInfoRepository.windowLayoutInfo
            .collect { newLayoutInfo ->
                updateStateLog(newLayoutInfo)
                updateCurrentState(newLayoutInfo)
            }
    }
}

When the user can see the application, the application can update the layout using the information contained in WindowLayoutInfo

FoldingFeature includes information such as hinge direction ), and whether the folding function creates two logical screen areas ( isSeparating ) attributes). We can use these values to check if the device is in desktop mode (the screen is half open and the hinge is horizontal):

△ 设备处于 TableTop 模式

△ The device is in TableTop mode

private fun isTableTopMode(foldFeature: FoldingFeature) =
    foldFeature.isSeparating && 
            foldFeature.orientation == FoldingFeature.Orientation.HORIZONTAL

Or book mode (the screen is half open and the hinge is in the vertical orientation):

△ 设备处于 Book 模式

△ The device is in Book mode

private fun isBookMode(foldFeature: FoldingFeature) =
    foldFeature.isSeparating &&
            foldFeature.orientation == FoldingFeature.Orientation.VERTICAL

Please refer to: in a foldable device. The example in this article introduces how to implement such a function in a media player application.

Note: It is very important to collect events in the main thread/UI thread, which can avoid synchronization problems between UI and event processing.

supports responsive UI

The screen size of Android devices changes very frequently, so it’s important to start designing a fully adaptive and responsive UI. Another feature included in the Jetpack WindowManager library is the ability to retrieve indicator information for the current window and the largest window. This is similar to WindowMetrics API API 30, but it is backward compatible to API 14.

Jetpack WindowManager provides two ways to retrieve WindowMetrics information, through the stream in the data stream event or through the WindowMetricsCalculator class for synchronization processing.

When writing view code, it may be difficult to use asynchronous APIs (such as onMeasure )). At this time, you can use WindowMetricsCalculator.

val windowMetrics = 
    WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(activity)

Another use case is for testing (see the testing section below for details).

In the high-level usage of processing application UI, WindowInfoRepository#currentWindowMetrics provided by the library can be notified when the window size changes, which has nothing to do with whether the configuration change is triggered.

This example is about how to switch your layout based on the available area:

// 因为 repeatOnLifecycle 是挂起函数,所以创建一个新的协程
lifecycleScope.launch(Dispatchers.Main) {
   // 传递给 repeatOnLifecycle 的代码块将在生命周期进入 STARTED 时执行
    // 并在生命周期为 STOPPED 时取消
    // 它将会在生命周期再次进入 STARTED 时自动重启
   lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
       // 当生命周期处于 STARTED 时安全地从 windowInfoRepository 中收集数据
       // 当生命周期进入 STOPPED 时停止收集数据
       windowInfoRepository.currentWindowMetrics
           .collect { windowMetrics ->
               val currentBounds = windowMetrics.bounds
               Log.i(TAG, "New bounds: {$currentBounds}")
               // 我们可以根据需要在这里更新布局
           }
   }
}

callback adapter

To use this library in the Java programming language or use the callback interface, please add androidx.window:window-java dependency to your application. This component provides WindowInfoRepositoryCallbackAdapter through which you can register (unregister) a callback for receiving device posture and window indicator information updates.

public class SplitLayoutActivity extends AppCompatActivity {

   private WindowInfoRepositoryCallbackAdapter windowInfoRepository;
   private ActivitySplitLayoutBinding binding;
   private final LayoutStateChangeCallback layoutStateChangeCallback =
           new LayoutStateChangeCallback();

   @Override
   protected void onCreate(@Nullable Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);

       binding = ActivitySplitLayoutBinding.inflate(getLayoutInflater());
       setContentView(binding.getRoot());

       windowInfoRepository =
               new WindowInfoRepositoryCallbackAdapter(WindowInfoRepository.getOrCreate(this));
   }

   @Override
   protected void onStart() {
       super.onStart();
       windowInfoRepository.addWindowLayoutInfoListener(Runnable::run, layoutStateChangeCallback);
   }

   @Override
   protected void onStop() {
       super.onStop();
       windowInfoRepository.removeWindowLayoutInfoListener(layoutStateChangeCallback);
   }

   class LayoutStateChangeCallback implements Consumer<WindowLayoutInfo> {
       @Override
       public void accept(WindowLayoutInfo windowLayoutInfo) {
           binding.splitLayout.updateWindowLayout(windowLayoutInfo);
       }
   }
}

test

Developers mentioned that a more robust testing API is critical to maintaining LTS (long-term support). Let's talk about how to test the posture of a foldable device on a normal device.

Now, we already know that the Jetpack WindowManager library can send notifications to your application when the device posture changes so that you can modify the layout of the application.

The library provides WindowLayoutInfoPublisherRule androidx.window:window-testing WindowInfoLayout to support testing FoldingFeature:

import androidx.window.testing.layout.FoldingFeature
import androidx.window.testing.layout.WindowLayoutInfoPublisherRule

We can dummy a FoldingFeature in the test:

val feature = FoldingFeature(
   activity = activity,
   center = center,
   size = 0,
   orientation = VERTICAL,
   state = HALF_OPENED
)
val expected =
   WindowLayoutInfo.Builder().setDisplayFeatures(listOf(feature)).build()

publisherRule.overrideWindowLayoutInfo(expected)

Then use WindowLayoutInfoPublisherRule to publish it:

val publisherRule = WindowLayoutInfoPublisherRule()

publisherRule.overrideWindowLayoutInfo(expected)

Finally, use the available Espresso matcher to check whether the layout of the Activity we are testing meets expectations.

In the following test, a HALF_OPENED is released in the 0614dc4d6b1950 state and the hinge is perpendicular to the center of the screen:

@Test
fun testDeviceOpen_Vertical(): Unit = testScope.runBlockingTest {
   activityRule.scenario.onActivity { activity ->
       val feature = FoldingFeature(
           activity = activity,
           orientation = VERTICAL,
           state = HALF_OPENED
       )
       val expected =
           WindowLayoutInfo.Builder().setDisplayFeatures(listOf(feature)).build()

       val value = testScope.async {
           activity.windowInfoRepository().windowLayoutInfo.first()
       }
       publisherRule.overrideWindowLayoutInfo(expected)
       runBlockingTest {
           Assert.assertEquals(
               expected,
               value.await()
           )
       }
   }

    // 检查在有垂直折叠特性时 start_layout 在 end_layout 的左侧
    // 这需要在足够大的屏幕上运行测试以适应屏幕上的两个视图
   onView(withId(R.id.start_layout))
       .check(isCompletelyLeftOf(withId(R.id.end_layout)))
}

View sample code

The latest on Github shows how to use the Jetpack WindowManager library to WindowLayoutInfo stream, or to obtain display posture information WindowInfoRepositoryCallbackAdapter

This example also contains some tests, which can be run on any device or simulator.

Use WindowManager in your application

Foldable devices and dual-screen devices are no longer just experimental or forward-looking-the large screen space and additional device gestures have been proven to have user value, and there are now more devices for your users to choose from. Foldable devices and dual-screen devices represent the natural evolution of smart phones. For Android developers, this provides an opportunity to enter the growing high-end market, thanks to device manufacturers who have renewed their attention to large-screen devices.

We launched Jetpack WindowManager alpha01 version last year. The library has been developing steadily since then, and early feedback has greatly improved it. Now, it has embraced Android's Kotlin first concept, gradually transitioning from a callback-driven model to a coroutine and data flow. As WindowManager enters the testing phase, the API has been stable, and we strongly recommend using it.

Updates are not limited to this. We plan to add more features to the library and develop it into a system UI library unbundled with AppCompat, so that developers can easily implement modern and responsive UIs on all Android devices.

Welcome to click here to submit feedback to us, or share your favorite content, found problems. Your feedback is very important to us, thank you for your support!


Android开发者
404 声望2k 粉丝

Android 最新开发技术更新,包括 Kotlin、Android Studio、Jetpack 和 Android 最新系统技术特性分享。更多内容,请关注 官方 Android 开发者文档。