The confusing world of Android adaptive layouts
Tags: android, mobile, kotlin, tristan
Published: 2023-08-02 21:22:57

### Table of contents
1. [The UI wireframe](#wire)
2. [The confusing world of Android adaptive layouts](#confusing)
3. [Your UI System](#UI)
4. [Landscape mode](#landscape)

### The code
- [GitHub](https://github.com/thePlebDev/Clicker)

### Introduction <a name="intro"></a>
- I have embarked on my next app, a Twitch client app. This series will be all my notes and problems faced when creating this app.

### The UI wireframe <a name="wire"></a>
- So I want two configurations for my app. A portrait configuration and landscape configuration. Basically, it should look something like this:


![portrait landscape layout](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ieuny7d4ol038ubj1u70.png)


### The confusing world of Android adaptive layouts <a name="confusing"></a>
- Now I call adaptive layouts confusing because there are just so many options. Let me give you an example.
 you can use: 

- [Compose adaptive layout](https://developer.android.com/jetpack/compose/layouts/adaptive)

- [XML adaptive layouts](https://developer.android.com/develop/ui/views/layout/constraint-layout)

- [SlidingPaneLayout](https://developer.android.com/develop/ui/views/layout/twopane)

- [Window size classes](https://developer.android.com/guide/topics/large-screens/support-different-screen-sizes)

- [Alternative Resources](https://developer.android.com/guide/topics/resources/providing-resources#AlternativeResources)

- [Restricted Screen support](https://developer.android.com/guide/practices/screens-distribution)

- Obviously it is nice to have so many choices but, it would also be nice to have a clear and direct path to creating adaptive layouts.


### Your UI System <a name="UI"></a>
- First and foremost, your adaptive layout depends on which UI system you are using. Are you using the traditional XML or are you use Jetpack Compose?. For my application, I am using both XML and Compose code. So my original UI code looks like this:

```
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    tools:context=".presentation.stream.StreamFragment">

        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">

                <WebView
                    android:id="@+id/webView"
                    android:layout_width="0.dp"
                    android:layout_height="0.dp"
                    app:layout_constraintDimensionRatio="16:9"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintTop_toTopOf="parent" />

            <androidx.compose.ui.platform.ComposeView
                android:id="@+id/compose_view"
                android:layout_width="0dp"
                android:layout_height="0dp"

                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/webView" />
        </androidx.constraintlayout.widget.ConstraintLayout>

</FrameLayout>

```
- The main things I want to draw you attention to are this:

- ` 1) I am not using any hard coded values`. As you can image hard coded values become very unreliable as your app is used on devices on different sizes.

- `2) ConstraintLayout`, I am using a Constraint layout to take advantage of its ability to form [adaptive layouts](https://developer.android.com/develop/ui/views/layout/constraint-layout). 

- `3) width and heights`, all width and heights are set to 0dp which is considered `matching constraints`. Basically we are telling the design system to allow the constraints to determine the widths and heights. You can read more about it [HERE](https://developer.android.com/develop/ui/views/layout/constraint-layout#add-a-constraint).

- `4) constraintDimensionRatio`, anytime you are dealing with videos or pictures you have to be aware of the aspect ratio. Basically, we don't want the video to look squished or stretched and we want it to remain in the `16:9` viewing ratio. `app:layout_constraintDimensionRatio="16:9"` tells the design system, to automatically calculate the width available and stretch the height until we have the desired height which gives us the `16:9` ratio. 

- `5) constraintTop_toBottomOf="@+id/webView` , in the [ComposeView](https://developer.android.com/jetpack/compose/migrate/interoperability-apis/compose-in-views#compose-in-fragments) we are restricting its height to the bottom of `WebView`

- Since I have not hard coded any direct values and instead relied on constraints and aspect ratios to define my height and width. No matter what screen size uses our application, in portrait mode it will adapt

### Landscape mode <a name="landscape"></a>
- Again I want to remind the reader that your design system will determine how you will handle landscape orientations. However, since I have both XML and Compose I will rely heaviliy on these two resources:

**1) [Alternative Resources for XML](https://developer.android.com/guide/topics/resources/providing-resources#AlternativeResources)**

**2) [Configuration classes for Compose](https://developer.android.com/reference/android/content/res/Configuration)**


#### Alternative Resources for XML
- These can be created through android studio like so:

![Landscape orientation creation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/10pnmwrt7luyfu09sx34.png)

- Just make sure the name of the file is the exact same as portrait file and that the [quantifiers](https://developer.android.com/guide/topics/resources/providing-resources#AlternativeResources) are set to Landscape


#### Configuration classes for Compose
- This is where things are a bit up to the individual because there are two main ways to do this:

**1) [Window Size classes](https://developer.android.com/guide/topics/large-screens/support-different-screen-sizes#window_size_classes)** the official recommended way 

**2) [Configuration class hack](https://developer.android.com/reference/android/content/res/Configuration)** the way I did it

- So I would caution the user, that you should probably not follow my lead here and I will probably convert this code into the window class into the future. However, for the time being I am using this hack:
```
@Composable
fun StreamView(
    streamViewModel: StreamViewModel
){

    var orientation by remember { mutableStateOf(Configuration.ORIENTATION_PORTRAIT) }
    val configuration = LocalConfiguration.current

    LaunchedEffect(configuration) {
        // Save any changes to the orientation value on the configuration object
        snapshotFlow { configuration.orientation }
            .collect { orientation = it }
    }

    when (orientation) {
        Configuration.ORIENTATION_LANDSCAPE -> {

            Column(){
                Text("LANDSCAPE",fontSize=30.sp,color= Color.Red)
               
            }
        }
        else -> {
            Column(){
                Text("PORTRAIT",fontSize=30.sp,color= Color.Red)
                
            }
        }
    }

}

``` 

- Now if we combine this with the Landscape XML file:
```
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    tools:context=".presentation.stream.StreamFragment">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <WebView
            android:id="@+id/webView"
            android:layout_width="0.dp"
            android:layout_height="0.dp"

            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            />

        <androidx.compose.ui.platform.ComposeView
            android:id="@+id/compose_view"
            android:layout_width="0dp"
            android:layout_height="0dp"

            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
             />
    </androidx.constraintlayout.widget.ConstraintLayout>

</FrameLayout>

```
- Notice the constraints, I have used the constraints to tell our design system to grow both the ComposeView and the WebView as large as the screen will allow them. This will overlap them and give the start of a stream overlay. (Now we get to rebuild of all Twitch's stream overlay, yay!)

- Thankfully, through heavy use of the [ConstraintLayout](https://developer.android.com/develop/ui/views/layout/constraint-layout). We now have our desired UI


### Conclusion
- Thank you for taking the time out of your day to read this blog post of mine. If you have any questions or concerns please comment below or reach out to me on [Twitter](https://twitter.com/AndroidTristan).