Initial Movies UI
Flesh out the screens
Now we flesh out the screens, making a very simple (but inefficient) list, and a simple display.
Our MovieDisplay sets up a Column as the main content of the Scaffold. Note
the paddingValues that are passed to the content lambda. These define the padding you must use
to avoid overlapping with other slots in the Scaffold.
show in full file app/src/main/java/com/androidbyexample/compose/movies/screens/MovieDisplay.kt
// ...
@OptIn(ExperimentalMaterial3Api::class) // for TopAppBar
@Composable
fun MovieDisplayUi(
// ...
) {
Scaffold(
// ...
modifier = modifier,
) { innerPadding ->
// Text(
// text = "Movie Display: ${movie.title}",
// modifier = Modifier.padding(innerPadding),
// )
Column (
modifier = Modifier
.padding(innerPadding)
.verticalScroll(rememberScrollState())
) {
Label (textId = R.string.title)
Display(text = movie.title)
Label(textId = R.string.description)
Display(text = movie.description)
}
}
}
Our Column is scrollable and stacks Labels and Displays to show the details
of a movie. (Be sure to use our Label instead of the Material3 Label)
MovieListUi is a bit more complex. It uses a Scaffold in the same way as MovieDisplay,
but the contents of its column are dynamic. We iterate through the list of movies
using forEach, and create a nice little Card for each movie, showing an icon and title.
show in full file app/src/main/java/com/androidbyexample/compose/movies/screens/MovieList.kt
// ...
@OptIn(ExperimentalMaterial3Api::class) // for TopAppBar
@Composable
fun MovieListUi(
// ...
) {
Scaffold(
// ...
modifier = modifier,
) { innerPadding ->
// Text(
// text = "Movie List",
Column (
modifier = Modifier
.padding(innerPadding)
// .clickable {
// onMovieClicked(movies[0])
.fillMaxSize()
.verticalScroll(rememberScrollState())
) {
movies.forEach { movie ->
Card (
elevation = CardDefaults.cardElevation(
defaultElevation = 8.dp,
),
onClick = {
onMovieClicked(movie)
},
modifier = Modifier.padding(8.dp)
) {
Row (
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(8.dp)
) {
Icon(
imageVector = Icons.Default.Star,
contentDescription = stringResource(id = R.string.movie)
)
Display(text = movie.title)
}
}
}
}
// )
}
}
We make the icon and title line up nicely by adding an alignment specification.
By defining onClick at the Card level,
show in full file app/src/main/java/com/androidbyexample/compose/movies/screens/MovieList.kt
// ...
@OptIn(ExperimentalMaterial3Api::class) // for TopAppBar
@Composable
fun MovieListUi(
// ...
) {
Scaffold(
// ...
) { innerPadding ->
// ...
Column (
// ...
) {
movies.forEach { movie ->
Card (
// ...
defaultElevation = 8.dp,
),
onClick = {
onMovieClicked(movie)
},
modifier = Modifier.padding(8.dp)
) {
// ...
}
}
}
// ...
}
}
the user can click anywhere inside the card to select the movie. That click passes the
movie for that card to onMovieClicked(), informing the caller that a movie has been
selected.
As usual, define any literal strings that the user will see in
app/src/main/res/values/strings.xml.
All code changes
CHANGED: app/src/main/java/com/androidbyexample/compose/movies/MainActivity.kt
package com.androidbyexample.compose.movies
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.androidbyexample.compose.movies.screens.Ui
import com.androidbyexample.compose.movies.ui.theme.MoviesTheme
class MainActivity : ComponentActivity() {
private val viewModel by viewModels<MovieViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
MoviesTheme {
Ui(
viewModel = viewModel,
) {
finish()
}
}
}
}
}
CHANGED: app/src/main/java/com/androidbyexample/compose/movies/screens/MovieDisplay.kt
package com.androidbyexample.compose.movies.screens
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.androidbyexample.compose.movies.Movie
import com.androidbyexample.compose.movies.R
import com.androidbyexample.compose.movies.components.Display
import com.androidbyexample.compose.movies.components.Label
@OptIn(ExperimentalMaterial3Api::class) // for TopAppBar
@Composable
fun MovieDisplayUi(
movie: Movie,
modifier: Modifier = Modifier,
) {
Scaffold(
topBar = {
TopAppBar(
title = {
Text(text = movie.title)
}
)
},
modifier = modifier,
) { innerPadding ->
// Text(
// text = "Movie Display: ${movie.title}",
// modifier = Modifier.padding(innerPadding),
// )
Column (
modifier = Modifier
.padding(innerPadding)
.verticalScroll(rememberScrollState())
) {
Label (textId = R.string.title)
Display(text = movie.title)
Label(textId = R.string.description)
Display(text = movie.description)
}
}
}
CHANGED: app/src/main/java/com/androidbyexample/compose/movies/screens/MovieList.kt
package com.androidbyexample.compose.movies.screens
//import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Star
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.androidbyexample.compose.movies.Movie
import com.androidbyexample.compose.movies.R
import com.androidbyexample.compose.movies.components.Display
@OptIn(ExperimentalMaterial3Api::class) // for TopAppBar
@Composable
fun MovieListUi(
movies: List<Movie>,
modifier: Modifier = Modifier,
onMovieClicked: (Movie) -> Unit,
) {
Scaffold(
topBar = {
TopAppBar(
title = {
Text(text = stringResource(R.string.movies))
}
)
},
modifier = modifier,
) { innerPadding ->
// Text(
// text = "Movie List",
Column (
modifier = Modifier
.padding(innerPadding)
// .clickable {
// onMovieClicked(movies[0])
.fillMaxSize()
.verticalScroll(rememberScrollState())
) {
movies.forEach { movie ->
Card (
elevation = CardDefaults.cardElevation(
defaultElevation = 8.dp,
),
onClick = {
onMovieClicked(movie)
},
modifier = Modifier.padding(8.dp)
) {
Row (
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(8.dp)
) {
Icon(
imageVector = Icons.Default.Star,
contentDescription = stringResource(id = R.string.movie)
)
Display(text = movie.title)
}
}
}
}
// )
}
}
CHANGED: app/src/main/res/values/strings.xml
<resources>
<string name="app_name">Movies</string>
<string name="movies">Movies</string>
<string name="title">Title</string>
<string name="description">Description</string>
<string name="movie">Movie</string>
</resources>