Table of contents
The code
Introduction
- 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
- So I want two configurations for my app. A portrait configuration and landscape configuration. Basically, it should look something like this:
The confusing world of Android adaptive layouts
Now I call adaptive layouts confusing because there are just so many options. Let me give you an example.
you can use: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
- 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.3) width and heights
, all width and heights are set to 0dp which is consideredmatching 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.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 the16: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 the16:9
ratio.5) constraintTop_toBottomOf="@+id/webView
, in the ComposeView we are restricting its height to the bottom ofWebView
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
- 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
2) Configuration classes for Compose
Alternative Resources for XML
- These can be created through android studio like so:
- Just make sure the name of the file is the exact same as portrait file and that the quantifiers 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 the official recommended way
2) Configuration class hack 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. 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.
Top comments (0)