این سری نوشته ها خلاصه کتاب اموزشی به نام Android Programming: The Big Nerd Ranch Guide که سعی خواهم کرد در حال مطالعه کتاب نکات خلاصه و مفید رو بنویسم
نوشتن برنامه ها براساس معماری یعنی رعایت یکسری اصول تعریف شده در آن برای ایجاد نظم بیشتر . یکی از معماری های عالی برای برنامه های اندروید MVVM می باشد که شامل بخش های Model , View , Viewmodel می باشد . در برنامه های اندرویدی View همان فایل های layout ، اکتیویتی و فرگمنت ها می باشد و Model نیز کلاس های منطق برنامه است . اما Viewmodel کلاسی است که ارتباط دهنده بین View و Model میباشد . این Viewmodel با ویومدلی که در کامپوننت Jetpack قرار دارد از نظر معنایی متفاوت بوده ، هر چند میتواند از هر دو استفاده کرد . ویومدل Jetpack برای کار با اطلاعات در چرخه حیات فرگمنت و اکتیویتی است ، ولی Viewmodel یک قسمت انتزاعی از معماری MVVM می باشد که کار اتباط دهی را انجام می دهد و میتواند از ویومدل Jetpack نیز ارث بری کند .
نحوه ارتباط بین Viewmodel و View در معماری بطوری تعریف شده که Viewmodel به هیچ وجه نباید به View وابسته باشد ، برای اینکار باید از طریق DataBinding بین آنها ارتباط برقرار کرد ، بطوری که View با استفاده از DataBinding بصورت غیر مستقیم ازتغییرات Viewmodel با خبر می شود . برای فعال کردن DataBinding به شکل زیر ابتدا dependency را اضافه میکنیم :
apply plugin: 'kotlin-kapt' android { ... buildTypes { ... } dataBinding { enabled = true } }
سپس در هر فایل layout که میخواهیم DataBinding را در آن اجرا کنیم ، کل تگ های آن را با تگ <layout> می پوشانیم ، مثل کد زیر :
<layout xmlns:android="http://schemas.android.com/apk/res/android"> <androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent"/> </layout>
حال اگر یکبار پروژه را build کنید ، اندروید استودیو بصورت اتوماتیک یک سری کلاس به فرم CamelCase با نام
فایل layout و با پسوند Binding می سازد تا در DataBinding بتوان از آن استفاده کرد . مثلا برای فایل activity_main فایلی با نام ActivityMainBinding ساخته خواهد شد که در اکتیویتی به شکل زیر استفاده خواهد شد :
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val binding: ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main) } }
اولین کاری که باید با DataBinding انجام داد معرفی layout در ابتدای برنامه است که اینکار با DataBindingUtil.setContentView انجام می شود .
حال زمان تعریف Viewmodel است ، کلاسی که اطلاعات Model را برای نمایش در View آماده میکند و نحوه نمایش منطق برنامه را از خود نمایش جدا میکند . کافی است یک کلاس ساخته و منطق برنامه را در آن لود کنیم :
class ViewModel { var sound: Sound? = null set(sound) { field = sound } val title: String? get() = sound?.name }
حال برای اینکه بتوان از Viewmodel در View استفاده کرد با استفاده از تگ <data> و تعریف <variable> عملا Viewmodel را در View تعریف میکنیم :
<layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <data> <variable name="viewModel" type="com.bignerdranch.android.beatbox.SoundViewModel"/> </data> ... </layout>
حال بطور مثال برای نمایش اطلاعات روی android:text یک Button بجای تعریف مستقیم از پیشوند @ با property که در ViewModel تعریف شده ارتباط را بقرار میکنیم :
<Button .... android:text="@{viewModel.title}" />
و آخرین کار ایجاد کلاس Viewmodel و چسباندن آن به binding در اکتیویتی می باشد :
binding.viewModel = SoundViewModel()
درست است که Viewmodel به View بطور کامل متصل شده و اصول MVVM نیز رعایت شده ، ولی هر بار که بخواهیم اطلاعات را جهت نمایش اماده کنیم باید در Viewmodel تغییری در Model ایجاد کنیم و به View اطلاع بدهیم که اطلاعات تغییر کرده . برای اینکار روش های مانند Observable وجود دارد ولی یکی از بهترین راه حل ها برای اینکار استفاده از LiveData برای property هایی است که در DataBinding شرکت میکنند به این شکل :
class SoundViewModel { val title: MutableLiveData<String?> = MutableLiveData() var sound: Sound? = null set(sound) { field = sound title.postValue(sound?.name) } val title: String? get() = sound?.name }
اینطوری LiveData به صورت اتوماتیک اطلاعات تغییر یافته را به DataBinding اطلاع خواهد داد ، در نتیجه بصورت اتوماتیک View به روز خواهد شد . تنها کار باقی مانده اتصال چرخه حیات برای DataBinding است که مثل LiveData هر وقت که فرگمنت یا اکتیویتی از بین رفت DataBinding نیز از بین برود ، به شکل زیر می توان اکتیویتی را به عنوان LifecycleOwner معرفی کرد :
binding.lifecycleOwner = this@MainActivity
به همین سادگی و تمام .
فقط یک نکته بسیار عالی اینکه اگر بخواهید میتوانید حتی به صورت مستقیم رویداد هایی مثل در Button را هم در Viewmodel صدا بزنیم ، بدون اینکه کدی در فرگمنت یا اکتیویتی نوشته شود :
<Button ..... android:="@{() -> viewModel.onButtonClicked()}" />