해당 게시글은 코드랩 예제를 참고하였습니다.
먼저 step1의 경우 chronometer를 그대로 실행하여 타이머를 구성하였기에 따로 바꿀 필요 없이
앱 코드만 다운로드해서 실행만 하면 됩니다:) (밑에는 샘플코드를 다운로드 할 수 있는 링크입니다.)
https://developer.android.com/codelabs/android-lifecycles?hl=ko#1
3. Step 2 - Add a ViewModel
해당 예제에서는 chronometer 뷰 컴포넌트를 이용하여 타이머를 만들었는데, ViewModel을 사용하면 화면이 회전도기너, 액티비티가 소멸되어도 타이머가 리셋되지 않는다고 나와있습니다 :)
전체 소스코드를 먼저 봐보죠!! :)
[build.gradle(Module)]
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "com.devjamesp.architecture"
minSdkVersion 23
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
viewBinding = true
dataBinding = true
}
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.5.0'
implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
// test lib
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
implementation ("androidx.activity:activity-ktx:1.2.3")
implementation ("androidx.fragment:fragment-ktx:1.3.5")
implementation ("androidx.cardview:cardview:1.0.0")
implementation ("androidx.recyclerview:recyclerview:1.2.1")
implementation ("androidx.room:room-ktx:2.3.0")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.3.1")
// ViewModel
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1")
}
buildFeatures { ... } 부분은 일일이 view를 findViewById로 바인딩 하는게 귀찮아서 추가한 부분입니다.
이부분은 제외하시고 findViewById로 chronometer뷰를 할당하셔서 사용하셔도 됩니다:)
[ChronometerViewModel.kt]
package com.devjamesp.architecture.step2
import androidx.lifecycle.ViewModel
// data 관리는 여기서~
class ChronometerViewModel : ViewModel() {
var mStartTime : Long = 0L
}
[MainActivity.kt]
package com.devjamesp.architecture.step2
import android.os.Bundle
import android.os.SystemClock
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProvider
import com.devjamesp.architecture.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var binding : ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
// viewModel 생성
val chronometerViewModel = ViewModelProvider(this).get(ChronometerViewModel::class.java)
if(chronometerViewModel.mStartTime == 0L) {
val startTime = SystemClock.elapsedRealtime()
chronometerViewModel.mStartTime = startTime
binding.chronometer.base = startTime // viewModel -> view data전달
} else {
binding.chronometer.base = chronometerViewModel.mStartTime
}
binding.chronometer.start()
}
}
[activity_main.xml]
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".step2.MainActivity"
android:padding="16dp">
<Chronometer
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:id="@+id/chronometer"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/chronometer"/>
</androidx.constraintlayout.widget.ConstraintLayout>
코드랩 예제 코드가 너무 잘나와있어서 쉽게 할 수 있습니다.
핵심은 해당 코드입니다.
뷰모델을 생성하는 코드입니다. 생성된 객체를 activity에서 가지고 있고, VIewModel객체에는
mStartTime이라는 프로퍼티가 있고, 이를 통해
처음 mStartTime값이 0일때, 현재 시간을 측정하여, ViewModel과 최초 한번만 동기화해줍니다.
그리고나서 view에 시간 데이터를 전달하여 사용자에게 보여줍니다.
그리고 0이 아니라면, view에 바로 전달하면 되겠죠? :)
(이유는 액티비티가 소멸되는 경우viewModel은 살아있기 때문에 초기화를 또 해줄 필요가 없기 때문입니다.
chronometer.start()메소드를 호출하여 시작하면 끝~
[실행 화면]
AppCompatActivity를 포함한 액티비티클래스들은 ComponentActivity 클래스를 상속하고 있는데 이 클래스가 LifecycleOwner 인터페이스를 구현하고 있고, lifecycleOwner의 scope가 유지되고 있는 한 ViewModel은 유지된다고 합니다.
(다음의 경우 ViewModel은 죽지 않는다고 하네요:)
1. 스크린이 회전하는 경우
2. 또다른 앱을 실행하거나 기존의 다른 앱으로 돌아가는 경우
이상 간단하게 ViewModel을 사용하는 방법을 알아봤습니다. :)
https://developer.android.com/codelabs/android-lifecycles?hl=ko#0
'쉽게 읽는 개발문서 > 안드로이드 개발문서' 카테고리의 다른 글
뷰페이저2와 탭으로 스와이프 뷰 만들기(FragmentStateAdapter) 2021-06-09 (0) | 2021.06.09 |
---|---|
개발자 가이드(소개) - 데이터 접근 결정 2021-06-09 (0) | 2021.06.08 |