https://www.youtube.com/playlist?list=PLzZEuVaFb9Exi-pc8qtHBrrLg8bUn-TP6
Jetpack Compose - MVVM - Clean Architecture News App
"Building a Modern News App with Jetpack Compose, MVVM, and Clean Architecture." In this hands-on learning experience, you'll master the art of creating a fe...
www.youtube.com
상기에 기재된 강의를 보면서 정리한 글입니다.
SplashScreen 만들기
SplashScreen을 Dark/Light Theme 에 맞춰서 변환 및 구현해보기.
1. SplashScreen 을 실행하기 위해서 onCreate API 내에서 installSplashScreen() 을 실행해줘야 한다.
build.gradle 에는 다음과 같이 추가해준다.
//Splash Api
implementation "androidx.core:core-splashscreen:1.0.1"
2. SplashScreen의 Theme 속성은 다음과 같다.
<!-- Splash 화면의 백그라운드 컬러 지정 -->
<item name="android:windowSplashScreenBackground">@color/color</item>
<!-- Splash 화면의 중앙에 위치할 아이콘 Drawable 지정 -->
<item name="android:windowSplashScreenAnimatedIcon">@drawable/icon</item>
<!-- Splash 화면 중앙의 아이콘 주변 Background Color 지정 -->
<item name="android:windowSplashScreenIconBackgroundColor">@color/color</item>
<!-- Splash 화면이 나타나는 시간 지정 -->
<item name="android:windowSplashScreenAnimationDuration">duration_int</item>
<!-- Splash 화면 하단에 나타낼 브랜딩이미지 지정 (비권장) -->
<item name="android:windowSplashScreenBrandingImage">@drawable/branding_image</item>
3. Dark/Light 모드 두 개를 설정하기 위해서, res/values/splash 폴더를 생성하고, splash.xml 과 xml 생성 시 "night mode" 를 선택해서 splash.xml 을 생성해준다.
4. splash.xml 에는 다음과 같이 추가를 해준다.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="App.Starting.Theme" parent="Theme.SplashScreen">
<item name="windowSplashScreenBackground">#1C1E21</item>
<item name="windowSplashScreenAnimatedIcon">@drawable/ic_splash</item>
<item name="postSplashScreenTheme">@style/Theme.NewsApp</item>
</style>
</resources>
OnBoardingScreen 만들기
앱에 진입하면 나오는 세 개의 OnBoardingScreen 을 만들어본다.
화면에 보이는 부분을 presentation 으로 나눈다. 추후에 나오겠지만 ViewModel 등 UI 단을 몰아서 여기에 배치할 것이다, 그리고 그 안에서 공통적으로 사용할 부분은 common 패키지, 각 화면 별로 나눌 것이다. (OnBoarding Package > components, Deimens, OnBoardingScreen, Page). 이렇게 각각 나누는 것이 Clean Architecture 에 부합하기 때문이다.
OnBoardingScreen 페이지를 구성하기 위한 페이지는 Page.kt 에 별도로 data class 로 선언해서 관리하도록 하고, pages 배열 내에 Page instance 로 이루어지도록 한다.
data class Page(
val title:String,
val description:String,
@DrawableRes val image: Int
)
val pages = listOf(
Page(
title = "Test page 1st Dump title",
description = "Test page 1st Dump Description,Test page 1st Dump Description, Test page 1st Dump Description,",
image = R.drawable.onboarding1
),
Page(
title = "Test page 2nd Dump title",
description = "Test page 2nd Dump Description,Test page 2nd Dump Description, Test page 2nd Dump Description,",
image = R.drawable.onboarding2
),
Page(
title = "Test page 3rd Dump title",
description = "Test page 3rd Dump Description,Test page 3rd Dump Description, Test page 3rd Dump Description,",
image = R.drawable.onboarding3
)
)
R 클래스란?
자동으로 생성되는 클래스로서, resource의 id가 배정되는 클래스이다. 소스파일(.kt) 에서 리소스에 접근하고자 할 때, 사용하는 클래스이다. 현재 onboarding1,2,3 이미지가 res/drawable 내에 위치해있기 때문에 상기에 선언된 것처럼 접근할 수 있다.
image 변수에는 @DrawableRes 와 같이 Annotation을 사용하도록 한다.
components
components 폴더에는 OnBoardingScreen을 이루는 OnBoardingPage와 하단의 PageIndicator Compose를 선언해준다.
해당 Compose는 UI를 구성하는 화면이다.
OnBoardingPage.kt
fun OnBoardingPage(
modifier: Modifier = Modifier,
page: Page
) {
Column(modifier = Modifier) {
Image(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight(fraction = 0.6f),
painter = painterResource(id = page.image), contentDescription = null,
contentScale = ContentScale.Crop
)
Spacer(modifier = Modifier.height(MediumPadding1))
Text(
text = page.title,
modifier = Modifier.padding(horizontal = MediumPadding2),
style = MaterialTheme.typography.displaySmall.copy(fontWeight = FontWeight.Bold),
color = colorResource(id = R.color.display_small)
)
Text(
text = page.description,
modifier = Modifier.padding(horizontal = MediumPadding2),
style = MaterialTheme.typography.bodyMedium,
color = colorResource(id = R.color.text_medium)
)
}
}
1. Image Compose 에서의 Property (painter, contentScale) 을 참고하도록 하자.
2. Text Compose 에서의 color, style 어떻게 사용하는지 보고, 추후에 활용해보도록 하자.
@Preview Annotation 을 사용하면, Split 탭에서 화면을 미리 볼 수 있다는 건 기존에 알고 있었는데, 여기서 uiMode = UI_MODE_NIGHT_YES 옵션을 넣어주면, Dark Mode 일 때의 화면도 설정해줄 수 있다.
PageIndicator.kt
@Composable
fun PageIndicator(
modifier: Modifier = Modifier,
pageSize: Int,
selectedPage: Int,
selectedColor: Color = MaterialTheme.colorScheme.primary,
unselectedColor: Color = BlueGray
) {
Row(modifier = modifier, horizontalArrangement = Arrangement.SpaceBetween) {
repeat(pageSize) { page ->
Box(
modifier = Modifier
.size(IndicatorSize)
.clip(CircleShape)
.background(color = if (page == selectedPage) selectedColor else unselectedColor)
)
}
}
}
repeat 이란?
반복문이다. for문과 같다고 생각하면 되는데 여기서는 parameter로 받아온 pageSize 만큼 Box Compose를 그릴 것이다
Common
공통으로 사용하는 Button 과 같은 것들은 common 폴더에 별도로 Compose를 구현해주는 것이 좋다.
NewsButton.kt
@Composable
fun NewsButton(
text: String,
onclick: () -> Unit
) {
Button(
onClick = onclick,
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primary,
contentColor = Color.White
),
shape = RoundedCornerShape(size = 6.dp)
) {
Text(
text = text,
style = MaterialTheme.typography.labelMedium.copy(fontWeight = FontWeight.SemiBold)
)
}
}
OnBoardingScreen.kt
Column(modifier = Modifier.fillMaxSize()) {
val pagerState = rememberPagerState(initialPage = 0) {
pages.size
}
val buttonState = remember {
derivedStateOf {
when(pagerState.currentPage) {
0 -> listOf("", "Next")
1 -> listOf("Back", "Next")
2 -> listOf("Back", "Get Started")
else -> listOf("", "")
}
}
}
HorizontalPager(state = pagerState) {index ->
OnBoardingPage(page = pages[index])
}
}
rememberPagerState
HorizontalPager 내에서 page의 상태를 저장하는 PageState
derivedStateOf
글 참고 : https://medium.com/androiddevelopers/jetpack-compose-when-should-i-use-derivedstateof-63ce7954c11b
UI 를 업데이트하려는 것보다 state 또는 key가 더 많이 변경될 때 사용한다.
HorizontalPager
횡스크롤 형태를 사용하기 위해서 해당 Compose를 사용한다.
MainActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
installSplashScreen()
setContent {
NewsAppTheme {
Box(modifier = Modifier.background(color = MaterialTheme.colorScheme.background)) {
OnBoardingScreen()
}
}
}
}
#1 결과화면
'[ Jetpack Compose ]' 카테고리의 다른 글
News Application Clone #2 (0) | 2023.11.14 |
---|---|
Jetpack Compose Basic #4 (0) | 2023.11.07 |
Jetpack Compose Basic #3 (0) | 2023.11.02 |
Jetpack Compose Basic #2 (0) | 2023.10.30 |
Jetpack Compose Basic #1 (0) | 2023.10.30 |