Android/Composició d'un layout

Introducció[modifica]

Podem entendre un Layout com un contenidor de Views (figura 1), realment la classe Layout és una extensió de la classe ViewGroup. En aquesta pàgina descriurem els diferents tipus de layouts i explicarem alguns dels atributs més importants d'aquests a l'hora de configurar la disposició dels elements a la pantalla.

Fig 1. Esquema de classes Android.

Layouts[modifica]

Un Layout és una vista (View) que en pot contenir d'altres, que controla el comportament i la posició de les Views que conté. Els Layouts es poden declarar de dues formes:

  • Declarar els elements UI en fitxers XML. Android proveeix un vocabulari XML que correspon a les classes i subclasses View.
  • Crear elements Layout en temps d'execució. Una aplicació pot crear objectes View i ViewGroup (i manipular les seves propietats) a través de la programació Java.[1]

Linear Layout[modifica]

Un linear layout organitza cada element dintre d'ell segons el tipus de linear layout que sigui(vertical o horitzontal). Si es un layout vertical organitza els elements en files i un layout horitzontal en columnes. Si la longitud de la columna o fila és superior a la longitud de la pantalla del dispositiu es crea una barra de desplaçament tot i que no vol dir que sigui visible. Per defecte, una linear layout tindrà una orientació horitzontal.


Per especificar quin tipus de Linear Layout es vol utilitzar s'ha d'assignar a la propietat android:orientation l'atribut horizontal o vertical.

 android:orientation="horizontal"
 <!-- o -->
 android:orientation="vertical"

Exemple 1 : Botons en columna[modifica]

Fig 2. Exemple layout vertical

En el següent exemple es mostrarà com queda el posicionament d'uns botons dintre d'un layout vertical (figura 2). Com es pot veure els botons queden un sota l'altre fent que només hi hagi un botó per fila. Si s'hagués modificat les propietats layout:margin i layout:gravity els botons podrien tenir una disposició diferent dins de la seva fila.




Exemple 2 : Botons en fila[modifica]

Fig 3. Exemple layout horitzontal


En el següent exemple es mostrarà com queda el posicionament d'uns botons dintre d'un layout horitzontal (figura 3). Com es pot veure, els botons queden l'un al costat de l'altre en una fila. Si s'hagués modificat les propietats 'layout:margin' i 'layout:gravity' els botons podrien tenir una disposició diferent dins de la seva columna.





Exemple 3 : Layout niuats[modifica]

Fig 4. Exemple layouts niuats.

Podem combinar diferents tipus de layouts per obtenir la disposició dels elements desitjada. A continuació veurem un exemple on fem servir una configuració amb diferents LinearLayouts (horitzontals i verticals) (figura 4).




Relative Layout[modifica]

És un layout que la seva principal característica és que cada fill podrà basar la seva posició a partir del posicionament dels altres fills o el pare. Aquest layout és una eina bastant poderosa a l'hora de la creació d'interfícies ja que permet l'eliminació dels ViewGroups niuats, això vol dir que en un mateix disseny no caldrà fer diferents combinacions de layouts per posicionar els widgets. Els Relative Layout permeten tenir més simplicitat i netedat en el codi.

Llistat d'atributs[modifica]

A continuació mostrem dues taules amb els atributs dels Relative Layouts segons si serveixen per posicionar els fills respecte altres fills, i si serveixen per posicionar els fills respecte al layout pare:


Posicionament d'un fill respecte a un altre fill:

XML attributes
android:layout_above Posiciona el cantó inferior d'aquesta View per sobre de la ID d'àncora de View donada.
android:layout_below Posiciona el cantó superior d'aquesta View per sota de l'ID d'àncora de View donada.
android:layout_toEndOf Posiciona el cantó d'inici d'aquesta View cap al final de l'ID d'àncora de View donada.
android:layout_toLeftOf Posiciona el cantó dret d'aquesta View cap a l'esquerra de l'ID d'àncora de View donada.
android:layout_toRightOf Posiciona el cantó esquerre d'aquesta View cap a la dreta de l'ID d'àncora de View donada.
android:layout_toStartOf Posiciona el cantó del final d'aquesta View cap a l'inici de l'ID d'àncora de View donada.
android:layout_alignBaseline Posiciona la línia base d'aquesta View cap a la línia base de l'ID d'àncora de View donada.
android:layout_alignBottom Fa encaixar el cantó inferior d'aquesta View amb el cantó inferior de l'ID d'àncora de View donada.
android:layout_alignEnd Fa encaixar el cantó del final d'aquesta View amb el cantó del final de l'ID d'àncora de View donada.
android:layout_alignLeft Fa encaixar el cantó esquerre d'aquesta View amb el cantó esquerre de l'ID d'àncora de View donada.
android:layout_alignRight Fa encaixar el cantó dret d'aquesta View amb el cantó dret de l'ID d'àncora de View donada.
android:layout_alignStart Fa encaixar el cantó de l'inici d'aquesta View amb el cantó de l'inici de l'ID d'àncora de View donada.
android:layout_alignTop Fa encaixar el cantó del cim d'aquesta View amb el cantó del cim de l'ID d'àncora de View donada.


Posicionament d'un fill respecte al layout pare:

XML attributes
android:layout_alignParentBottom Si cert, fa encaixar el cantó inferior d'aquesta View amb el cantó inferior del parent.
android:layout_alignParentEnd Si cert, fa encaixar el cantó del final d'aquesta View amb el cantó del final del parent.
android:layout_alignParentLeft Si cert, fa encaixar el cantó esquerre d'aquesta View amb el cantó esquerre del parent.
android:layout_alignParentRight Si cert, fa encaixar el cantó dret d'aquesta View amb el cantó dret del parent.
android:layout_alignParentStart Si cert, fa encaixar el cantó d'inici d'aquesta View amb el cantó d'inici del parent.
android:layout_alignParentTop Si cert, fa encaixar el cantó cim d'aquesta View amb el cantó cim del parent.
android:layout_alignWithParentIfMissing Si configurat a cert, el parent s'usarà com àncora quan l'àncora no es pugui trobar per a les funcions layout_toLeftOf, layout_toRightOf, etc.

[Llista completa]

Exemple 4 : Usant una relative layout[modifica]

Fig 5. Exemple relative layout

En el següent exemple es mostrarà el posicionament d'uns widgets dins d'un Relative Layout (figura 5). Com es pot veure s'ha aconseguit fer una disposició dels widgets sense utilitzar cap combinació de Linear Layout, tan sols s'ha utilitzat un Relative Layout. Per aconseguir aquesta disposició dels widgets s'ha fet el següent:

  • Per fer la disposició del textView s'ha fet ús dels següents atributs: android:layout_alignParentStart i android:layout_alignParentTop.
  • Tots els checkBoxs han fet ús de l'atribut android:layout_below sobre el textView.
  • Els checkBox "Opció B" i "opció C" han utilitzat l'atribut android:layout_toRightOf sobre id de l'element que tenen a l'esquerra.
  • El botó "Ok" ha fet ús dels atributs android:layout_below i android:layout_alignStart sobre l'identificador del checkBox de la "Opcio C", a més a fet ús de l'atribut android:layout_alignParentRight.




Altres Layouts[modifica]

FrameLayout[modifica]

Aquest no realitza cap distribució sobre els elements que hi ha dins, simplement els apila un sobre l'altre. Si es vol modificar la distribució d'aquests elements és farà ús dels atributs: "android:foregroundGravity" i "android:measureAllChildren".

GridLayout[modifica]

Un GridLayout és un view que incorpora tots els elements qui hi ha dintre d'aquest en una quadricula. Serveix per evitar anidar Linear Layouts per crear dissenys complexos. Els seus principals atributs són els següents: "android:columnCount", "android:rowCount" i "android:useDefaultMargins".

Disposició dels elements dins d'un Layout[modifica]

Mida del elements: match_parent i wrap_content[modifica]

Els valors match_parent i wrap_content es poden utilitzar per especificar les propietats dels atributs layout:height (alçada) i layout:width (amplada) d'una view. Amb aquest dos atributs definim l'espai que ocupa una view filla en el pare.

match_parent[modifica]

Fixant el valor a match_parent forcem la view a expandir-se i agafar el màxim d'espai possible, és a dir tot l'espai lliure del layout pare.

android:layout_width="match_parent"
android:layout_height="match_parent"

wrap_content[modifica]

Fixant el valor a wrap_content només forcem la view a expandir-se tant d'espai com el que ocupin els seus fills.

android:layout_width="wrap_content"
android:layout_height="wrap_content"

Exemple 5 : Ús de match_parent i wrap_content[modifica]

Fig 6. Ús de match_parent i wrap_content

En el següent exemple veurem els diferents resultats d'utilitzar match_parent i wrap_content sobre un conjunt de EditTexts. S'han disposat 3 EditTexts en un LinearLayout vertical (figura 6). Cadascun dels quals té un color de fons diferents (blau, verd i vermell) per veure l'efecte més clar.

  • El LinearLayout té com a paràmetres match_parent per l'alçada i amplada, ja que volem que ocupi tota la pantalla.
  • El primer EditText, el de color blau, s'ha configurat amb wrap_content tant per l'alçada com l'amplada, de tal manera que només ocupa l'espai que ocupa el seu contingut, en aquest cas un text.
  • El segon EditText, el de color verd, s'ha configurat amb wrap_content per l'alçada i match_parent per l'amplada, per això ocupa tot l'ample del LinearLayout pare.
  • L'últim EditText, el de color vermell s'ha configurat amb match_parent per l'alçada i amplada, així que ocupa tot l'espai restant.




L'atribut Weight[modifica]

L'atribut "weight" ens serveix per decidir de quina manera es repartiran l'espai les diferents views. Quan parlem d'espai no ens referim a l'espai total d'un layout sinó a l'espai restant del layout que no està ocupat per cap view. De tal manera que si tenim un layout vertical amb una alçada de 10 unitats on tenim col·locades dues views d'alçada 2 unitats, l'espai a repartir entre les views serà de 6 unitats (10-2-2) i no pas el total de 10.

El valor d'aquest atribut és un nombre que representa una proporció respecte la resta de weights de les altres views germanes.

android:layout_weight="VALOR"

Exemple 6 : Efectes de l'atribut weight[modifica]

Fig 7. Exemple ús de weight

El següent exemple serveix per veure l'efecte de l'atribut weight sobre un conjunt de views germanes. En aquest cas tenim un LinearLayout vertical que conté diferents LinearLayouts horitzontals, cadascun amb uns quants botons a dintre (figura 7). Per a cada conjunt de botons de cada layout horitzontal hem assignat diferents valors de weight. A continuació repassarem cada conjunt i explicarem l'efecte que ha ocasionat l'assignació de pesos:

  • El primer, és un conjunt de tres botons on cadascun dels quals té assignat un pes 1, de forma que es reparteixen l'espai equitativament com podem observar a la imatge de l'exemple.
  • El segon és un altre conjunt de tres botons, aquest cop tots tres tenen assignat un pes 100. El resultat és el mateix que en el cas anterior, ja que com hem dit anteriorment, l'atribut weight fa referència a una proporció. Aleshores abans teníem una proporció d'1/3 i ara tenim una proporció de 100/300.
  • En el tercer cas tenim un conjunt de dos botons amb pesos 1 i 2 respectivament, el resultat és que el primer botó (el de l'esquerra) ocupa 1/3 part de l'espai lliure del layout horitzontal i el segon botó (el de la dreta) ocupa 2/3 parts de l'espai lliure.
  • El quart conjunt està format altra vegada per dos botons, en aquest cas el primer és un botó amb una gran quantitat de text (que ocupa molt espai del layout horitzontal) i té pes 1, el segon és un botó molt menys ample amb pes 500. Podríem pensar que el botó amb pes 500 ocuparà més espai que el primer, ja que aquest només té pes 1, però hem de recordar que l'espai que es reparteixen és l'espai restant del pare que no ocupen les views. Així doncs el resultat és el que podem veure a la imatge.
  • En el cinquè exemple podem veure dos botons, el de l'esquerra amb pes 1 i width: 0, i el de la dreta amb pes 0 i width wrap_content. En aquesta situació el botó amb pes 1 agafa tot l'espai lliure disponible (ja que l'altre botó té pes 0), i el boto amb pes 0 com té una amplada de wrap_content, queda reduït només a l'amplada del seu contingut.
  • Per últim tenim un cas amb una configuració igual a l'anterior (un botó gran amb pes 1 i un botó petit amb pes 500), amb la diferència que abans el width del botó era wrap_content i ara és 0. En aquest cas com inicialment el botó no ocupa cap, l'espai que li és assignat és d'1/500 unitats, que en la pràctica fa que no veiem el botó a la pantalla.

Resum:

Botó esquerre Botó centre Botó dret
Fila 1 width: wrap_content, weight: 1 width: wrap_content, weight: 1 width: wrap_content, weight: 1
Fila 2 width: wrap_content, weight: 100 width: wrap_content, weight: 100 width: wrap_content, weight: 100
Botó esquerre Botó dret
Fila 3 width: wrap_content, weight: 1 width: wrap_content, weight: 1
Fila 4 width: wrap_content, weight: 1 width: wrap_content, weight: 500
Fila 5 width: 0, weight: 1 width: wrap_content, weight: 0
Fila 6 width: wrap_content, weight: 500 width: 0, weight: 1




Gravity i layout_gravity[modifica]

Gravity[modifica]

Amb gravity configurem l'alineació del contingut de la propia view.

android:gravity="VALOR"

Possibles valors:

Codi XML
top
bottom
left
right
center_vertical
fill_vertical
center_horizontal
fill_horizontal
center
fill
clip_vertical
clip_horizontal
start
end

Layout_gravity[modifica]

Configurem l'alineació de la view amb la seva view pare.

android:layout_gravity="VALOR"

Possibles valors:

Codi XML
top
bottom
left
right
center
center_vertical
center_horizontal

Exemple 7 : Efectes dels atributs gravity i layout_gravity[modifica]

Fig 8. Exemple gravity i layout_gravity

En el següent exemple podem veure els efectes dels atributs gravity i layout_gravity (figura 8).

  • En el primer cas tenim un TextView que ocupa tot l'ample de la pantalla (width: match_parent) amb gravity: right, com podem veure el seu contingut (en aquest cas un text) queda alineat a la dreta de la view.
  • En el segon cas tenim un altre TextView amb width: wrap_content, i l'atribut layout_gravity: right, en aquest cas és tota la view (TextView) la que es desplaça a l'esquerra del layout pare (LinearLayout).
  • Per últim tenim un LinearLayout horitzontal amb dos botons; el LinearLayout té l'atribut gravity: center, el que fa que els botons quedin situats al centre d'aquest.




Padding i margins[modifica]

Serveixen per modificar l'espaiat entre les diferents views. És recomanables especificar aquests valors utilitzant "dp" com a unitat de mesura (densitat de pixels). És una unitat de mesura relativa que permet una millor adaptabilitat a l'hora de visualitzar la nostra aplicació en dispositius amb pantalles de diferents mides.

Tant el padding com el margin es poden definir per a tots els costats de la view, o només per alguns específics. Per exemple: (layout_paddingBottom, layout_marginLeft, ...).

Padding[modifica]

El padding es refereix a l'espaiat entre el contingut de la view i els marges d'aquesta mateixa. Es defineix de la següent manera:

android:padding="VALOR"

Margin[modifica]

El margin es refereix a la separació entre la view amb la view veïna.

android:layout_margin="VALOR"

Exemple 8 : Efectes dels atributs padding i layout_margin[modifica]

Fig 9. Exemple margin i padding

En el següent exemple podem veure com mitjançant la configuració d'aquests dos atributs (margin i padding) podem posicionar els elements en un LinearLayout vertical al nostre gust (figura 9).




Introducció als ConstraintLayouts[modifica]

Els ConstraintLayouts són una nova manera de dissenyar els layouts que ha sigut introduïda per Google a la versió d'Android Studio 2.2 preview. Podríem considerar que són una "ampliació" (o més bé un redisseny) dels RelativeLayouts, però, amb un seguit de noves característiques que ens proporcionen un major control sobre la posició de les views en el layout. Es retro-compatible fins l'Android API 9.

Què necessitem per utilitzar-los?[modifica]

Com hem dit al principi és necessita la versió beta d'Android Studio 2.2 preview o superior. A part d'això hem d'incloure les biblioteques de suport dels ConstraintLayouts:

dependencies {
    compile 'com.android.support.constraint:constraint-layout:1.0.0-alpha8'
}

Paràmetres dels Constraint Layouts[modifica]

En aquesta taula podem veure quins són alguns dels nous paràmetres introduïts pels ConstraintLayouts:

Paràmetre XML
layout_constraintTop_toTopOf  Alinea el cim de la View desitjada cap al cim d'una altra.
layout_constraintTop_toBottomOf   Alinea el cim de la View desitjada cap a la part inferior d'una altra.
layout_constraintBottom_toTopOf  Alinea la part inferior de la View desitjada cap al cim d'una altra.
layout_constraintBottom_toBottomOf  Alinea la part inferior de la View desitjada cap a la part inferior d'una altra.
layout_constraintLeft_toTopOf  Alinea l'esquerra de la View desitjada cap al cim d'una altra.
layout_constraintLeft_toBottomOf   Alinea l'esquerra de la View desitjada cap a la part inferior d'una altra.
layout_constraintLeft_toLeftOf  Alinea l'esquerra de la View desitjada cap a l'esquerra d'una altra.
layout_constraintLeft_toRightOf   Alinea l'esquerra de la View desitjada cap a la dreta d'una altra.
layout_constraintRight_toTopOf  Alinea la dreta de la View desitjada cap al cim d'una altra.
layout_constraintRight_toBottomOf  Alinea la dreta de la View desitjada cap a la part inferior d'una altra.
layout_constraintRight_toLeftOf  Alinea la dreta de la View desitjada cap a l'esquerra d'una altra.
layout_constraintRight_toRightOf  Alinea la dreta de la View desitjada cap a la dreta d'una altra.

També podem fer servir "start" i "end" en comptes de left i right.

Avantatges[modifica]

L'objectiu de Google amb la implementació d'aquesta nova classe Constraint Layout es reduir el nombre de views niuades dins d'un layout. Quin sentit té aixó? Doncs a part d'afegir un grau de simplicitat al layout, també té una part important d'optimització. Quan construïm un layout realment el que estem fent és un arbre de "views", on els nodes poden ser viewgroups i tenir views com a fills (figura 10). També les views tenen com a fills el seu propi contingut. Si utilitzem els Constraints Layouts estem reduint el nombre de viewgroups amb views filles, per tant el resultat serà un arbre de menys profunditat, que a l'hora de recorre'l suposa un cost menor.

Fig 10. Arbre de views.

Creant un layout del tipus ConstraintLayout[modifica]

Un cop tenim instal·lada la versió 2.2 d'Android Studio, hem d'obrir el SDK Manager i comprovar que tenim instal·lats els paquets referents als ConstraintLayouts (figura 11).

Fig 11. Paquets ConstraintLayouts

Per crear un nou layout del tipus ConstraintLayout hem d'especificar com a element arrel el següent (figura 12):

android.support.constraint.ConstraintLayout

És important recordar que hem d'afegir al gradle la dependència que s'ha citat més amunt. build.gradle;

apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.3"
    defaultConfig {
        applicationId "com.example.victor.myapplication"
        minSdkVersion 23
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile 'com.android.support.constraint:constraint-layout:1.0.0-alpha8'
}

Actualització 06/10/2017: Amb les noves versions de l'Android Studio (3.0 Beta 6), ja no cal incloure manualment tals dependències.

Fig 12. Crear ConstraintLayout.

Entorn de disseny[modifica]

L'entorn de disseny dels ConstraintLayouts és diferent de l'entorn al qual estem acostumats per treballar amb LinearLayouts o RelativeLayouts. Ara a part de veure la pantalla amb les views que hem col·locat, a la dreta d'aquesta tenim una pantalla que mostra de forma esquemàtica les relacions entre les diferents views que tenim col·locades en el nostre layout (figura 13).

Fig 13. Visualització ConstrainLayout.

Constraints[modifica]

Posicionament al costat[modifica]

En els ConstraintLayouts podem definir la posició d'una determinada view dient "aquesta view va al costat dret d'aquesta altra" o "aquesta view va a sota d'aquesta", etc.

Això ho podem fer des de l'editor visual: seleccionant un dels costats de la view i arrossegant-to cap a una altra view, o bé des de el codi XML:

app:layout_constraintRight_toRightOf="parent"
<!--- o --->
app:layout_constraintRight_toLeftOf="@+id/button1"

Exemple 1 : Posicionament al costat amb constraint layouts[modifica]

Fig 14. Exemple ConstraintLayout posicionament al costat.

En aquest exemple podem veure com hem fet servir aquesta característica de posicionar elements respecte al costat dels altres elements en ConstraintLayout amb un EditText i un botó (figura 14).




Redimensionar[modifica]

En els altres tipus de layouts per poder redimensionar un fill s'havien de tenir en compte els atributs i la posició del layout pare i dels altres fills. Tot això canvia amb l'ús dels ConstraintLayout, ja que a partir d'una interfície gràfica es pot manipular l'amplada i alçada d'un fills dintre un layout. Els atributs utilitzats per definir l'amplada i alçada dins del codi xml son els mateixos que s'utilitzen en els Linear Layouts:

android:layout_width
android:layout_height

Exemple 2 : Redimensionament amb constraint layouts[modifica]

Fig 15. Redimensio elements constraint layout

En el següent exemple es pot comprovar la redimensio feta sobre 3 botons alineats verticalment. La redimensió tant sols s'ha fet a partir de l'interfície gràfica. En el codi xml es pot veure com s'han modificat els atributs android:layout_width i android:layout_height (Figura 15).




Biaix vertical i horitzontal[modifica]

Per definir la posició d'un objecte a la pantalla podem fer servir el biaix vertical i horitzontal, de tal manera que assignem a les views una posició relativa respecte a uns eixos X i Y que establim entre views. Això conjuntament amb els constraints de posicionar al costat ens permet crear layouts adaptatius a diferents pantalles.

Exemple 3 : Modificant biaix amb constraint layouts[modifica]

Fig 16. Exemple ConstraintLayout biaix.

En el següent exemple podem veure com hem fet servir el biaix per posicionar els elements a la pantalla. Veiem que es poden definir eixos entre dos views, o entre una view i el pare. També podem afegir padding als eixos. (figura 16).




Referències[modifica]

  1. «Layouts predefinits pals adapters» (en català). [Consulta: 16/12/2016].