데이터 베이스 설정한 파일에 graph 사용할 것임
package com.lululalal.wishlist.data
import androidx.room.Database
import androidx.room.RoomDatabase
//데이터베이스 설정
@Database(
entities = [Wish::class], //데이터 베이스 내부에 필요한 entity - 지금은 하나지만 여러개가 될 수 있음
version = 1, //
exportSchema = false //
)
abstract class WishDatabase : RoomDatabase() { //사용하고 있는 데이터베이스
abstract fun WishDao() : WishDao //WishDao에 작성한 것에 접근 가능하도록 함
}
*** 그래프에서 할 일
- lateinit 키워드 사용해서 데이터베이스 선언(어떤 데이터 베이스인지)
- wishEwpository에 데이터 베이스 넘겨주기
-> lazy 키워드를 통해 처음 사용할 때 초기화 되도록 선언 : 리소스 아껴줌- databaseBuilder만들기
-> 1에 선언한 데이터베이스에 인스턴스 만들어 할당
databaseBuilder?
- 데이터베이스의 인스턴스 생성
Room.databaseBuilder(Context, 데이터베이스에 사용할 클래스 : @Database 어노테이션을 달고 있고 RoomDatabase()를 상속 받고 있는 클래스 파일, 데이터베이스 이름 설정).build()
package com.lululalal.wishlist
import android.content.Context
import androidx.room.Room
import com.lululalal.wishlist.data.WishDatabase
import com.lululalal.wishlist.data.WishRepository
object Graph {
//그래프에서 필요한거
//1. lateinit 키워드 사용해서 데이터베이스 선언
lateinit var database: WishDatabase
//사용전에 초기화되도록 약속된 non-nullable한 데이터베이스 선언
//2. wishRepository에 데이터 베이스 넘겨주기
//lazy 키워드를 통해 처음 사용할 때 초기화 되도록 선언 -> 리소스 아껴줌
val wishRepository by lazy {
WishRepository(wishDao = database.wishDao())
}
//3. databaseBuilder만들기
//데이터베이스의 인스턴스 생성 -> 인스턴스는 lateinit var database: WishDatabase에 할당됨
//provide함수 : 호출 시 데이터베이스 초기화
fun provide(context: Context) {
database = Room.databaseBuilder(context,WishDatabase::class.java, "wishList.db").build()
// @JvmStatic
// public final fun <T : RoomDatabase> databaseBuilder(
// context: Context,
// klass: Class<T>,
// name: String?
// ): RoomDatabase. Builder<T>
// Creates a RoomDatabase. Builder for a persistent database. Once a database is built, you should keep a reference to it and re-use it.
// Params:
// context - The context for the database. This is usually the Application context.
// klass - The abstract class which is annotated with Database and extends RoomDatabase.
// name - The name of the database file.
// T - The type of the database class.
// Returns:
// A RoomDatabaseBuilder<T> which you can use to create the database.
}
}
by lazy?
변수 초기화가 필요할 때에만 사용되도록함
즉 애플리케이션을 연 순간 모든 것을 처음부터 load하지않고 특정 시점에만 함
기본적으로 thread safety함 : 다양한 스레드의 접근을 받을 떄에만 초기화한다는 뜻
dependency injection(종속 항목 삽입)
- 동일하게 코드 작업을 수행하기 위해 특정 정보 혹은 도구 필요, 동작할 때 필요한 것을 따로 찾지 않아도 찾아다 주는 헬퍼
- 앱의 변경 사항을 더 쉽게 관리할 수 있도록 도와줌(수정사항을 찾기 위해 앱 전체를 보지 않아도 됨)
package com.lululalal.wishlist
import android.app.Application
class WishListApp : Application() {
override fun onCreate() {
super.onCreate()
Graph.provide(this)
}
}
만들어둔 듀 모델 매개변수에 초기상태로 Graph에 작성해둔 리포지포리 넣어주기
class WishViewModel(
//pass로 전달하는 WishRepository를 따로 설정할 필요가 없도록 초기 상태를 설정
private val wishRepository: WishRepository = Graph.wishRepository
):ViewModel() {
내부 코드는 1에 있음
}
스낵바 사용해보기
1. 스낵바 변수 선언val snackMessage = remember { mutableStateOf("") }
val scope = rememberCoroutineScope()
val ScaffoldState = rememberScaffoldState()
Scaffold( // AppBar의 onBackNavClicked()은 Scaffold에 작성
scaffoldState = scaffoldState, //Scaffold에 상태 유지용 매개변수로 넣기
topBar = { AppBarView(title =
if (id != 0L) stringResource(id = R.string.update_wish)
else stringResource(id = R.string.add_wish)
)
{
navController.navigateUp()
// navigateUp() => HomeView로 돌아감
// 사용자를 이전에 있던 화면으로 돌아가게 하는 것을 의도
}
}
) {
...
}
Button(onClick =
{ /*데이터 데이스 Room에 저장하기*/
if (wishViewModel.wishTitleState.isNotEmpty()
&& wishViewModel.wishDescriptionState.isNotEmpty()) {
if (id != 0L) {
//TODO 기존 데이터 업데이트
} else {
//TODO 데이터 추가
wishViewModel.addWish(
Wish(
title = wishViewModel.wishTitleState.trim(),
description = wishViewModel.wishDescriptionState.trim()
)
)
snackMessage.value = "등록 완료"
}
} else {
//TODO 필드 작성 & wish 항목 생성
snackMessage.value = "항목 생성을 위해 필드를 작성하세요"
}
//생성 후 페이지 이동
scope.launch {
scaffoldState.snackbarHostState.showSnackbar(snackMessage.value)
navController.navigateUp()
}
},
modifier = Modifier.fillMaxWidth()) {
//버튼 텍스트를 표시하되 뭔가를 받아왔는지에 따라 달라질것
Text(
text =
if (id != 0L) stringResource(id = R.string.update_wish)
else stringResource(id = R.string.add_wish),
style = TextStyle(
fontSize = 18.sp
)
)
}
HomeView.kt 파일에 list 만드는 부분인 LazyColumn 수정하기
fun HomeView(
navController: NavController,
viewModel: WishViewModel
) {
val context = LocalContext.current
Scaffold(
topBar = { AppBarView("WishList", {
}) },
floatingActionButton = {
FloatingActionButton(
modifier = Modifier.padding(all=20.dp),
contentColor = Color.White,
backgroundColor = Color.Black,
onClick = {
navController.navigate(Screen.AddScreen.route)
}) {
Icon(imageVector = Icons.Default.Add, contentDescription = "추가")
}
}
) {
//위시 리스트를 가져오기
val wishList = viewModel.getAllWishes.collectAsState(initial = listOf()) //빈 목록으로 초기화
LazyColumn(modifier = Modifier
.fillMaxSize()
.padding(it)){
items(wishList.value) {
wish -> WishItem(wish = wish) {
}
}
}
}
}
1) HomeView.kt
floating 버튼 클릭할 땐 "+/0L" 붙여서 보내고 리스트 클릭할 땐 "+/id" 로 보내서 항상성 유지
fun HomeView(
navController: NavController,
viewModel: WishViewModel
) {
val context = LocalContext.current
Scaffold(
topBar = { AppBarView("WishList", {
}) },
floatingActionButton = {
FloatingActionButton(
modifier = Modifier.padding(all=20.dp),
contentColor = Color.White,
backgroundColor = Color.Black,
onClick = {
navController.navigate(Screen.AddScreen.route+"/0L")
}) {
Icon(imageVector = Icons.Default.Add, contentDescription = "추가")
}
}
) {
//위시 리스트를 가져오기
val wishList = viewModel.getAllWishes.collectAsState(initial = listOf()) //빈 목록으로 초기화
LazyColumn(modifier = Modifier
.fillMaxSize()
.padding(it)){
items(wishList.value) {
wish -> WishItem(wish = wish) {
val id = wish.id
navController.navigate(Screen.AddScreen.route + "/$id")
}
}
}
}
}
2) Navigation.kt
NavHost 안 화면이 될 composable route수정 및 arguments 추가
get방식으로 보내지는 주소로 변경 및 쿼리 변수의 속성 지정
NavHost(
navController = navController,
startDestination = Screen.HomeScreen.route
) {
//화면이 될 composable 추가
composable(Screen.HomeScreen.route) {
HomeView(navController, viewModel)
}
composable(Screen.AddScreen.route+"/{id}",
arguments = listOf(
navArgument("id") {
type = NavType.LongType
defaultValue = 0L
nullable = false
}
)
) { entry ->
val id = if(entry.arguments != null) entry.arguments!!.getLong("id") else 0L
AddEditDetailView(id = id, wishViewModel = viewModel, navController = navController)
}
}
3) AddEditDetailView.kt
id != 0L일 때 기존 데이터 보여주고 수정 하기
3-1. fun AddEditDetailView에 id로 wish 가져오는 경우와 0L로 가져오는 경우 viewModel의 값 셋팅하는 부분 추가
//wishViewModel 셋팅하는 부분 추가
if (id != 0L) {
val wish = wishViewModel.getByWishId(id).collectAsState(initial = Wish(0L,"","")) //초기값 설정
wishViewModel.wishTitleState = wish.value.title
wishViewModel.wishDescriptionState = wish.value.description
} else {
wishViewModel.wishTitleState = ""
wishViewModel.wishDescriptionState = ""
}
3-2. 데이터 엡데이트 부분 수정
wishViewModel에작성되어 있는 updateWish을 사용하여 업데이트
Button(onClick =
{ /*데이터 데이스 Room에 저장하기*/
if (wishViewModel.wishTitleState.isNotEmpty()
&& wishViewModel.wishDescriptionState.isNotEmpty()) {
if (id != 0L) {
//TODO 기존 데이터 업데이트
wishViewModel.updateWish(
Wish(
id = id,
title = wishViewModel.wishTitleState.trim(),
description = wishViewModel.wishDescriptionState.trim()
)
)
} else {
1) 스와이프 이용한 데이터 삭제
LazyColumn(modifier = Modifier
.fillMaxSize()
.padding(it)){
//items에 key 설정하면 원하는 item이 삭제가 됨
items(wishList.value, key={wish->wish.id}) {
wish ->
//스와이프 삭제
//삭제를 위한 코드
val dismissState = rememberDismissState(
confirmStateChange = {
if(it == DismissValue.DismissedToEnd
|| it == DismissValue.DismissedToStart) {
viewModel.deleteWish(wish)
}
true //상태변화 확인
}
)
SwipeToDismiss(
state = dismissState,
background = {
//val => state에서 작동하기 위해 변수 var 아닌 val로 선언
val color by animateColorAsState(
if (dismissState.dismissDirection == DismissDirection.EndToStart) Color.Red else Color.Transparent,
label = ""
)
val alignment = Alignment.CenterEnd
Box(
modifier = Modifier
.fillMaxSize()
.background(color)
.padding(horizontal = 20.dp),
contentAlignment = alignment //contentAlignment는 Box에 {}가 있어야만 됨
) {
Icon(Icons.Default.Delete,
contentDescription = "삭제",
tint = Color.White)
}
},
//방향 - 왼쪽에서부터 쓸지 오늘쪽에서부터 쓸지 정하는 것
directions = setOf(DismissDirection.EndToStart),
//어느 시점에서 dismiss 할 지
dismissThresholds = {FractionalThreshold(1f)},
//쓸어 넘긴 후 삭제할 내용
dismissContent = {
WishItem(wish = wish) {
val id = wish.id
navController.navigate(Screen.AddScreen.route + "/$id")
}
}
)
}
}
}
cf) 참고
https://tutorials.eu/storing-data-permanently-part-2-2-day-14-android-14-masterclass/