30 Days Of Habit App 만들기
위의 페이지를 참고하여 지금까지 배운 Scroll, Material Design, Animation을 한번에 실습해보자
리소스 준비
string.xml
<resources>
<string name="app_name">P30DaysOfMakingHabit</string>
<string name="desc1">You\'re now just beginner. Take easy. Taste little bit. Start with no pressure.</string>
<string name="desc2">You can make this job into continuous thing. But you can quit any time. It\'s okay. Don\'t be responsible and just enjoy.</string>
<string name="desc3">Many people quit in this moment. If you get through this time, you can easily go for a week.</string>
<string name="desc4">Now this job becomes your daily job. Maybe you like this job so far. Enjoy that satisfaction.</string>
<string name="desc5">You do this job for weekday. This means you do this job like your natural work. Can you do this in weekend? If you\'re not, just skip. It doesn't matter.</string>
<string name="desc6">Whether you do this in weekend or not, you start to consider this work important and worth the time.</string>
<string name="desc7">Many people quit in this moment. If you get through this time, you can easily go for a 2 weeks.</string>
<string name="desc8">Now you consider this job is your job. Don\'t be afraid. Just another week like you did before. Let the time do this job. Take easy.</string>
<string name="desc9">Maybe you do this job naturally now. Just let the time do this job. If you get stressed doing this job, take off your hands and make job more easier.</string>
<string name="desc10">Ten days. Regardless how do you think about this, it\'s huge thing for start something. You did great so far.</string>
<string name="desc11">Maybe you feels this job is not new and boring. All job is not always new and funny. Just do what did you do yesterday or do a little less than yesterday.</string>
<string name="desc12">If you doing this job for an hour in a day, you have done this job for a half of a day.</string>
<string name="desc13">Habit is your second nature and it breaks your first nature.</string>
<string name="desc14">Many people quit in this moment. If you get through this time, you can easily go for a month.</string>
<string name="desc15">Habit can be the best servant or the worst master.</string>
<string name="desc16">Excellence is not an act, but a habit.</string>
<string name="desc17">Once you made habit, then habit will make you.</string>
<string name="desc18">Everyone born in same, habit makes different.</string>
<string name="desc19">Habit change your life.</string>
<string name="desc20">Twenty days. now it\'s on the different orbit. You know what? it\'s the maximum you can count using your fingers and toes.</string>
<string name="desc21">It\'s been 3 weeks from start. It means you did this for at least 3 periods. Yeah, you\'re now used to it.</string>
<string name="desc22">In the first half of life, you can make habit. In another half of life, you can\'t. You just live in habit.</string>
<string name="desc23">Habit grows bigger like tree ring.</string>
<string name="desc24">If you doing this job for an hour in a day, you have done this job for a pure day.</string>
<string name="desc25">If you do this job just for weekday, you did this for 5 weeks. If you this job for money, today is your payday.</string>
<string name="desc26">The best way to get rid of bad habit is not to start.</string>
<string name="desc27">We are what we repeatedly do.</string>
<string name="desc28">Habit is the best guidance in life.</string>
<string name="desc29">Making good habit is harder than bad habit. However giving up good habit is easier than bad habit.</string>
<string name="desc30">Now this job is your habit. Congratulations!</string>
</resources>
Day.kt
package com.example.p30daysofmakinghabit
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
data class Day(val num: Int, @DrawableRes val imageId: Int, @StringRes val stringId: Int)
object DayData{
val days = listOf(
Day(1, R.drawable.image1, R.string.desc1),
Day(2, R.drawable.image2, R.string.desc2),
Day(3, R.drawable.image3, R.string.desc3),
Day(4, R.drawable.image4, R.string.desc4),
Day(5, R.drawable.image5, R.string.desc5),
Day(6, R.drawable.image6, R.string.desc6),
Day(7, R.drawable.image7, R.string.desc7),
Day(8, R.drawable.image8, R.string.desc8),
Day(9, R.drawable.image9, R.string.desc9),
Day(10, R.drawable.image10, R.string.desc10),
Day(11, R.drawable.image11, R.string.desc11),
Day(12, R.drawable.image12, R.string.desc12),
Day(13, R.drawable.image13, R.string.desc13),
Day(14, R.drawable.image14, R.string.desc14),
Day(15, R.drawable.image15, R.string.desc15),
Day(16, R.drawable.image16, R.string.desc16),
Day(17, R.drawable.image17, R.string.desc17),
Day(18, R.drawable.image18, R.string.desc18),
Day(19, R.drawable.image19, R.string.desc19),
Day(20, R.drawable.image20, R.string.desc20),
Day(21, R.drawable.image21, R.string.desc21),
Day(22, R.drawable.image22, R.string.desc22),
Day(23, R.drawable.image23, R.string.desc23),
Day(24, R.drawable.image24, R.string.desc24),
Day(25, R.drawable.image25, R.string.desc25),
Day(26, R.drawable.image26, R.string.desc26),
Day(27, R.drawable.image27, R.string.desc27),
Day(28, R.drawable.image28, R.string.desc28),
Day(29, R.drawable.image29, R.string.desc29),
Day(30, R.drawable.image30, R.string.desc30),
)
}
drawbles
코드 작성하기
MainActivity.kt
@Composable
fun DayCard(day: Day, modifier: Modifier = Modifier) {
Card(
modifier = modifier,
elevation = 3.dp
){
Column(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp),
horizontalAlignment = Alignment.CenterHorizontally
){
Text(
text = "Day " + day.num,
style = MaterialTheme.typography.h2,
modifier = Modifier
.fillMaxWidth(),
textAlign = TextAlign.Left
)
Spacer(Modifier.height(16.dp))
Image(
painter = painterResource(day.imageId),
contentDescription = null,
modifier = Modifier
.width(400.dp)
.height(300.dp)
)
Text(
text = stringResource(day.stringId),
style = MaterialTheme.typography.body1
)
}
}
}
@Composable
fun DayCardColumn(modifier: Modifier = Modifier){
LazyColumn(modifier = modifier){
items(DayData.days){ day ->
DayCard(day,
Modifier
.padding(
horizontal = 8.dp,
vertical = 4.dp
)
)
}
}
}
테마 설정하기
Color.kt
package com.example.p30daysofmakinghabit.ui.theme
import androidx.compose.ui.graphics.Color
val light_primary = Color(0xFFff9600)
val light_secondary = Color(0xFFffdd00)
val light_surface = Color(0xFFfcfcfc)
val light_background = Color(0xFFFFFFFF)
val light_onprimary = Color(0xFF000000)
val light_onsecondary = Color(0xFF000000)
val light_onsurface = Color(0xFF4a4a4a)
val light_onbackground = Color(0xFF979797)
val dark_primary = Color(0xFFb96d00)
val dark_secondary = Color(0xFF887600)
val dark_surface = Color(0xFF696969)
val dark_background = Color(0xFF858585)
val dark_onprimary = Color(0xFFffffff)
val dark_onsecondary = Color(0xFFffffff)
val dark_onsurface = Color(0xFFf5f5f5)
val dark_onbackground = Color(0xFFececec)
Shape.kt
package com.example.p30daysofmakinghabit.ui.theme
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Shapes
import androidx.compose.ui.unit.dp
val Shapes = Shapes(
small = RoundedCornerShape(8.dp),
medium = RoundedCornerShape(8.dp),
large = RoundedCornerShape(0.dp)
)
Type.kt
package com.example.p30daysofmakinghabit.ui.theme
import android.speech.tts.TextToSpeechService
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
import com.example.p30daysofmakinghabit.R
val Signikanegative = FontFamily(
Font(R.font.signikanegative_regular, FontWeight.Normal),
Font(R.font.signikanegative_bold, FontWeight.Bold)
)
// Set of Material typography styles to start with
val Typography = Typography(
h1 = TextStyle(
fontFamily = Signikanegative,
fontWeight = FontWeight.Bold,
fontSize = 30.sp
),
h2 = TextStyle(
fontFamily = Signikanegative,
fontWeight = FontWeight.Bold,
fontSize = 26.sp
),
body1 = TextStyle(
fontFamily = Signikanegative,
fontWeight = FontWeight.Normal,
fontSize = 20.sp
)
)
Theme.kt
package com.example.p30daysofmakinghabit.ui.theme
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material.MaterialTheme
import androidx.compose.material.darkColors
import androidx.compose.material.lightColors
import androidx.compose.runtime.Composable
private val DarkColorPalette = darkColors(
primary = dark_primary,
onPrimary = dark_onprimary,
secondary = dark_secondary,
onSecondary = dark_onsecondary,
surface = dark_surface,
onSurface = dark_onsurface,
background = dark_background
)
private val LightColorPalette = lightColors(
primary = light_primary,
onPrimary = light_onprimary,
secondary = light_secondary,
onSecondary = light_secondary,
surface = light_surface,
onSurface = light_onsurface,
background = light_background,
onBackground = light_onbackground
)
@Composable
fun P30DaysOfMakingHabitTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
val colors = if (darkTheme) {
DarkColorPalette
} else {
LightColorPalette
}
MaterialTheme(
colors = colors,
typography = Typography,
shapes = Shapes,
content = content
)
}
color.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="light_status">#ff9600</color>
<color name="dark_status">#b96d00</color>
</resources>
themes.xml, themes.xml (v23)
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.P30DaysOfMakingHabit" parent="android:Theme.Material.Light.NoActionBar">
<item name="android:statusBarColor">@color/light_status</item>
<item name="android:windowLightStatusBar">true</item>
</style>
</resources>
themes.xml (night)
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.P30DaysOfMakingHabit" parent="android:Theme.Material.NoActionBar">
<item name="android:statusBarColor">@color/dark_status</item>
</style>
</resources>
애니메이션 추가, TopAppBar 추가
MainActivity.kt
package com.example.p30daysofmakinghabit
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.animation.animateColorAsState
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.example.p30daysofmakinghabit.ui.theme.P30DaysOfMakingHabitTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
P30DaysOfMakingHabitTheme {
// A surface container using the 'background' color from the theme
DayHabitApp()
}
}
}
}
@Composable
fun DayHabitApp(modifier: Modifier = Modifier){
Scaffold(
topBar = { DayHabitTopAppBar() }
) {
DayCardColumn()
}
}
@Composable
fun DayHabitTopAppBar(modifier: Modifier = Modifier){
Row(
modifier = modifier
.fillMaxWidth()
.height(100.dp)
.background(MaterialTheme.colors.primary),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center
){
Text(
text = "30 Days of Making Habit",
style = MaterialTheme.typography.h1,
color = MaterialTheme.colors.onPrimary
)
}
}
@Composable
fun DayCard(day: Day, modifier: Modifier = Modifier) {
var isClicked by remember { mutableStateOf(false) }
val changedSurfaceColor by animateColorAsState(
targetValue = if(isClicked) MaterialTheme.colors.primary else MaterialTheme.colors.surface
)
val changedTextColor by animateColorAsState(
targetValue = if(isClicked) MaterialTheme.colors.onPrimary else MaterialTheme.colors.onSurface
)
Card(
modifier = modifier
.clickable { isClicked = !isClicked },
elevation = 3.dp,
){
Column(
modifier = Modifier
.background(changedSurfaceColor)
.width(500.dp)
.padding(
start = 16.dp,
end = 16.dp,
top = 8.dp,
bottom = 32.dp
)
,
horizontalAlignment = Alignment.CenterHorizontally
){
Text(
text = "Day " + day.num,
style = MaterialTheme.typography.h2,
modifier = Modifier
.fillMaxWidth(),
textAlign = TextAlign.Left,
color = changedTextColor
)
Spacer(Modifier.height(20.dp))
if(isClicked == false) {
Image(
painter = painterResource(day.imageId),
contentDescription = null,
modifier = Modifier
.height(200.dp)
)
} else {
Text(
text = stringResource(day.stringId),
style = MaterialTheme.typography.body1,
modifier = Modifier
.width(400.dp)
.height(200.dp),
color = changedTextColor
)
}
}
}
}
@Composable
fun DayCardColumn(modifier: Modifier = Modifier){
LazyColumn(
modifier = modifier
.fillMaxWidth()
.background(MaterialTheme.colors.background),
horizontalAlignment = Alignment.CenterHorizontally
){
items(DayData.days){ day ->
DayCard(day,
Modifier
.padding(
horizontal = 16.dp,
vertical = 16.dp
)
)
}
}
}
@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
P30DaysOfMakingHabitTheme {
DayHabitTopAppBar()
}
}
@Preview(showBackground = true)
@Composable
fun DefaultPreview1() {
P30DaysOfMakingHabitTheme {
DayHabitApp()
}
}
결과화면
'기타 > 안드로이드' 카테고리의 다른 글
33. Unscramble App : App architecture와 ViewModel (2) | 2022.11.20 |
---|---|
32. Dessert Clicker App : Activity Life Cycle (2) | 2022.11.15 |
30. SuperHero App: Material Design, App Icon 복습 (0) | 2022.11.06 |
29. 앱의 접근성 개선 (0) | 2022.11.06 |
28. Woof App : 애니메이션, icon 추가 (2) | 2022.10.29 |