A Model-View-Presenter(MVP) mellett leginkább ismert tervezési minta a Model-View-ViewModel, ami a 2015 Google I/O-n bemutatott Databinding könyvtárral lett népszerű.
Az MVP-hez nagyon hasonló feépítésről van szó. A különböző szerepkörök szétválasztását célozza meg, így segítve a tesztelést és karbantartást. Ilyen szerepkör a felület és az üzleti logika.
Model-View-Binding néven is emlegetik, mivel a kulcs ebben a mintában a felület és az adatok között létrejövő kapcsolat. Ha változás történik az adatokban, az megjelenik a felületen is.
Model
Az üzleti logika itt található. Itt van a helye a Java POJO osztályoknak, az adatbázis és API kezelőknek is. Ezeknek a felépítésével nem foglalkozik az MVVM.
View
A felület, ami meg fog jelenni, azaz layout XML-ek. Ide tartoznak az Activity és Fragment osztályok is.
ViewModel
A ragasztó az üzleti logika és a felület között. Segít a Model változásairól értesíteni a felületedet databinding használatával. Feliratkozik és értesül a felület eseményeiről, azaz itt helyezkedik el a felület logikája is.
Hogyan kell használni?
Egy egyszerű Gradle beállítással engedélyezd az egészet:
android {
dataBinding {
enabled = true
}
}
Az XML layoutokat vedd körül egy új XML taggel:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
...eddigi layout...
</layout>
A databinding könyvtár generál a layout fájlból egy osztályt, amit a kapcsolatra használni kell. A neve a layout fájl neve lesz. Például main_activity.xml-ből MainActivityBinding osztály fog keletkezni, mivel az _ (alulvonás) utáni karaktereket nagybetűvel kezdi és végére csap a névnek egy Binding szót. Ezt elérheted az onCreate() vagy onViewCreated() metódusokban:
//Fragment vagy RecyclerView esetén
FragmentListBinding binding = DataBindingUtil.inflate(inflater, R.layout.fragment_list, container, false);
//Fragment vagy RecyclerView másik lehetőség
FragmentListBinding binding = FragmentListBinding.inflate(inflater, container, false);
//Activity esetén
MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity);
//Activity másik lehetőség
MainActivityBinding binding = MainActivityBinding.inflate(getLayoutInflater());
A következő lépésben létrehozod a felülethez tartozó ViewModel osztályodat.
Ebben lesznek a felületet és Modelt érintő metódusok, változók:
public class ViewModel {
...
}
Hivatkozol az XML layoutodban a ViewModel osztályodra, mint adatra:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="viewModel"
type="hu.fps.example.ViewModel"/>
</data>
...eddigi layout...
</layout>
Létrehozod és csatolod a ViewModelt kódban is. Ezt a generált databinding osztályon keresztül lehet megtenni. A generált osztályban lesz egy setVáltozóNév() metódus. A „változó név” az előbbi példából a <variable name="változó név">:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
//Binding létrehozása
MainActivityBinding binding = MainActivityBinding.inflate(getLayoutInflater());
//ViewModel létrehozása
ViewModel viewModel = new ViewModel();
//ViewModel csatolása a view-hoz databindinggal
binding.setViewModel(viewModel);
}
Hogy a felületet feltöltsd adatokkal, hivatkoznod kell rájuk XML-ben. Ezt a becsatolt változón keresztül tudod megtenni, aminek látod a metódusait, változóit:
<TextView
...
android:text="@{viewModel.exampleText}" />
A fenti példában tehát az XML deklarált a viewModel változó exampleText metódusára vagy változójára mutatunk. Magától keresi meg melyikről van szó. Szóba jöhetnek a következők:
- publikus exampleText változó
- getExampleText() metódus
- exampleText() metódus
A felhasználótól jövő bemenetre is lehet figyelni, de azt másképp kell megírni az XML-ben:
<EditText
...
android:text="@={viewModel.userName}" />
Ekkor a ViewModel-ben a setUserName() metódust fogja meghívni a rendszer.
A ViewModel-ban történt változásokra akkor tud feliratkozni a felület, ha az Observable interface-t implementálod. Szerencsére vannak wrapper osztályok, amikkel megfigyelhető lesz egy változó:
private ObservableField exampleText;
A fenti exampleText változóban elérhető egy set(String value) metódus, ami beállítja azt és értesíti a felületet a változásról.
Ezek mind alapvető dolgok, de elkezdeni így lehet. Ha belevágsz még több mindennel fogsz találkozni:
- Saját XML attribútum létrehozása az összekötéshez:
Ha használsz képletöltőt (Picasso, Glide stb.), akkor az XML-hez egy app:url="" attribútumot hozhatsz létre, ahol csak az URL-t kell megadnod. Egy úgynevezett BindingAdapter-ben lesz a kód, ami letölti és beállítja a képet. - RecycleView kezelése
- XML-ben lévő kód:
Itt egész bonyolult dolgokat lehet létrehozni, ugyanis logikai operátorok, lambda kifejezések, és még sok minden rendelkezésre áll.
Amikre figyelned kell
-
Logika az XML-ben:
Figyelni kell, hogy ne rakjon logikát az XML-be senki. Habár erre hiába figyelsz, valaki a csapatból úgyis oda fog rakni valamit. -
Hibakezelés:
Az esemény vezéreltség miatt nehezebb keresztül lépkedni a kódon. Egy databinding nevű fekete dobozon keresztül állítod be a felületi elem tulajdonságait, ami tovább nehezítheti a hibakeresést.
Kisebb, javítható hiányosságok is vannak, amik idegesítőek. Például hibás XML esetén az Android Studio csak sor és oszlopszámot dob ki jelenleg, nem tud odaugrani a hiba helyére.
És most?
Ez csak egy kis segítség a kezdéshez. Ha elkezded alkalmazni, a developer portál databinding cikkében jó segítséget találsz a pontos működéshez.
Amennyiben érdekelnek az ehhez hasonló tervezési minták, akkor ajánlom olvasásra Martin Fowler Patterns of Enterprise Application Architecture című könyvét.
Hozzászólások