الگوی معماری MVVM در اندروید

پیاده سازی معماری MVVM در اندروید – بخش سوم


حال که با پیاده سازی الگوی MVVM آشنایی پیدا کردید، به شرح مثال هایی برای درک بهتر موضوع می پردازیم، با ما همراه باشید.

چندین مثال کاربردی

لایبرری DataBinding از وضعیت بتا خارج شده و میزبان ویژگی های کاربردی جدیدی شده که یکی از آنها two-way binding می باشد.

اکنون داده ها بر روی UI تاثیر می گذارند و بالعکس. برای مثال وقتی کاربر نام خود را در EditText وارد می کند، مقدار متغیر نیز فورا به روز می شود. قبلا نیز چنین قابلیتی پیاده سازی شده بود، اما شامل TextWatcher و BindingAdapter می شد، اما در حال حاضر این کار ساده تر صورت می پذیرد و تنها نیاز است "{android:text="@{viewModel.text}" на android:text="@={viewModel.text  را تغییر دهید (به علامت = بعد از "@" توجه کنید). چنین ترفندهایی تنها با فیلدهای Observable کار می کنند (ObservableInt ،ObservableBoolean ،ObservableField و غیره). در زیر دیالوگی را مشاهده می کنید که در آن وضعیت Mark تغییر پیدا کرده است:

<layout

xmlns:android="http://schemas.android.com/apk/res/android">

<data>

<variable

name="viewModel"

type="com.stfalcon.androidmvvmexample.features.dialogs.input.InputDialogVM"/>

</data>

<EditText

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:hint="@string/dialog_status_text"

android:text="@={viewModel.text}"

android:textColor="@color/primary_text"

android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"/>

</layout>

در اینجا یک ViewModel برای ویو در دیالوگ اضافه شده و انتقال مستقیم ObservableField به متغیر به درستی کار نمی کند. به همین شکل قادر به بایند کردن سایر ویژگی ها مانند CheckBox و RadioButton انتخاب شده می باشید.

اگر می خواهید در حین تغییر داده ورودی/خروجی به آن واکنش نشان دهید، می توانید ()get یا ()set را در فیلد Observable اورراید کنید و تغییرات موردنظر خود را اعمال نمایید.

field=newObservableField(){@OverridepublicStringget(){//TODO:yourlogicreturnsuper.get();}@Overridepublicvoidset(Stringvalue){//TODO:yourlogicsuper.set(value);}}; decode:true " >public final ObservableField<String> field = new ObservableField<String>() {

@Override

public String get() {

// TODO: your logic

return super.get();

}

@Override

public void set(String value) {

// TODO: your logic

super.set(value);

}

};

چنانچه مشکل تنها به پیگیری تغییرات مربوط است می توانید OnPropertyChangedCallback را اضافه کنید:

field.addOnPropertyChangedCallback(new OnPropertyChangedCallback() {

@Override

public void onPropertyChanged(Observable sender, int propertyId) {

// TODO: your logic

}

});

قابلیت دیگر امکان استفاده از setterها به عنوان ویژگی هایی در markup می باشد. تصور کنید یک متد ()SetAdapter در RecyclerView مشابه دارید، برای نصب آن لازم است مستقیما به widget instance مراجعه کرده و متدهای آن را مستقیما از کد فراخوانی کنید که برخلاف رویکرد ما می باشد. برای برطرف کردن این مشکل می توانید BindingAdapter یا حتی CustomView بسازید که از RecylerView اکسپند خواهد شد و می توانید ویژگی های موردنظر خود را اضافه کنید، اما این روش بهترین گزینه نمی باشد.

می توانید به نام setter در xml اشاره کرده و set را حذف نماییم، بدین ترتیب می توان به صورت زیر adapter را ست کرد:

bind:adapter="@{viewModel.adapter}"

پیشوند bind همان application namespace قدیمی است ولی بهتر است آنها را کپی کرد تا با ویژگی های کاستوم و ویژگی هایی که از طریق Binding تولید شده، اشتباه گرفته نشود:

<layout

xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:app="http://schemas.android.com/apk/res-auto"

xmlns:bind="http://schemas.android.com/apk/res-auto">

...

<android.support.v7.widget.RecyclerView

android:layout_width="match_parent"

android:layout_height="match_parent"

app:reverseLayout="true"

bind:adapter="@{viewModel.adapter}"/>

</layout>

اگر setter موردنظر در ویجت وجود نداشته باشد یا به درستی نامگذاری نشده باشد می توان از CustomView استفاده کرد.

اما انتقال پارمترها در ViewModel با این معماری به چه صورتی انجام می شود؟ برای راحتی کار یک متد استاتیک باز (یا openForResult) ساخته شده که تمامی پارامترهای موردنیاز را فهرست می کند، سپس پارامترها را بیرون آورده و آنها را در ViewModel که یک کانستراکتور مناسب دارد ارسال می کند. برای مثال می توان به اکتیویتی یک وضعیت به عنوان پارامتر داد:

private static final String KEY_STATUS = "STATUS";

public static void open(Context context, String status) {

Intent intent = new Intent(context, ProfileActivity.class);

intent.putExtra(KEY_STATUS, status);

context.startActivity(intent);

}

@Override

public ProfileActivityVM onCreate() {

return new ProfileActivityVM(this, getIntent().getStringExtra(KEY_STATUS));

}

قابلیت دیگر اعمال فیلدهای isLoading و isError در کلاس اصلی ViewModel می باشد. این فیلدها عمومی و از نوع ObservableBoolean می باشند و به همین خاطر نیازی به تکرار منطق وضعیت بارگذاری و خطاها نمی باشد. برای نمایش تغییرات، به سادگی می توان از include استفاده کنید:

<include

layout="@layout/part_loading_state"

bind:visible="@{viewModel.isLoading}"/>

در صورت نیاز می توانید آیکون ها و پیام های موارد مختلف را در متغیرهای جداگانه منتقل کنید (علت های مختلف بوجود آمدن خطا)، نتیجه یک کامپوننت قابل تغییر می باشد که با یک جفت سطر در هر لی اوت پیاده سازی شده است.

در استفاده از MVVM، باید کدهای زیادی بنویسیم: تغییرات اکتیویتی/فرگمنت در کلاس های اصلی، تعیین نام های طولانی برای Binding-classes در generics، ساخت و بایند کردن ViewModel. در مراحل اولیه لازم است کلاس های اصلی را از یک پروژه به دیگری کپی کنیم که موجب صرفه جویی در زمان می شود. به همین دلیل یک لایبرری و پلاگین برای اندروید استودیو ساخته شده که با استفاده از آن این مراحل تنها با 2 یا 3 کلیک انجام می شود.

AndroidMvvmHelper library مجموعه ای از کلاس های اصلی و مقدماتی برای کار با MVVM است. این لیست شامل کلاس هایی برای کار با اکتیویتی (BindingFragment و FragmentViewModel و ActivityViewModel) و فرگمنت (BindingFragment و FragmentViewModel) با منطق بایندینگ می باشد و متدهای ضروری برای فراخوانی ها نیز تعریف شده اند. به منظور استفاده از آن تنها باید وابستگی ها را در فایل گرادل تعریف کنید:

dependencies {

...

compile 'com.github.stfalcon:androidmvvmhelper:X.X'

}

استفاده از لایبرری کار توسعه دهنده را ساده تر می کند، اما ساخت کلاس ها هنوز هم یک فرایند زمانبر می باشد. برای برطرف کردن این مشکل یک افزونه برای IntelliJ IDEA و اندروید استودیو به نام MVVM Generator ساخته شده است که تنها با یک کلیک شما را قادر به ساخت کلاس BindingActivity (یا BindingFragment) و ViewModel آن و فایل xml آماده برای رجیستر کردن کامپوننت در AndroidManifest می کند و اگر این افزونه وابستگی لایبرری MVVMHelper را تشخیص ندهد، به طور خودکار اضافه خواهد شد.

برای نصب آن باید به بخش مدیریت پلاگین مراجعه کنید:

بر روی Browse repositories کلیک کرده و به دنبال پلاگین های در دسترس در وب بگردید:

در باکس جستجو MVVM Generator را وارد کرده، پلاگین را انتخاب و بر روی Install کلیک کنید:

پس از اتمام نصب، باید IDE را ری استارت کنید، تنها پس از این کار پلاگین برای استفاده شما آماده می شود.

حال بیایید یک پروفایل فرگمنت بسازیم، در حین ساخت یک کلاس معمولی context menu را برای پکیج موردنیاز فراخوانی می کنیم و Create Binding Fragment را انتخاب می کنیم.

وقتی که عنوان را که در این مورد ProfileFragment است وارد کردید، موارد زیر را دریافت خواهیم کرد:

کلاس های آماده در آن قرار گرفته اند:

public class ProfileFragment

extends BindingFragment<ProfileFragmentVM, FragmentProfileBinding> {

public ProfileFragment() {

// Required empty public constructor

}

public static ProfileFragment getInstance() {

return new ProfileFragment();

}

@Override

protected ProfileFragmentVM onCreateViewModel(FragmentProfileBinding binding) {

return new ProfileFragmentVM(this);

}

@Override

public int getVariable() {

return BR.viewModel;

}

@Override

public int getLayoutId() {

return R.layout.fragment_profile;

}

}

public class ProfileFragmentVM

extends FragmentViewModel<ProfileFragment> {

public ProfileFragmentVM(ProfileFragment fragment) {

super(fragment);

}

}

علاوه بر این یک xml آماده نیز داریم:

<layout 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">

<data>

<variable

name="viewModel"

type="com.stfalcon.androidmvvmexample.features.profile.ProfileFragmentVM"/>

</data>

<RelativeLayout

android:layout_width="match_parent"

android:layout_height="match_parent">

</RelativeLayout>

</layout>

در چند ثانیه همه چیز برای شروع کار در اختیار شما قرار گرفت. این پلاگین بسیار ساده بوده و تنها عملیات اصلی که همان ساخت فایل هاست را انجام می دهد، اما در آینده امکانات بیشتری نیز به آن افزوده خواهند شد.

 

https://stfalcon.com برگرفته از

اینها را هم بخوانید