[Android - Kotlin] Jetpack Compose - 6

민채·2024년 2월 20일
0

Android - Codelab

목록 보기
6/10

활동 수명 주기 단계

이전에 정리했던 생명주기 글을 보면 됨!

https://velog.io/@minchae75/Android-Lifecycle-생명주기-p1jkr4f5

컴포저블의 수명 주기

initial Composition : 처음 호출된 Composable 상태
Recomposition : 상태가 변경됨에 따라 UI 가 다시 그려지는 상태
Decomposition : Composable 이 파괴 되는 상태`

  • 컴포저블에는 활동 수명 주기와 별개인 자체 수명 주기가 있음
  • 수명 주기는 컴포지션을 시작하고 0회 이상 재구성한 다음 컴포지션을 종료하는 이벤트로 구성
  • 앱의 UI는 처음에 컴포지션이라는 프로세스에서 구성 가능한 함수를 실행하여 빌드됨
  • 앱 상태가 변경되면 리컴포지션이 예약
    • Recomposition은 상태가 변경되었을 수 있는 구성 가능한 함수를 Compose에서 다시 실행하고 업데이트된 UI를 만드는 것을 의미 -> 컴포지션은 이러한 변경사항을 반영하도록 업데이트됨
  • Compose가 Recomposition을 추적하고 트리거하려면 상태가 변경된 시점을 알아야 함
    • 객체의 유형이 State 또는 MutableState이면 객체의 상태를 추적할 수 있음
      • State : 변경할 수 없으며 읽기만 가능
      • MutableState : 변경 가능하며 읽기 및 쓰기를 허용

rememberSaveable

  • 기존에 Recomposition이 일어날 때 변경된 상태를 저장하기 위해(값을 유지하고 재사용하도록) remember를 사용
  • remember은 구성 변경 중에는 이 상태를 유지하지 않음
    • ex) 가로, 세로 전환 -> Activity 가 새로 그려지기 때문에 새로운 상태가 생성
  • 구성 변경 중에 상태를 유지하기 위해 rememberSaveable을 사용

앱 아키텍처

https://velog.io/@minchae75/Android-안드로이드-앱-아키텍처-App-Architecture

Compose의 ViewModel 및 상태

  • ViewModel을 사용하여 기기 구성 변경 시에도 UI 데이터를 유지할 수 있음
  • 이번 앱에서는 ViewModel을 이용해 게임 UI 상태(글자가 섞인 단어, 단어 수, 점수)를 저장

StateFlow

  • StateFlow는 현재 상태와 새 상태 업데이트를 수집기에 내보내는 관찰 가능한 상태 홀더 흐름(Flow)
  • StateFlow의 value 속성은 현재 상태 값을 반영
  • 상태를 업데이트하고 흐름에 전송하려면 MutableStateFlow 클래스의 value 속성에 새 값을 할당
  • 관찰 가능한 불변 상태를 유지해야 하는 클래스에 적합
  • 컴포저블이 UI 상태 업데이트할 수 있게 하고 구성 변경에도 화면 상태가 지속되게 하기 위해 사용
  • asStateFlow() : 변경 가능 상태 흐름을 읽기 전용 상태 흐름으로 만듦

사용법

builde.gradle에 추가

implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1")

ViewModel 클래스 추가

  • ui 패키지에 GameViewModel 클래스 추가
  • ui 패키지에서 GameUiState라는 상태 UI의 모델 클래스를 추가
/* GameViewModel.kt */
class GameViewModel : ViewModel() { // ViewModel 클래스 확장해서 사용

    // Game UI state, StateFlow 사용
    private val _uiState = MutableStateFlow(GameUiState()) // ViewModel 내부에서만 값에 접근 가능
    val uiState: StateFlow<GameUiState> = _uiState.asStateFlow() // asStateFlow()는 이 변경 가능 상태 흐름을 읽기 전용 상태 흐름으로 만듦

    // 임의의 단어를 선택하고 단어의 글자를 섞는 도우미 메서드를 추가

    // Set of words used in the game
    private var usedWords: MutableSet<String> = mutableSetOf()

    private lateinit var currentWord: String // 글자가 섞인 현재 단어를 저장

    init {
        resetGame()
    }

    // 목록에서 임의 단어를 선택하여 글자를 섞는 메서드
    private fun pickRandomWordAndShuffle(): String {
        // Continue picking up a new random word until you get one that hasn't been used before
        currentWord = allWords.random()

        if (usedWords.contains(currentWord)) {
            return pickRandomWordAndShuffle()
        } else {
            usedWords.add(currentWord)
            return shuffleCurrentWord(currentWord)
        }
    }

    // String을 받아서 순서가 섞인 String을 반환하여 현재 단어의 순서를 섞는 도우미 메서
    private fun shuffleCurrentWord(word: String): String {
        val tempWord = word.toCharArray()

        // Scramble the word
        tempWord.shuffle()

        while (String(tempWord).equals(word)) {
            tempWord.shuffle()
        }

        return String(tempWord)
    }

    // 게임 초기화
    // usedWords 세트에 있는 모든 단어를 지우고 _uiState를 초기화
    // pickRandomWordAndShuffle()을 사용하여 currentScrambledWord의 새 단어를 선택
    fun resetGame() {
        usedWords.clear()
        _uiState.value = GameUiState(currentScrambledWord = pickRandomWordAndShuffle())
    }

}

/* GameUiState.kt */
data class GameUiState(
    val currentScrambledWord: String = "" // 글자가 섞인 현재 단어를 위한 변수를
)

데이터 전달하기

  • GameScreen.kt 파일에서 ViewModel 인스턴스를 UI로(즉, GameViewModel에서 GameScreen()으로) 전달
  • GameScreen()에서 collectAsState()를 통해 ViewModel 인스턴스를 사용하여 uiState에 접근
  • collectAsState() : StateFlow에서 값을 수집하고 State를 통해 최신 값을 나타냄
  • StateFlow.value는 초깃값으로 사용
    • StateFlow에 새 값이 게시될 때마다 반환된 State가 업데이트되어 State.value가 사용된 모든 경우에서 재구성이 이루어짐
/* GameScreen 컴포저블 */
@Composable
fun GameScreen(
    gameViewModel: GameViewModel = viewModel()
) {

    // uiState 값이 변경될 때마다 gameUiState 값을 사용하여 구성 가능한 함수가 재구성되도록 함
    val gameUiState by gameViewModel.uiState.collectAsState()
}

GameLayout(
   currentScrambledWord = gameUiState.currentScrambledWord,
   modifier = Modifier
       .fillMaxWidth()
       .wrapContentHeight()
       .padding(mediumPadding)
)

/* GameLayout 컴포저블 */
@Composable
fun GameLayout(
   currentScrambledWord: String, // 인수 추가
   modifier: Modifier = Modifier
) {
	Column(
       verticalArrangement = Arrangement.spacedBy(24.dp)
   	) {
    //...
       Text(
       		text = currentScrambledWord,
            style = typography.displayMedium
       )
    //...
    }
}

여기까지 하고 앱을 실행 하면 글자가 섞인 단어가 표시됨

전체 코드

https://github.com/MinchaeKwon/AndroidCompose/tree/master/Chapter4/Unscramble

실행 화면

참조

Compose Codelab - Compose 아키텍처 구성요소 (4단원)

profile
코딩계의 떠오르는 태양☀️

0개의 댓글