다이어리 앱 4. Activity

woniwon·2024년 3월 18일
0

Android

목록 보기
15/19
post-thumbnail

Activity

DiaryActivity

  • 전체 코드
    class DiaryActivity : AppCompatActivity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
    
            super.onCreate(savedInstanceState)
            val diaryDao = DiaryDatabase.getDatabase(application).diaryDao()
            val repository = OfflineDiariesRepository(diaryDao)
            val viewModelFactory = DiaryViewModelFactory(repository)
            val diaryViewModel = ViewModelProvider(this, viewModelFactory)[DiaryViewModel::class.java]
    
            setContent{
                val navController = rememberNavController()
                //ViewModel 선언
                DiaryTheme {
                    Surface(
                        modifier = Modifier.fillMaxSize(),
                        color = MaterialTheme.colorScheme.background
                    ) {
                        SetupNavHost(navController = navController, viewModel = diaryViewModel)
                    }
                }
            }
        }
    }
    
    @Composable
    fun SetupNavHost(navController: NavHostController, viewModel: DiaryViewModel) {
        NavHost(
            navController = navController,
            startDestination = "list"
        ) {
            composable("list") {
                DiariesListScreen(
                    viewModel,
                    onNavigateToCreate = {
                        navController.navigate("create")
                    },
                    onDiaryClick = { diaryId ->
                        navController.navigate("detail/$diaryId")
                    }
                )
            }
            composable("create") {
                DiaryEntryForm(
                    navController,viewModel
                )
            }
            composable("detail/{id}",
                arguments = listOf(navArgument("id") { type = NavType.IntType }) // 인자 정의
            ) { backStackEntry ->
                // 인자 값을 가져와서 사용
                val id = backStackEntry.arguments?.getInt("id") ?: return@composable
                DiaryDetailScreen(navController,viewModel = viewModel, id = id,onNavigateToUpdate = { diaryId ->
                    navController.navigate("update/$diaryId")
                })
            }
            composable(
                "update/{id}",
                arguments = listOf(navArgument("id") { type = NavType.IntType })
            ) { backStackEntry ->
                val id = backStackEntry.arguments?.getInt("id") ?: return@composable
               DiaryUpdateForm(viewModel = viewModel, id = id)
            }
        }
    }
    
    @Composable
    fun CalendarPicker(onDateSelected: (Date) -> Unit) {
        val context = LocalContext.current
    
        // 현재 날짜를 가져옵니다.
        val today = Calendar.getInstance()
    
        // 캘린더 아이콘을 클릭하면 DatePickerDialog를 표시합니다.
        Icon(
            imageVector = Icons.Filled.DateRange,
            contentDescription = "Calendar",
            modifier = Modifier
                .size(40.dp)
                .clickable {
                    // DatePickerDialog 표시, 현재 날짜(today)를 초기값으로 설정합니다.
                    DatePickerDialog(
                        context,
                        { _, year, month, dayOfMonth ->
                            // 사용자가 날짜를 선택하면 콜백 함수를 호출합니다.
                            val selectedCalendar = Calendar
                                .getInstance()
                                .apply {
                                    set(year, month, dayOfMonth)
                                }
                            onDateSelected(selectedCalendar.time) // 새로운 날짜를 반환합니다.
                        },
                        today.get(Calendar.YEAR),
                        today.get(Calendar.MONTH),
                        today.get(Calendar.DAY_OF_MONTH)
                    ).show()
                }
        )
    }
    
    fun dateFormatter(selectedDate: String): LocalDateTime {
        // 선택된 날짜를 LocalDateTime으로 변환
        val date = LocalDate.parse(selectedDate, DateTimeFormatter.ISO_LOCAL_DATE)
        // LocalDate를 LocalDateTime으로 변환하면서 시간을 0시 00분으로 설정
        return LocalDateTime.of(date, LocalTime.MIDNIGHT)
    }
  • Single-Activity Architecture를 구현하기 위해 사용된다.
  • NavHost가 복잡해질 수 있기 때문에 따로 빼서 사용한다.
  • 함수를 인자로 전달할 수 있고 navHostController 를 인자로 전달받을 수 있는데 각각의 장 단점이 확실하고, 아직 Fragment 구성을 확실하게 하지 않아 두가지 방법 다 사용해 보았다.
    • 함수 인자 Vs navHostController 인자
      • 함수를 인자로 받는 경우

        • 장점: 컴포저블의 재사용성이 높아지며, 네비게이션 로직이 한 곳에 집중되지 않아 유연성이 증가합니다.
        • 단점: 네비게이션 로직이 여러 컴포저블에 분산될 수 있어, 전체적인 네비게이션 흐름을 파악하기 어려울 수 있습니다.
      • NavHostController를 직접 받는 경우
        - 장점: 네비게이션 로직이 컴포저블 내부에서 직접 처리되므로, 관련 로직을 한눈에 볼 수 있고 관리가 용이합니다.
        - 단점: 컴포저블의 재사용성이 다소 감소할 수 있으며, NavHostController에 대한 의존성이 커집니다.

        어느 것이 더 효과적인지는 앱의 구조와 사용 사례에 따라 달라질 수 있습니다. 만약 네비게이션 로직이 복잡하고 여러 컴포저블에서 비슷한 네비게이션 동작이 필요한 경우, NavHostController를 직접 인자로 받는 것이 좋을 수 있습니다. 반면, 컴포저블을 다양한 상황에서 재사용하고자 하거나, 네비게이션 로직을 외부에서 제어하고 싶은 경우에는 함수를 인자로 받는 방식이 더 적합할 수 있습니다.

SetupNavHost

@Composable
fun SetupNavHost(navController: NavHostController, viewModel: DiaryViewModel) {
    NavHost(
        navController = navController,
        startDestination = "list"
    ) {
        composable("list") {
            DiariesListScreen(
                viewModel,
                onNavigateToCreate = {
                    navController.navigate("create")
                },
                onDiaryClick = { diaryId ->
                    navController.navigate("detail/$diaryId")
                }
            )
        }
        composable("create") {
            DiaryEntryForm(
                navController,viewModel
            )
        }
        composable("detail/{id}",
            arguments = listOf(navArgument("id") { type = NavType.IntType }) // 인자 정의
        ) { backStackEntry ->
            // 인자 값을 가져와서 사용
            val id = backStackEntry.arguments?.getInt("id") ?: return@composable
            DiaryDetailScreen(navController,viewModel = viewModel, id = id,onNavigateToUpdate = { diaryId ->
                navController.navigate("update/$diaryId")
            })
        }
        composable(
            "update/{id}",
            arguments = listOf(navArgument("id") { type = NavType.IntType })
        ) { backStackEntry ->
            val id = backStackEntry.arguments?.getInt("id") ?: return@composable
           DiaryUpdateForm(viewModel = viewModel, id = id)
        }
    }
}
  • arguments 파라미터: 이 네비게이션 대상으로 전달되는 인자를 정의한다.
  • listOf(navArgument("id") { type = NavType.IntType })id라는 이름의 인자를 정의하며, 이 인자의 타입이 Int임을 나타냄
  • 람다 표현식 backStackEntry -> {...}: composable 함수에 대한 콜백으로, 네비게이션 대상이 활성화될 때 실행됨.
    • backStackEntry는 이 네비게이션 대상으로 이동할 때의 상태를 포함하는 객체로 이 객체를 통해 인자 값을 가져올 수 있다.
  • backStackEntry.arguments?.getInt("id") ?: return@composable:
    • backStackEntryarguments에서 "id" 인자의 값을 Int로 가져온다.
    • ?.는 안전 호출 연산자로, argumentsnull이 아닐 때만 getInt("id")를 호출
    • ?:는 엘비스 연산자로, 왼쪽 표현식의 결과가 null일 경우 오른쪽 값을 사용, 여기서는 null일 경우 composable 람다 함수에서 반환하여 더 이상 진행하지 않음

DiaryActivity 전체 코드 코멘트

  • 변수 선언
    • rememberNavController 함수가 Composable 함수이며, Compose UI의 컨텍스트에서 호출되어야 하기 때문에 setContent 안에서 호출 된다.
    • 이외의 다른 변수들은, onCreate의 최 상단에 한번만 호출해도 상관 없다.
  • setContent는 Compose UI 라이브러리를 사용하는 Android 애플리케이션에서 UI의 최상위 진입점을 정의한다.
    • Jetpack Compose를 사용하여 앱의 UI를 구성하고 있으며, setContent 내에서 UI를 구성하는 Composable 함수들을 호출한다.
profile
단순 기록용 Velog 입니다.

0개의 댓글