Surface Duo Layout

Important

This article describes functionality and guidance that is in public preview and may be substantially modified before it's generally available. Microsoft makes no warranties, express or implied, with respect to the information provided here.

The SurfaceDuoLayout component is a custom layout built to display Views when the device is in single screen mode or dual screen mode. It works like a set of two containers in spanned mode and one container in single-screen mode.

Single-screen layout Dual-screen layout
app on single screen in wide mode app spanned across two screens in wide mode
app on single screen in tall mode app spanned across two screens in tall mode

XML Attributes

  • single_screen_layout_id - Receives the layout id to be displayed in single screen mode.
  • dual_screen_start_layout_id - Receives the layout id to be displayed in the start container of dual screen mode.
  • dual_screen_end_layout_id - Receives the layout id to be displayed in the start container of dual screen mode.

The SurfaceDuoLayout has the ability to create a single container for dual portrait and dual landscape modes.

We added four new attributes that will help you create the UI you want:

  • dual_portrait_single_layout_id - Receives the layout id for the dual portrait single container.
  • is_dual_portrait_single_container - Creates an empty dual portrait single container.
  • dual_landscape_single_layout_id - Receives the layout id for the dual landscape single container.
  • is_dual_landscape_single_container - Creates an empty dual landscape single container.

smallestScreenSize support

When an Activity transitions to a new screen mode but it is not recreated because flag smallestScreenSize is set in the Manifest file, the layout will detect the new configuration and automatically resize the containers or even add or remove the second container depending on the SurfaceDuoLayout configuration.

Android Studio preview

  • tools_hinge_color - Select hinge color in layout preview.
  • tools_screen_mode - Select screen mode in layout preview.

If you add fragments inside xml files to SurfaceDuoLayout, you will need the following attributes to preview the fragment UI:

  • show_in_single_screen
  • show_in_dual_screen_start
  • show_in_dual_screen_end
  • show_in_dual_portrait_single_container
  • show_in_dual_landscape_single_container

Container IDs

The containers have the ids:

  • first_container_id
  • second_container_id

No matter the screen orientation or special behaviour set to the layout, if the layout will show only one container, its id will be first_container_id. If it will show two container there will also be second_container_id.

For example:

  • If the application is in single screen mode the container id will be first_container_id.
  • If a transition to dual screen mode in dual portrait is done and we have dual_portrait_single_layout_id or is_dual_portrait_single_container set then there will be only one container in dual screen mode and its id will still be first_container_id.
  • When a transition to dual landscape is done but we don't have the attributes dual_landscape_single_layout_id and is_dual_landscape_single_container set, then both containers with the ids first_container_id and second_container_id will be present.

Create SurfaceDuoLayout in code

findViewById<FrameLayout>(R.id.parent).addView(
    SurfaceDuoLayout(this, SurfaceDuoLayout.Config().apply {
        singleScreenLayoutId = R.layout.single_screen
        dualScreenStartLayoutId = R.layout.dual_screen_start
        dualScreenEndLayoutId = R.layout.dual_screen_end
        dualLandscapeSingleLayoutId = R.layout.single_screen
    })
)

Replace SurfaceDuoLayout configuration

The code below will discard the old configuration, replace it with a new one and inflate the view with the new configuration.

findViewById<SurfaceDuoLayout>(R.id.surface_duo_layout)
    .newConfigCreator()
    .singleScreenLayoutId(R.layout.single_screen)
    .dualScreenStartLayoutId(R.layout.dual_screen_start)
    .dualScreenEndLayoutId(R.layout.dual_screen_end)
    .reInflate()

Update SurfaceDuoLayout configuration

The code below will update the current configuration with the selected attributes and inflate the view:

findViewById<SurfaceDuoLayout>(R.id.surface_duo_layout)
    .updateConfigCreator()
    .dualScreenStartLayoutId(R.layout.dual_screen_start)
    .reInflate()

How to use the layout components

To create an application, you can either use an Activity or an Activity with Fragments to handle the UI. The Fragments can also be declared in a layout resource file or can be directly created in an Activity. We'll talk about how the components handle these cases later.

process diagram

Using an Activity

Here, we look at how the components work by using just an Activity to handle the UI.

  1. First, you have to add the SurfaceDuoLayout to the *.xml file of the Activity.

    <com.microsoft.device.dualscreen.layouts.SurfaceDuoLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/enlightened_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:animateLayoutChanges="true"
        app:single_screen_layout_id="@layout/single_screen_layout"
        app:dual_screen_start_layout_id="@layout/single_screen_layout"
        app:dual_screen_end_layout_id="@layout/dual_screen_end_layout"/>
    
  2. Then, create the three layouts for the different screen modes.

  3. Link your layout to your activity.

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

Using Fragments declared in a resource file

Here, we see how the components work with fragments that are declared in the *.xml files.

  1. First, you have to add the SurfaceDuoLayout to the *.xml file of the Activity.

    <com.microsoft.device.dualscreen.layouts.SurfaceDuoLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/activity_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:single_screen_layout_id="@layout/single_screen_layout"
        app:dual_screen_start_layout_id="@layout/dual_screen_start_layout"
        app:dual_screen_end_layout_id="@layout/dual_screen_end_layout" />
    
  2. Next, you declare your fragments into the singlescreenlayout.xml, dualscreenstartlayout.xml, and dualscreenendlayout.xml files.

    <fragment
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/single_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:name="com.microsoft.device.display.samples.contentcontext.MapPointListFragment" />
    
  3. Link your layout to your activity.

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

Using Fragments created in an Activity

Finally, we will see how the components work with fragments that are added using getSupportFragmentManager().beginTransaction().‚Äč

  1. First, you have to add the SurfaceDuoLayout to the *.xml file of the Activity.

    <com.microsoft.device.dualscreen.layouts.SurfaceDuoLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/activity_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    

    This creates the containers for the views but there will be nothing inflated into them.

  2. You can have access to the containers of the SurfaceDuoLayout by using their ids:

    • first_container_id
    • second_container_id
  3. Next please visit fragment manager state handler library as fragments need special care when dealing with screen mode transitions.