Android/Binding Data
Vinculació de dades
modificaLa vinculació de dades (o data binding en anglès) és el nom que rep la tècnica o metodologia de vincular un element visualitzable d'una aplicació (un text, un botó...) amb un element del model de dades (una variable per exemple). De tal forma que els canvis realitzats sobre un, condicionen l'altre. Per exemple: la vinculació d'un text d'un EditText del layout amb un variable de tipus String. Tots els canvis realitzats sobre aquesta variable es mostrarien per pantalla i a la inversa, tota seqüència de caràcters introduïda per l'usuari a través d'aquest element de la interfície de l'usuari es trobaria emmagatzemada en la variable enllaçada.
D'aquí surt el terme de vinculació entre elements de la interfície de l'usuari i del model de dades. Podem concloure que els elements de la interfície d'usuari són observables i observadors al mateix temps, ja que també es veuen condicionats pels canvis realitzats pel model de dades. En general podem distingir dos tipus de vinculació de dades:
- Vinculació de dades conegudes: aquest és el tipus més utilitzat, on es realitza una vinculació de dades conegudes. Per exemple, el TextView mostrat a un usuari amb una variable String del model de dades. Per fer aquesta vinculació els objectes en qüestió han de ser compatibles.
- Vinculació de dades avançada: en aquest cas la vinculació de dades es realitza per objectes "desconeguts". Per tant la vinculació es realitza en temps d'execució des de l'Activitat principal. És a dir, aquest tipus de vinculació es realitza quan en temps d'execució s'escull quin objecte és compatible a vincular amb un element del layout. És molt comú quan l'element en qüestió forma part d'una estructura de dades més gran (una llista, un diccionari...). Per exemple, una aplicació on es donen diferents resultats numèrics per a la solució d'una equació complexa. Obtindrem múltiples resultats però solament es visualitzaran aquells que siguin positius, per tant solament els resultats que compleixin la condició indicada seran vinculats amb elements del layout en qüestió.
Requisits
modifica- Tenir instal·lat el complement (plugin) d'Android Gradle (versió 1.5.0-alpha1 o superior)
- Tenir instal·lada la biblioteca dataBinding (recomanable mitjançant l'Android SDK Manager)
Configuració
modificaPosteriorment, cal habilitar la vinculació de dades modificant l'script de Gradle build.Gradle (Module:app) amb el següent codi:
- Link al repositori: Pàgina principal d'Android Developers Data Binding
- Anotacions: Versió 4.4 del graddle, 3.1.4 d'Android Plugin i 1.0 d'Android Studio
- Vegeu també: No
android{
...
dataBinding {
enabled = true;
}
}
En fer canvis en el Gradle haurem de sincronitzar-lo amb el projecte, per fer això farem clic a File -> Sync Project with Gradle Files d'Android Studio o amb el botó que podem trobar a la cantonada superior dreta.
Vinculació de dades:pas a pas
modificaPer vincular dades hem de tenir en compte quins elements del nostre model de dades seran vinculats amb elements del disseny. Per tal de fer possible una visualització en pantalla d'unes dades és necessari que existeixi una compatibilitat entre el contingut de l'element del disseny i la font de dades. En altres paraules, les dades no només han d'estar reflectides al disseny, si no que tots els canvis realitzats sobre aquestes també es faran sobre el disseny. També és recomanable realitzar un pas previ de reflexió, per a saber quina informació del nostre model de dades volem que estigui vinculada amb el layout. Degut a que aquesta pot ser modificada tant per la pròpia lògica de l'aplicació com per l'usuari. A continuació, l'exemple de creació d'una classe. En aquest cas, la classe Usuari:
- Link al repositori: Classes java en desenvolupament Android
- Anotacions: Codi funcional en versions actuals de Java a partir de la 7.0
- Vegeu també: No
public class usuari {
private final String nom;
private final String cognom;
private int edat;
private boolean major_edat;
private int tlf;
private int num_connexions;
public usuari(String firstName, String lastName,int age,int tlf_number){
nom = firstName;
cognom = lastName;
num_connexions=1;
edat=age;
tlf=tlf_number;
major_edat = (edat>=18)
}
public String getFirstName() {
return nom;
}
public String getLastName() {
return cognom;
}
public boolean es_major_edat(){
return major_edat;
}
public void modifica_connexions(){
num_connexions++;
}
}
Continuarem editant el fitxer del layout de la nostra aplicació, utilitzant el LinearLayout, que és el més senzill d'entendre. Amb l'etiqueta anomenada data indicarem el conjunt d'elements del model de dades a vincular. Cal concretar el nom de la variable (de l'activitat principal) i el tipus. El tipus o type a indicar és el camí per arribar a la classe que pertany la variable en qüestió. Dins de l'element data també podem utilitzar import de classes implementades per nosaltres o classes natives de Java. Per cada etiqueta data només podem utilitzar un tipus d'element visualitzable. Per tant, si tenim diferents elements en el layout declararem tantes etiquetes data com elements necessitem enllaçar. Aquesta vinculació es realitza indicant dins l'expressió de vinculació i els atributs de la variable utilitzada. Aquesta vinculació es realitza independentment de com hem declarat els atributs de la classe, siguin públics o privats. A continuació podem veure un exemple de codi XML amb 2 Textview. El paràmetre text de cadascun d'ells pren el valor de nom i cognom de la classe Usuari, respectivament:
- Link al repositori: Pàgina principal de Layouts d'Android Developer
- Anotacions: Cal haver implementat la classe Usuari, mostrada en el codi anterior.
- Vegeu també: Android/Composició d'un layout
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"> //layout com element arrel
<data>
<variable name="usuari" type="p.damo.databinding.usuari"/> //type: camí per arribar a la classe vinculada
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{usuari.nom}"/> //accés a l'atribut directament de la variable user
<TextView
android:id="@+id/text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{usuari.cognom}"/>
</LinearLayout>
Vinculació generada
modificaEl codi indicat en XML ens genera automàticament una nova classe el nom de la qual depèn del nom de l'arxiu de disseny, afegint-hi el sufix "binding". Per exemple, si el nostre arxiu s'anomena main_activity.xml la nova classe generada serà ActivityMainBinding. Aquesta classe té totes les vinculacions de les propietats definides en el Layout i determina com assignar valors per realitzar una correcta vinculació. Aquesta es realitza per cada activitat i layout, augmentant així la mida de l'aplicació. La vinculació es realitza des del MainActivity. En l'exemple a continuació vincularem l'atribut nom de la classe ususari amb el text que apareix per pantalla. Per poder fer-la hem d'utilitzar un dels mètodes declarats en MainActivityBinding, tot i que podríem afegir-ne més si ho necessitéssim.
- Link al repositori: Data binding d'Android Studio
- Anotacions: Versió 4.4 del graddle, 3.1.4 d'Android Plugin i 1.0 d'Android Studio
- Vegeu també: No
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding= DataBindingUtil.setContentView(this, R.layout.activity_main);
usuari user = new usuari("James", "Martin",19,654321987); //constructor classe usuari
binding.setUser(user); //vinculació pel mètode set, de la variable declarada en el layout
inicialitza();
...
}
private void inicialitza(){
text1 = (TextView) findViewById(R.id.text1);
text2 = (TextView) findViewById(R.id.text2);
...
}
Com ja estem, la crida al mètode de la classe "vinculadora" es pot realitzar abans de la visualització dels elements que es troben enllaçats.
Al realitzar una vinculació de dades hem de tenir molt en compte que la visualització i la lògica estan sincronitzades, és a dir, els canvis que succeeixen a nivell d'execució i que són invisibles a l'usuari afecten a les dues i per tant hem de saber en tot moment que ens interessa enllaçar. Tanmateix, també podem dur a terme una vinculació personalitzada de dades, és a dir, podem escollir la variable, el layout i l'objecte del disseny pel qual es realitza una única vinculació. Normalment aquest fet es produeix a l' intentar enllaçar o vincular variables d'altres activitats dins del mateix projecte.
Expressions i sintaxi
modificaPodem utilitzar una gran varietat d'expressions al realitzar una vinculació de dades. L'expressió de vinculació de dades s'indica amb @{ }, escrivint-hi dins les dades a vincular. Se'ns permet utilitzar un llenguatge d'expressió molt similar al de java, on trobem:
- Operadors matemàtics: +, -,/,% i *.
- Operadors lògics: || i &&.
- Literals: strings, enters, caràcters...
- Altres: crides a funcions, ús de mètodes de classe, recursos, comparadors...
En el següent codi es pot veure un exemple de l'ús d'aquest llenguatge, al realizar una vinculació de dades:
- Link al repositori: No
- Anotacions: No
- Vegeu també: No
<Textview
android:id="@+id/tpresentacio"
android:layout_width="match_parent"
android:gravity="center_horizontal"
android:layout_height="wrap_content"
android:text="@{user.num_connexions}"
android:visibility="@{user.major_edat ? View.GONE : View.VISIBLE}" // expressió usant operador ternari; si l'edat és major a 18 anys el contingut serà visible
/>
Gestió d'events i listeners
modificaUna de les característiques de la vinculació de dades és l'escriptura d'expressions que ens permeten gestionar la resposta dels events succeïts per la interacció de l'usuari. En general podem diferenciar dos mètodes per a gestionar un event:
- Referències de mètodes: com es comentava en l'apartat 2.2 Expressions i sintaxi podem fer referència a mètodes de receptors dins de l'expressió usada. Es basa en definir el conjunt de respostes dels events que volem captar fent referència a la classe i al mètode corresponents.
- Vinculacions de receptors: són expressions similars al cas anterior però que s'avaluen en el moment que ocorre l'event, el receptor s'enregistra en temps d'execució.
Les dues alternatives són similars i comparteixen l'ús de crides als mètodes que gestionen la resposta a l'event ocorregut. El principal avantatge respecte la gestió d'events que es realitza típicament és que podem delegar la gestió dels events a components que no siguin la pròpia activitat principal, reduint així la quantitat de codi d'aquesta. Per exemple, donada una activitat, el layout corresponent a l'activitat i una classe addicional, podem delegar tota la gestió de les respostes a events per part de l'usuari a aquesta classe sense que l'activitat principal hi participi.
Vinculació de receptors
modificaPer poder utilitzar listeners en vinculació de dades, haurem d'indicar en el layout el listener o una directiva d'escolta, com per exemple un android:onClick. Juntament amb la directiva indicarem la referència al mètode encarregat de respondre a l'event succeït. En aquest cas, però, ho indicarem amb l'expressió de vinculació. Aquesta referència del mètode ha d'estar declarada en alguna classe del nostre projecte.
El principal avantatge d'aquest mètode és que podem observar els errors ocorreguts en temps de compilació, quan es detecta algun error en la resposta de l'event o de la vinculació realitzada.
En el següent fragment de codi podem veure un exemple de la vinculació de receptors realitzada pel clic d'un botó amb una classe usuari:
- Link al repositori: Pàgina principal de Layouts d'Android Developer
- Anotacions: Cal haver implementat la classe Usuari, mostrada anteriorment.
- Vegeu també: Android/Composició d'un layout
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
//usuari és observador i observable al mateix temps
<variable name="usuari" type="p.damo.databinding.usuari"/>
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{usuari.nom}"/>
<TextView
android:id="@+id/text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{usuari.cognom}"/>
<Button
android:id="@+id/boto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="@{ ()-> usuari.modifica_connexions()}" //crida al mètode sense paràmetres
/>
<EditText
android:id="@+id/edit_num"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{usuari.num_connexions} //sempre mostrem l'últim valor actualitzat
/>
</LinearLayout>
</layout>
- Link al repositori: Classes java en desenvolupament Android
- Anotacions: Codi funcional en versions actuals de Java a partir de la 7.0
- Vegeu també: No
public class usuari {
private String nom;
private String cognom;
private int edat;
private boolean major_edat;
private int tlf;
private int num_connexions;
public usuari(String firstName, String lastName,int age,int tlf_number){
nom = firstName;
cognom = lastName;
num_connexions=1;
edat=age;
tlf=tlf_number;
major_edat = (edat>=18);
}
public String getFirstName() {
return nom;
}
public String getLastName() {
return cognom;
}
public boolean es_major_edat(){
return major_edat;
}
public void modifica_connexions(){
num_connexions++; //estem dins l'event, apliquem canvis!
}
}
En aquest exemple la resposta a l'event succeït "onClick" és respost pel component usuari. Segons quina acció volem realitzar, quan succeeix un determinat event podem gestionar la resposta d'una manera o una altra. En aquest cas, com modifiquem un atribut de la variable user, el mètode encarregat de respondre a l'event s'ha declarat en la mateixa classe, ja que posteriorment es mostra per pantalla. Per tal d'implementar una aplicació més sostenible i modular, seria recomanable utilitzar components o classes responsables de la gestió d'events.
Referències de mètodes
modificaEn aquest cas les expressions s'executen quan succeeix l'event en qüestió, de forma similar al mètode anterior. Fent servir aquest mètode el receptor és enregistrat en el moment que ocorre l'event per la pròpia vinculació de dades. Com en el cas anterior s'utilitzen les directives dels elements del disseny per tal d'indicar una expressió de vinculació. En aquesta vinculació indiquem el nom de la classe que s'encarrega de respondre a l'event succeït seguit del nom del mètode a utilitzar, separat amb dos punts dobles ('::') per realitzar aquesta referència.
- Link al repositori: Pàgina principal de Layouts d'Android Developer
- Anotacions: Cal haver implementat la classe Usuari, mostrada en el codi anterior.
- Vegeu també: Android/Composició d'un layout
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="usuari" type="p.damo.databinding.usuari"/>
<variable name="gestor" type="p.damo.databinding.gestor" /> //afegim al data la classe encarregada de respondre a l'event
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{usuari.nom}"/>
<TextView
android:id="@+id/text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{usuari.cognom}"/>
<Button
android:id="@+id/boto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick=" @{(view) -> gestor::onClickMeu()}}" //passem com a paràmetre la vista del botó
/>
<EditText
android:id="@+id/edit_num"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{ usuari.num_connexions}"
/>
</LinearLayout>
</layout>
Dins la funció onClickMeu realitzarem les accions a convenir segons les nostres necessitats. És preferible utilitzar una classe encarregada de respondre als events, en aquest cas l'hem anomenat gestor.
- Link al repositori: No
- Anotacions: No
- Vegeu també: No
public class gestor{
...
public void onClickMeu(View view){
//accions a realitzar
}
}