1. 시작하기 전에
이 Codelab에서는 Jetpack Compose를 사용하여 앱에서 스크롤 가능한 목록을 만드는 방법을 알아봅니다.
하루를 긍정적으로 보낼 수 있도록 도와줄 멋진 이미지가 포함된 격언 문구 목록을 표시하는 Affirmations 앱을 만듭니다.
데이터가 이미 있습니다. 데이터를 가져와서 UI에 표시하기만 하면 됩니다.
기본 요건
- Kotlin의 목록에 관한 기본 지식
- Jetpack Compose로 레이아웃을 빌드해 본 경험
- 기기 또는 에뮬레이터에서 앱 실행 경험
학습 내용
- Jetpack Compose를 사용하여 머티리얼 디자인 카드를 만드는 방법
- Jetpack Compose를 사용하여 스크롤 가능한 목록을 만드는 방법
빌드할 항목
- 기존 애플리케이션을 가져와서 UI에 스크롤 가능한 목록을 추가합니다.
완성된 제품은 다음과 같습니다.
필요한 항목
- 인터넷 액세스가 가능하고 웹브라우저, Android 스튜디오가 있는 컴퓨터
- GitHub 액세스
시작 코드 다운로드하기
Android 스튜디오에서 basic-android-kotlin-compose-training-affirmations
폴더를 엽니다.
앱은 starter
브랜치 코드에서 빌드될 때 빈 화면을 표시해야 합니다.
2. 목록 항목 데이터 클래스 만들기
Affirmation을 위한 데이터 클래스 만들기
Android 앱에서는 목록이 목록 항목으로 구성됩니다. 단일 데이터의 경우 문자열 또는 정수와 같은 단순한 형태일 수 있습니다. 이미지, 텍스트 등 여러 데이터가 포함된 목록 항목의 경우 이러한 속성을 모두 포함하는 클래스가 필요합니다. 데이터 클래스는 속성만 포함하는 클래스의 한 유형으로, 이러한 속성을 사용하는 유틸리티 메서드를 제공할 수 있습니다.
- com.example.affirmations에서 새 패키지를 만듭니다.
새 패키지 이름을 model로 지정합니다. 모델 패키지에는 데이터 클래스로 표시되는 데이터 모델이 포함됩니다. 이 데이터 클래스는 문자열 리소스와 이미지 리소스로 구성된 'Affirmation'과 관련된 정보를 나타내는 속성으로 구성됩니다. 패키지는 클래스와 다른 디렉터리가 포함된 디렉터리입니다.
- com.example.affirmations.model 패키지에서 새 클래스를 만듭니다.
새 클래스의 이름을 Affirmation으로 지정하고 Data class로 만듭니다.
- 각
Affirmation
은 하나의 이미지와 하나의 문자열로 구성됩니다.Affirmation
데이터 클래스에서 두 개의val
속성을 만듭니다. 하나는stringResourceId
로, 다른 하나는imageResourceId
로 지정해야 합니다. 둘 다 정수여야 합니다.
Affirmation.kt
data class Affirmation(
val stringResourceId: Int,
val imageResourceId: Int
)
stringResourceId
속성에@StringRes
주석을 붙이고imageResourceId
에@DrawableRes
주석을 답니다.stringResourceId
는 문자열 리소스에 저장된 격언 텍스트의 ID를 나타냅니다.imageResourceId
는 드로어블 리소스에 저장된 격언 이미지의 ID를 나타냅니다.
Affirmation.kt
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
data class Affirmation(
@StringRes val stringResourceId: Int,
@DrawableRes val imageResourceId: Int
)
- com.example.affirmations.data 패키지에서 Datasource.kt 파일을 열고 두 개의 import 문과
Datasource
클래스의 콘텐츠를 주석 처리 삭제합니다.
Datasource.kt
import com.example.affirmations.R
import com.example.affirmations.model.Affirmation
class Datasource() {
fun loadAffirmations(): List<Affirmation> {
return listOf<Affirmation>(
Affirmation(R.string.affirmation1, R.drawable.image1),
Affirmation(R.string.affirmation2, R.drawable.image2),
Affirmation(R.string.affirmation3, R.drawable.image3),
Affirmation(R.string.affirmation4, R.drawable.image4),
Affirmation(R.string.affirmation5, R.drawable.image5),
Affirmation(R.string.affirmation6, R.drawable.image6),
Affirmation(R.string.affirmation7, R.drawable.image7),
Affirmation(R.string.affirmation8, R.drawable.image8),
Affirmation(R.string.affirmation9, R.drawable.image9),
Affirmation(R.string.affirmation10, R.drawable.image10))
}
}
3. 앱에 목록 추가
목록 항목 카드 만들기
이 앱은 격언 목록을 표시합니다. 목록을 표시하도록 UI를 구성하는 첫 번째 단계는 목록 항목을 만드는 것입니다. 각 격언 항목은 이미지와 문자열로 구성됩니다. 이러한 각 항목에 대한 데이터는 시작 코드와 함께 제공되며, 이 항목을 표시하기 위한 UI 구성요소를 만듭니다.
항목은 Image
와 Text
컴포저블을 포함하는 Card
컴포저블로 구성됩니다. Compose에서 Card
는 단일 컨테이너에 콘텐츠와 작업을 표시하는 노출 영역입니다. Affirmation 카드는 미리보기에서 다음과 같이 표시됩니다.
카드에는 이미지와 그 아래에 텍스트가 표시됩니다. 이 세로 레이아웃은 Card
컴포저블에 래핑된 Column
컴포저블을 사용하여 구현할 수 있습니다. 직접 해 보거나 아래 단계에 따라 이 작업을 할 수 있습니다.
- MainActivity.kt 파일을 엽니다.
AffirmationsApp()
메서드 아래에AffirmationCard()
라는 새 메서드를 만들고@Composable
주석을 추가합니다.
MainActivity.kt
@Composable
fun AffirmationsApp() {
}
@Composable
fun AffirmationCard() {
}
- 메서드 서명을 수정하여
Affirmation
객체를 매개변수로 사용합니다.Affirmation
객체는model
패키지에서 가져옵니다.
MainActivity.kt
import com.example.affirmations.model.Affirmation
@Composable
fun AffirmationCard(affirmation: Affirmation) {
}
- 서명에
modifier
매개변수를 추가합니다. 매개변수의 기본값을Modifier
로 설정합니다.
MainActivity.kt
@Composable
fun AffirmationCard(affirmation: Affirmation, modifier: Modifier = Modifier) {
}
AffirmationCard
메서드 내에서Card
컴포저블을 호출합니다.modifier
매개변수를 전달합니다.
MainActivity.kt
import androidx.compose.material3.Card
@Composable
fun AffirmationCard(affirmation: Affirmation, modifier: Modifier = Modifier) {
Card(modifier = modifier) {
}
}
Card
컴포저블 내에Column
컴포저블을 추가합니다.Column
컴포저블 내의 항목은 UI에서 세로로 정렬됩니다. 이렇게 하면 연관된 텍스트 위에 이미지를 배치할 수 있습니다. 반대로Row
컴포저블은 포함된 항목을 가로로 정렬합니다.
MainActivity.kt
import androidx.compose.foundation.layout.Column
@Composable
fun AffirmationCard(affirmation: Affirmation, modifier: Modifier = Modifier) {
Card(modifier = modifier) {
Column {
}
}
}
Column
컴포저블의 람다 본문 내에Image
컴포저블을 추가합니다.Image
컴포저블에는 항상 표시할 리소스가 있어야 하며contentDescription
가 있어야 합니다. 리소스는painter
매개변수에 전달된painterResource
여야 합니다.painterResource
메서드는 벡터 드로어블과 PNG와 같이 래스터화된 애셋 형식을 로드합니다. 또한contentDescription
매개변수의stringResource
를 전달합니다.
MainActivity.kt
import androidx.compose.foundation.Image
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
@Composable
fun AffirmationCard(affirmation: Affirmation, modifier: Modifier = Modifier) {
Card(modifier = modifier) {
Column {
Image(
painter = painterResource(affirmation.imageResourceId),
contentDescription = stringResource(affirmation.stringResourceId),
)
}
}
}
painter
및contentDescription
매개변수 외에도modifier
및contentScale
을 전달합니다.contentScale
은 이미지의 크기를 조절하고 표시하는 방법을 결정합니다.Modifier
객체의fillMaxWidth
속성 및 높이가194.dp
이어야 합니다.contentScale
은ContentScale.Crop
이어야 합니다.
MainActivity.kt
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.ui.unit.dp
import androidx.compose.ui.layout.ContentScale
@Composable
fun AffirmationCard(affirmation: Affirmation, modifier: Modifier = Modifier) {
Card(modifier = modifier) {
Column {
Image(
painter = painterResource(affirmation.imageResourceId),
contentDescription = stringResource(affirmation.stringResourceId),
modifier = Modifier
.fillMaxWidth()
.height(194.dp),
contentScale = ContentScale.Crop
)
}
}
}
Column
내부에서Image
컴포저블 뒤에Text
컴포저블을 만듭니다.affirmation.stringResourceId
의stringResource
를text
매개변수로 전달하고,padding
속성이16.dp
로 설정된Modifier
객체를 전달한 다음,style
매개변수에MaterialTheme.typography.headlineSmall
을 전달하여 텍스트 테마를 설정합니다.
MainActivity.kt
import androidx.compose.material3.Text
import androidx.compose.foundation.layout.padding
import androidx.compose.ui.platform.LocalContext
@Composable
fun AffirmationCard(affirmation: Affirmation, modifier: Modifier = Modifier) {
Card(modifier = modifier) {
Column {
Image(
painter = painterResource(affirmation.imageResourceId),
contentDescription = stringResource(affirmation.stringResourceId),
modifier = Modifier
.fillMaxWidth()
.height(194.dp),
contentScale = ContentScale.Crop
)
Text(
text = LocalContext.current.getString(affirmation.stringResourceId),
modifier = Modifier.padding(16.dp),
style = MaterialTheme.typography.headlineSmall
)
}
}
}
AffirmationCard 컴포저블 미리보기
카드는 Affirmations 앱의 UI 핵심이며 카드를 만들기 위해 열심히 노력했습니다. 카드가 제대로 표시되는지 확인하려면 전체 앱을 실행하지 않고 미리 볼 수 있는 컴포저블을 만들면 됩니다.
AffirmationCardPreview()
라는 비공개 메서드를 만듭니다. 메서드에@Preview
및@Composable
주석을 답니다.
MainActivity.kt
import androidx.compose.ui.tooling.preview.Preview
@Preview
@Composable
private fun AffirmationCardPreview() {
}
- 메서드 내에서
AffirmationCard
컴포저블을 호출하고 생성자에 전달된R.drawable.image1
드로어블 리소스와R.string.affirmation1
문자열 리소스로 새Affirmation
객체를 전달합니다.
MainActivity.kt
@Preview
@Composable
private fun AffirmationCardPreview() {
AffirmationCard(Affirmation(R.string.affirmation1, R.drawable.image1))
}
- Split 탭을 열면
AffirmationCard
의 미리보기가 표시됩니다. 필요한 경우 Design 창에서 Build & Refresh를 클릭하여 미리보기를 표시합니다.
목록 만들기
목록 항목 구성요소는 목록의 기본 요소입니다. 목록 항목이 생성되면 이를 활용하여 목록 구성요소 자체를 만들 수 있습니다.
AffirmationList()
라는 함수를 만들고@Composable
주석을 달고Affirmation
객체의List
를 메서드 서명에 매개변수로 선언합니다.
MainActivity.kt
@Composable
fun AffirmationList(affirmationList: List<Affirmation>) {
}
- 메서드 서명에서
modifier
을 매개변수로Modifier
로 선언합니다.
MainActivity.kt
@Composable
fun AffirmationList(affirmationList: List<Affirmation>, modifier: Modifier = Modifier) {
}
- Jetpack Compose에서는
LazyColumn
컴포저블을 사용하여 스크롤 가능한 목록을 만들 수 있습니다.LazyColumn
과Column
의 차이점은 Compose가 한 번에 모두 로드하므로 표시할 항목이 적은 경우Column
을 사용해야 한다는 것입니다.Column
은 사전 정의된 또는 고정된 개수의 컴포저블만 보유할 수 있습니다.LazyColumn
은 주문형 콘텐츠를 추가할 수 있어 긴 목록의 경우 특히 목록의 길이를 알 수 없을 때 유용합니다.LazyColumn
은 추가 코드 없이 기본적으로 스크롤도 제공합니다.AffirmationList()
함수 내부에서LazyColumn
컴포저블을 선언합니다.modifier
객체를LazyColumn
에 인수로 전달합니다.
MainActivity.kt
import androidx.compose.foundation.lazy.LazyColumn
@Composable
fun AffirmationList(affirmationList: List<Affirmation>, modifier: Modifier = Modifier) {
LazyColumn(modifier = modifier) {
}
}
LazyColumn
의 람다 본문에서items()
메서드를 호출하고affirmationList
를 전달합니다.items()
메서드는LazyColumn
에 항목을 추가하는 방법입니다. 이 메서드는 이 컴포저블만의 고유한 특징이며, 대부분의 컴포저블에서 일반적으로 사용되는 방식은 아닙니다.
MainActivity.kt
import androidx.compose.foundation.lazy.items
@Composable
fun AffirmationList(affirmationList: List<Affirmation>, modifier: Modifier = Modifier) {
LazyColumn(modifier = modifier) {
items(affirmationList) {
}
}
}
items()
메서드를 호출하려면 람다 함수가 필요합니다. 이 함수에서affirmationList
의 격언 항목을 나타내는affirmation
의 매개변수를 지정합니다.
MainActivity.kt
@Composable
fun AffirmationList(affirmationList: List<Affirmation>, modifier: Modifier = Modifier) {
LazyColumn(modifier = modifier) {
items(affirmationList) { affirmation ->
}
}
}
- 목록의 각 격언의 경우
AffirmationCard()
컴포저블을 호출합니다.affirmation
과padding
속성이8.dp
로 설정된Modifier
객체를 전달합니다.
MainActivity.kt
@Composable
fun AffirmationList(affirmationList: List<Affirmation>, modifier: Modifier = Modifier) {
LazyColumn(modifier = modifier) {
items(affirmationList) { affirmation ->
AffirmationCard(
affirmation = affirmation,
modifier = Modifier.padding(8.dp)
)
}
}
}
목록 표시
AffirmationsApp
컴포저블에서 현재 레이아웃 방향을 가져와 변수에 저장합니다. 이는 나중에 패딩을 구성하는 데 사용됩니다.
MainActivity.kt
import com.example.affirmations.data.Datasource
@Composable
fun AffirmationsApp() {
val layoutDirection = LocalLayoutDirection.current
}
- 이제
Surface
컴포저블을 만듭니다. 이 컴포저블은AffirmationsList
컴포저블의 패딩을 설정합니다.
MainActivity.kt
import com.example.affirmations.data.Datasource
@Composable
fun AffirmationsApp() {
val layoutDirection = LocalLayoutDirection.current
Surface() {
}
}
Modifier
를 상위 요소의 최대 너비와 높이를 채우고 상태 표시줄 패딩을 설정하며 시작 및 끝 패딩을layoutDirection
으로 설정하는Surface
컴포저블에 전달합니다. 다음은LayoutDirection
객체를 패딩으로 변환하는 방법을 보여주는 예입니다.WindowInsets.safeDrawing.asPaddingValues().calculateStartPadding(layoutDirection)
MainActivity.kt
import com.example.affirmations.data.Datasource
@Composable
fun AffirmationsApp() {
val layoutDirection = LocalLayoutDirection.current
Surface(
Modifier = Modifier
.fillMaxSize()
.statusBarsPadding()
.padding(
start = WindowInsets.safeDrawing.asPaddingValues()
.calculateStartPadding(layoutDirection),
end = WindowInsets.safeDrawing.asPaddingValues()
.calculateEndPadding(layoutDirection),
),
) {
}
}
Surface
컴포저블의 람다에서AffirmationList
컴포저블을 호출하고affirmationList
매개변수에DataSource().loadAffirmations()
를 전달합니다.
MainActivity.kt
import com.example.affirmations.data.Datasource
@Composable
fun AffirmationsApp() {
val layoutDirection = LocalLayoutDirection.current
Surface(
Modifier = Modifier
.fillMaxSize()
.statusBarsPadding()
.padding(
start = WindowInsets.safeDrawing.asPaddingValues()
.calculateStartPadding(layoutDirection),
end = WindowInsets.safeDrawing.asPaddingValues()
.calculateEndPadding(layoutDirection),
),
) {
AffirmationsList(
affirmationList = Datasource().loadAffirmations(),
)
}
}
기기 또는 에뮬레이터에서 Affirmations 앱을 실행하고 완성된 제품을 확인합니다.
4. 솔루션 코드 가져오기
완료된 Codelab의 코드를 다운로드하려면 다음 git 명령어를 사용하면 됩니다.
$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-affirmations.git $ cd basic-android-kotlin-compose-training-affirmations $ git checkout intermediate
또는 ZIP 파일로 저장소를 다운로드한 다음 압축을 풀고 Android 스튜디오에서 열어도 됩니다.
솔루션 코드를 보려면 GitHub에서 확인하세요.
5. 결론
지금까지 Jetpack Compose를 사용하여 카드, 목록 항목, 스크롤 가능한 목록을 만드는 방법을 알아보았습니다. 이러한 도구는 목록을 만드는 기본 도구일 뿐입니다. 창의성을 마음껏 발휘하고 목록 항목을 원하는 대로 맞춤설정할 수 있습니다.
요약
Card
컴포저블을 사용하여 목록 항목을 만듭니다.Card
컴포저블 내에 포함된 UI를 수정합니다.LazyColumn
컴포저블을 사용하여 스크롤 가능한 목록을 만듭니다.- 맞춤 목록 항목을 사용하여 목록을 만듭니다.