MVVM 패턴으로 리팩토링 하는 과정을 기록한다.
토이 프로젝트에 리팩토링 하는 과정으로 먼저 익히고, 결과적으로 이전에 완성한 프로젝트를 리팩토링 하는것을 목표로 한다.
MVVM을 쓰는 이유
Activity, Fragment에 많은 코드를 작성하다보면 앱의 동작 속도 저하, 유지보수의 어려움 뿐 아니라
테스트 할때마다 매번 앱을 빌드해야 하므로 시간이 걸린다. MVVM 패턴은 View와 ViewModel의 분리, 의존성 주입, Unit Test등을 통해 이러한 문제들을 해결할 수 있다.
만들 토이프로젝트의 동작은 위와 같다.
GitHub api를 이용, repository를 검색하고, 해당 저장소의 정보 및 유저의 정보를 가져온다.
MainActivity에서 두개의 fragment를 사용하여 화면 전환하는식으로 구현하였다.
-
DataBinding?
기존의 findViewById를 이용하여 개별적으로 view들을 연결해주던것을 data model을 사용하여 xml에서 직접 연결하게끔 해주는 Android JetPack의 라이브러리.
plugins {
id 'kotlin-kapt'
}
android {
dataBinding {
enabled = true
}
}
dependencies {
kapt "com.android.databinding:compiler:4.1.0"
}
app 수준의 gradle에 위와 같이 추가한다.
item_search.xml
<data>
<variable
name="searchitem"
type="com.example.architecture.data.model.RepoSearchResponse.RepoItem" />
</data>
<TextView
android:id="@+id/search_textview_owner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{searchitem.Owner.login}"
app:layout_constraintEnd_toEndOf="@+id/search_imageview_profile"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/search_imageview_profile" />
(Recyclerview에 item으로 들어가는 layout)
layout에 data를 추가하고, @{..}의 형태로 값을 지정한다.
단 text이므로 데이터가 string이 아니라면, 형변환 해서 넣어야한다 ex) Integer.toString("")
여기서 type은 String, Integer등의 값 뿐 아니라 data class 를 넣을 수 있다.
class SearchFragment : Fragment() {
private lateinit var binding: FragmentSearchBinding
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
binding = DataBindingUtil.inflate(inflater,R.layout.fragment_search,container, false)
return binding.root
}
}
FragmentSearchBinding은 fragment_search.xml을 자동적으로 파스칼표기법으로 바꿔준것이다.
layout에 databinding을 적용하면 자동으로 생성된다.
기존의 view를 리턴하는대신, binding을 리턴한다.
fragment가 아니라 activity라도 마찬가지로 view를 리턴하는 onCreate에서 바꿔주면 된다.
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomViewHolder {
val binding = ItemSearchBinding.inflate(LayoutInflater.from(context), parent, false)
return CustomViewHolder(binding)
}
class CustomViewHolder(val binding : ItemSearchBinding) : RecyclerView.ViewHolder(binding.root) {
fun onBind(data : RepoSearchResponse.RepoItem){
binding.searchitem = data
}
}
override fun onBindViewHolder(holder: CustomViewHolder, position: Int) {
var view = holder.itemView
holder.onBind(searchArray[position])
}
(recyclerview의 adapter)
마찬가지로 recyclerview에서도 기존의 view 리턴 대신 새로 binding한 view를 return해주고 xml의 data와 연결하면
하나하나 데이터를 넣을 필요 없이 연결한 data class에 맞춰 알아서 들어가게된다.
object BindingAdapter {
@BindingAdapter("imgRes")
@JvmStatic
fun setImageResource(v : ImageView, imageUrl : String?){
Glide.with(v.context).load(imageUrl).into(v)
}
}
-
<ImageView
android:id="@+id/search_imageview_profile"
android:layout_width="80dp"
android:layout_height="80dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:imgRes="@{searchitem.owner.avatar_url}"/>
Glide 라이브러리를 이용하여 imageview에 데이터를 넣어야 할 때는 위와같이 BindingAdapter를 프로젝트 내에 만들어야한다. (따로 연결해주지 않아도 build하는 과정에서 자동으로 작동하는듯)
전체 코드는 다음에 기록해놓았다.
github.com/YeseopLee/ArchitectureStudy/tree/databinding
마치며
MVVM 패턴을 익히기 위해 필수적으로 사용해야하는 databinding을 알아보았다.
사실 아직 갈길이 매우 멀다. Livedata, ViewModel, DI, Rxjava 등.. databinding은 아주 기초 밑작업일 뿐이다.
흔히들 러닝커브가 높다라고 표현하는데, 관련해서 실습하며 익힐 예제들을 찾기가 쉽지 않아서 직접 처음부터 차근차근 적용해나가려 한다.
'Android > Architecture' 카테고리의 다른 글
Android - TDD 셋업하기 (Unit Test) (0) | 2021.10.13 |
---|---|
안드로이드 MVVM 패턴 익히기 (4) Dagger-Hilt (0) | 2021.01.07 |
안드로이드 MVVM 패턴 익히기 (3) Repository + DI (0) | 2021.01.02 |
안드로이드 MVVM 패턴 익히기 (2.5) Coroutine (0) | 2020.12.29 |
안드로이드 MVVM 패턴 익히기 (2) LiveData+ViewModel (0) | 2020.12.27 |