GitHub - WrappingorNot/MVVM_BeatBox
MVC 아키텍쳐
MVC 아키텍쳐의 문제점
→ MVVM(모델-뷰-뷰-뷰모델)과 같은 아키텍쳐를 사용하는 것, 작업 분할은 직접 구현해야함.
MVVM 에서는 뷰 와 밀접한 컨트롤러 코드를 레이아웃 파일로 옮길수 있기 때문 동적인(변하는데이터 처리하는) 컨트롤러의 코드의 일부를 뷰모델 클래스에 넣어서 앱의 테스트와 검증도 쉽게 가능
Jetpack ViewModel
액티비티나 프래그먼트의 생명주기에 걸쳐 데이터를 유지하고 관리하는 클래스
MVVM 뷰모델
개념적인 아키텍쳐의 일부분
플러그인 import 하는 방식이 안드로이드 스튜디오에서 변경 되었다. 모르면 아래 링크 참고하면 된다.
데이터 바인딩의 몇가지 장점중 최고
id "org.jetbrains.kotlin.kapt" version "1.7.21" apply false
, id 'kotlin-kapt'
입력plugin에dataBinding{
enabled = true
}
kotlin-kapt 플러그인을 적용하면 데이터 바인딩에서 코틀린의 애노테이션을 처리할 수 있다.
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</layout>
위의 태그는 이 레이아웃에 데이터 바인딩을 한다는 것을 나타낸다.
레이아웃에 이 태그가 있으면 데이터 바인딩 라이브러리가 바인딩 클래스 를 자동으로 생성한다.
→ setContentView(Int)를 사용해서 뷰를 인플레이트 하는 대신에 ActivityMainbinding의 인스턴스를 인플레이트 한다.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding: ActivityMainBinding =
DataBindingUtil.setContentView(this, R.layout.activity_main)
binding.recyclerView.apply{
layoutManager= GridLayoutManager(context, 3) //recyclerview가 한 행에 세 개의 격자를 가진다
}
}
private inner class SoundHolder(private val binding: ListItemSoundBinding):
RecyclerView.ViewHolder(binding.root)
}//list_item_sound와 연결되는 뷰홀더를 생성함
//Sound 객체를 생성하는 List 생성 하기
class BeatBox(private val assets:AssetManager) {
val sounds : List<Sound>
init{
sounds = loadSounds()
}
private fun loadSounds(): List<Sound> {
val soundNames: Array<String>
try {
soundNames = assets.list(SOUND_FOLDER)!!
} catch (e: Exception) {
Log.e(TAG, "Could not list assets", e)
returnemptyList()
}
val sounds =mutableListOf<Sound>()
soundNames.forEach{filename->
val assetPath = "$SOUND_FOLDER/$filename"
val sound = Sound(assetPath)
sounds.add(sound)
}
return sounds
}
}
에셋을 추가 해서 아래의 사진과 같잉 데이터 바인딩을 실시 한다.
보통 하나의 클래스는 한 가지의 책임만 가지게 하는것이 기본 원리
모델은 앱이 작동하는 방법을 나타낸다
컨트롤러는 모델과 뷰를 중재 하면서 앱의 데이터를 보여주는 방법을 결정한다
뷰는 화면에 데이터를 보여준다
그러므로 MVVM 모델로 사용하기 위해서는 뷰 모델이 필요하다.
위의 모델이 MVVM 모델이라고 한다.
보여줄 데이터를 형식화 하기 위해 MVC의 컨트롤러 클래스가 런타임시 시에 했던 대부분의 일을 뷰모델이 담당한다.
레이아웃에서 위젯들을 데이터와 바인딩하던 일을 뷰모델이 하게 된다.
컨트롤러(액티비티 나 프래그먼트)는 데이터 바인딩과 뷰모델을 초기화 하고 연결하는 일을 맡게 된다.
class SoundViewModel {
var sound: Sound? = null
set(sound) {
field= sound
}
val title: String?
get() = sound?.name
}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="viewModel"
type="com.example.mvvm_beatbox.SoundViewModel" />
</data>
<Button
android:layout_width="match_parent"
android:layout_height="120dp"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
/>
</layout>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="viewModel"
type="com.example.mvvm_beatbox.SoundViewModel" />
</data>
<Button
android:layout_width="match_parent"
android:layout_height="120dp"
android:layout_marginStart="5dp"
android:text="@{viewModel.title}"
android:layout_marginEnd="5dp"
/>
</layout>
RecyclerVie의 각 항목(위에서는 버튼) 데이터를 갖는 Soundholer에 다음 코드를 추가 한다.
init {
binding.viewModel= SoundViewModel()
}
fun bind(sound: Sound){
binding.apply{
viewModel?.sound = sound
executePendingBindings()
}
SoundViewModel 인스턴스를 생성하고 이것의 탐조를 바인딩 클래스인 ListItemSoundBinding의 viewModel 속성에 설정 한다. 그리고 바인딩 함수인 bind(…)를 추가한다.
init 초기화 블록에서 뷰모델 인스턴스를 생성, 바인딩 클래스의 viewModel 속성을 초기화
바인딩 함수인 bind(Sound)에서는 viewModel속성을 변경한다
executePendingBindings() 호출할 필요는 없으나, RecyclerView에 포함된 바인딩 데이터 변경, 뷰의 빠른 속도로 변경해야 한다. → 빠른 반응을 위해 executePendingBindings() 함수 사용
onBindViewHolder(…)에서 bind(Sound) 함수를 호출하여 뷰모델의 각 Sound 인스턴스를 SoundHolder 인스턴스와 연결한다.
import androidx.databinding.Bindable
class SoundViewModel : BaseObservable() {
var sound: Sound? = null
set(sound) {
field= sound
notifyChange()
}
@get:Bindable
val title: String?
get() = sound?.name
}
뷰모델이 SoundViewModel을 BaseObservable의 서브 클래스로 선언한다.
SoundViewModel의 바인딩되는 속성에 @Bindable 애노테이션을 지정한다.
바인딩되는 속성의 값이 변경될 때마다 notifyChange()또는 notifyPropertyChanged(Int)를 호출한다.
#안드로이드 #디자인패턴 #개발