Page 1

Layouts Layouts zijn extenties van de ViewGroup class die gebruikt worden om componenten te positioneren. De Android SDK heeft een reeks van ingebouwde layouts om een GUI te construeren. Je kan ze vergelijken met de LayoutManagers die we hebben gezien in Swing.

1. LinearLayout Een Linear Layout is een Box model waarin elk Child View naast elkaar wordt geplaatst. Dat kan op een horizontale of een verticale lijn zijn. Een verticale layout heeft een kolom van Views terwijl een horizontale layout een rij van Views heeft. Om een LinearLayout te configureren hebben we enkele mogelijkheden: • •

Orientation: met de eigenschap android:orientation geven we aan of we de Views horizontaal of verticaal willen plaatsen. Fill Model: De afmetingen van de Views zijn gebaseerd op hun tekst inhoud. Het kan uiteraard zijn dat de combinatie van deze Views ervoor zorgt dat alles niet op het scherm kan verschijnen. We kunnen dat voorkomen met hoogte en breedte in te stellen. Een View moet in een LinearLayout de eigenschappen android:layout_width en android:layout_height bevatten. Deze kunnen op drie verschillende wijzen worden ingevuld: o Opgeven van een specifieke dimensie van de component o Instellen op wrap_content om aan te geven dat de afmetingen van de control zich aanpassen aan de inhoud van dit component o Instellen op fill_parent waarbij we aangeven dat de componenten de nog beschikbare ruimte zal innemen in zijn container (nadat alle andere componenten zijn geplaatst) o Het zijn voornamelijk deze laatste twee instellingen die het meest gebruikt worden omdat deze niet zo afhankelijk zijn van de grote van het scherm. Wanneer we twee componenten over de nog resterende ruimte willen verdelen geven we dat eerst en vooral aan met het fill_parent attribuut en kunnen we aangeven hoe deze componenten de ruimte zullen innemen t.a.v elkaar. Dit doen we het attribuut android:layout_weight. Indien dit voor deze twee componenten dezelfde waarde heeft zullen deze ook gelijk worden verdeeld over de resterende ruimte. Wanneer component A de waarde 1 heeft en component B de waarde 2 dan zal component B tweemaal zoveel plaats krijgen van de resterende ruimte dan component A. Het weight attirbuut heeft een default waarde van 0. Een ander pattern die we kunnen gebruiken om het gewicht toe te kennen is door gebruik te maken van percentages. Hiervoor plaatsen we dan de android:layout_width waarde op 0 en de android:layout_weight waarde op het gewenste percentage voor elke View in de layout. Bijvoorbeeld


Gravity: De standaardwaarde van de uitlijning in LinearLayout is links en boven (left-top). Indien we een andere waarde willen geven doen we dat met het attribuut android:layout_gravity waarmee we willen aangeven hoe de uitlijning van een component binnen een container moet gebeuren. Er zijn verschillende waarden mogelijk: left, center, center_horizontal,…. Padding: De standaardwaarde is dat de Views vlak naast elkaar en onder elkaar staan. Om witruimte tussen de Views te plaatsen gebruiken we hiervoor het attribuut android:layout_padding.

Al deze eigenschappen kunnen zowel in het resource .xml bestand worden geconfigureerd maar er is steeds een corresponderende methode voorhanden om dat ook in runtime te kunnen verwezenlijken: setPadding(..),setGravity(…),setOrientation(…),…. Ons voorbeeld bestaat uit een Activity waarin we zowel in de resource.xml als in runtime de layout gaan veranderen.

Bij het opstarten krijgen we volgende activity

Wanneer we Vertical aanvinken zal de bovenste layout veranderen naar een verticale orientatie

Waneer we Right aanvinken zal de uitlijning van de onderste layout rechts worden uitgelijnd.

Voor onze toepassing gaan we eerst enkele String resources definiëren.


(1)Hier definiëren we een LinearLayout. (2)We geven aan dat we de Views verticaal gaan plaatsen. (dus onder elkaar) (3 – 4) Onze layout neemt het volledige scherm in beslag. Dit geven we aan met het attribuut ‘fill_parent’ (5) Onze toepassing bestaat uit selectieknoppen (RadioButton). Om ervoor te zorgen dat er slechts één kan geselecteerd worden nemen we deze op in een RadioGroup (5). Een RadioGroup is een afgeleide class van LinearLayout. We hebben hier dus een LinearLayout binnen een LinearLayout. We geven aan deze Component of widget een benaming. (orientation) (6)Vermits een RadioGroup tevens een LinearLayout is kunnen we ook instellen hoe de Views binnen deze layout geordend moeten worden. In ons voorbeeld is dat horizontaal. (7-8)We geven aan dat de afmetingen van deze Layout aangepast moet worden aan de inhoud die in deze layout komt. Opdracht lay_1: verander de layout_height in fill_parent en bekijk het resultaat. Neem een screenshot van het resultaat (emulator) en plaatst dat in de dropbox onder de naam lay1_naam_voornaam. (9) Rond de RadioGroup plaatsen we een witruimte van 50 dp. Straks gaan we dieper in op dimensies. (10-15)Plaatsen van de twee RadioButton (Views) in de RadioGroup. (17)Definiëren van een tweede RadioGroup (en dus eigenlijk van een tweede geneste LinearLayout) (18) De oriëntatie plaatsen we nu verticaal zodat de Componenten, Views of Widgets onder elkaar komen te staan. (21)Terug aangeven dat we een witruimte van 50dp rond deze Container willen maken. (22-31)Toevoegen van de RadioButtons aan de Layout.


Nu moeten we nog gedrag koppelen aan deze RadioButtons. Dit doen we in de Activity.

(10)Onze class is een uitbreiding op een Activity class, zodat ze ook dit gedrag kan uitvoeren. We geven ook onmiddellijk aan dat onze class moet luisteren naar acties van de class RadioGroup. Dit doen we door onze class de interface OnCheckedChangeListener te implementeren. Deze interface is opgenomen als een geneste structuur binnen de class RadioGroup. (11) (14-15)Declareren van twee variabelen van het type RadioGroup. Deze zullen straks een referentie zijn naar onze controls op de GUI. (18)de onCreate methode is een overriden methode van de class Activity en vormt ook de start bij het uitvoeren van een Activity. (20)We laden de layout van het scherm dat aan die Activity is gekoppeld in met de setContentView methode. Als parameter geven we onze layout resource mee die we daarnet hebben aangemaakt. (21)Binden van de RadioGroup control aan onze variabele die we in 14 hebben gedeclareerd. Hiervoor gebruiken we de methode findViewById() met als parameter de id van de control die in R.java is opgenomen. (22)Registreren van een callback methode wanneer de RadioButtons veranderen van toestand. Dit doen we met de methode setOnCheckedChangeListener die een parameter meekrijgt van het type OnCheckedChangeListener. Vermits onze Activity deze interface implementeert kunnen we hier de this operator meegeven. (23-24)We nemen dezelfde stappen voor onze RadioGroup met de naam gravity.


(33)De interface OnCheckedChangeListener kent één abstract methode namelijk onCheckedChanged() en die moeten we uiteraard overschrijven. De parameters zijn de naam van de RadioGroup waar de gebeurtenis is getriggerd en de waarde van de RadioButton. OPDRACHT: Zoek uit wat de mogelijke integere waarden zijn van de parameter checkedId. (34)Wat is de status van de RadioGroup (kan zowel voor de orientation als voor de gravity RadioGroup zijn) (35)Indien de RadioButton met id horizontal is aangevinkt (36)plaatsen we de layout van de RadioGroup (die een LinearLayout) is programmatorisch op HORIZONTAL. Dit doen we met met de constante HORIZONTAL van de class LinearLayout. (39-51) Hetzelfde principe bij het aanvinken van de overige RadioButtons. OPDRACHT lay_2: Maak een nieuwe RadioGroup waarbij er twee RadioButtons zijn opgenomen. Wanneer we op de éne RadioButton aanvinken wordt ons volledig scherm groen met de andere kunnen we de kleur opnieuw resetten. Exporteer je project en plaats dat in de dropbox onder de naam lay_2_naam_voornaam

Zoals we in de vorige listing hebben vermeld gaan we nu wat meer informatie geven over de termen dp en sp. px

pixel

in

inch

mm

millimeter

pt

Points

dp or dip

Densityindependentpixels

Exact één pixel van het scherm van het toestel. Deze eenheid wordt meestal gebruikt bij het schrijven van desktop applicaties maar is niet handig voor smartphones en tablets vermist er een grote verscheidenheid bestaat voor deze toestellen. Ongeveer één inch. Dat is gebaseerd op de fysische afmetingen van een scherm. Dat is uiteraard handig binnen de reële wereld van metingen maar opnieuw niet een aanrader voor het ontwikkelen op mobile toestellen Dat is opnieuw een maateenheid die in de reële wereld handig is. Is een metrische variant van inches: 25.4 millimeter is 1 inch Points zijn 1/72 van een inch. Deze worden meestal gebruikt voor afmetingen van lettertype en werken relatief goed voor font sizes Een DP is dezelfde afmeting als één pixel voor een 160 dpi (dots per inch) scherm. Deze afmeting is niet altijd een exacte ratio en precies maar het is de beste benadering voor het specifieke scherm waarvoor


je ontwikkelt. Het is in feite een abstract eenheid. ScaleNet zoals een dp unit is dit een pixel die geschaald is op de font size die Independent- een gebruiker selecteert. Het is de eenheid die wordt aangeraden bij pixels het instellen van fonts op een mobiel device. Op een mobiel device zal een gebruiker nogal vlug overschakelen op een andere font grote wanneer hij iets niet kan lezen. Een sp eenheid garandeert dat de user interface mee geschaald wordt bij verandering. px = dp * (dpi / 160). Bijvoorbeeld een 240 dpi scherm = 1,5 px sp

2. RelativeLayout Een RelativeLayout is een Layout waar de posities van de componenten (children) kunnen omschreven worden in relatie ten opzicht van elkaar of ten opzichte van de parent. Om een RelativeLayout werkbaar te krijgen moeten we in ons XML bestand steeds verwijzingen maken naar andere componenten of Widgets en aangeven wat hun relatieve positie is t.a.v. deze componenten.

We kunnen volgende posities aangeven t.a.v. de Container: • • •

android:layout_alignParentTop: uitlijnen van de bovenkant van de widgets met de bovenkant van de Container android:layout_alignParentBottom: uitlijnen van de onderkant van de widgets met de onderkant van de Container android:layout_alignParentLeft: uitlijnen van de linkerkant van de widgets met de linkerkant van de Container


• • • •

android:layout_alignParentRight: uitlijnen van de rechterkant van de widgets met de rechterkant van de Container android:layout_centerParentHorizontal: positioneert de widget horizontaal in het midden van de container android:layout_centerParentVertical: positioneert de widget verticaal in het midden van de container android:layout_centerInParent: positioneert de widget zowel horizontaal als verticaal in het midden van de container.

Bij programmatorische instellingen nemen deze eigenschappen een Boolean waarde aan. We kunnen ook de componenten t.a.v elkaar positioneren. • • •

android:layout_toRightOf = “@id/naamComponent : het component rechts positioneren t.a.v. component met id naamComponent. android:layout_toLeftOf = “@id/naamComponent : het component links positioneren t.a.v. component met id naamComponent android:layout_alignBaseline = “@id/naamComponent: de onderkant van het component uitlijnen t.a.v. het component met id naamComponent

(1)Aangeven dat we een RelativeLayout zullen gebruiken. (3) We gebruiken de volledige breedte van het scherm (4) De hoogte van het scherm wordt bepaald door de grote van de componenten die in deze Container zullen geplaatst worden


(5)we voorzien een witruimte van 5dp rond onze Container (6-9)plaatsen van een TextView widget (10)plaatsen van een widget van het type EditText. (14)We geven aan dat we deze component rechts plaatsen van het component met id label. Dat is het component dat we in (6) hebben aangemaakt. Het EditText widget zal dus rechts naast onze TextView komen te staan. (15)De onderkant van het EditText widget uitlijnen t.a.v. de onderkant van het component met de id label. (17)Plaatsen van een Button component (21)Het Button component plaatsen onder het component met id entry (ons EditText) (22)De rechterkant van ons Button component uitlijnen met de rechterkant van het component met id entry. (24)Een nieuw Button Component met id de waarde “cancel”. (25) (28)Het Button component met id ‘cancel’ links plaatsen van het component met id ‘ok’ (onze vorige Button) (29)De bovenkant van ons Button component met id ‘cancel’ uitlijnen op de bovenkant van het component met de id ‘ok’ (onze vorige Button) Opdracht lay_3: op regel 16 zien we een inputType dat een waarde “textUri” heeft. Heeft dat een speciale betekenis en zo ja welke? Post je antwoord in de dropbox met de naam lay_3_naam_voornaam Opdracht lay_4: Wat gebeurd er als ik regel 14 en 15 verwijder uit de code? (aantonen met een screenshot van de emulator) Post het screenshot in de dropbox onder de naam lay_4_naam_voornaam

3. TableLayout Deze layout schikt zijn componenten in rijen en kolommen. Een TableLayout bestaat uit een aantal TableRow objecten die elk een rij definiëren. Een TableLayout container toont geen randlijnen voor de rijen en kolommen. Elke cel kan een View object stockeren. De tabel kan uiteraard ook ledige cellen bevatten en cellen kunnen ook samengevoegd worden zoals in HTML. De breedte van een kolom wordt bepaald door de breedste cel in deze kolom. Een TableLayout kan echter bepaalde kolommen vergroten of verkleinen door een aanroep van de setColumnShrinkable() of setColumnStretchable() te plaatsen. Je kan ook beide instellingen op een kolom plaatsen. In dat geval zal de kolom zich aanpassen en beschikbare ruimte gebruiken maar niet meer dan nodig. Het aantal van de kolommen hangt af van de rij die de meeste kolommen heeft. De totale breedte van onze tabel is gedefinieerd door de Container. De children van een TableLayout kunnen geen waarde geven aan het layout_width attribuut. De breedte is steeds MATCH_PARENT. De cellen van een row worden één na één opgevuld (zowel in code als in XML). De nummering van kolommen is zero gebaseerd. Wanneer we een cel overslaan wordt deze beschouwd als een ledige cel.


In de TableRow op regel 28 hebben we slechts één cel nl van de View Button (29) (35)In deze TableRow hebben we wel twee cellen maar de éne zal niet zichtbaar zijn.

Niettegenstaande we meestal een TableRow zullen gebruiken als child voor een TableLayout kunnen we ook elke andere View subclass gebruiken als child voor onze TableLayout. Deze View zal dan ook getoond worden als één rij die alle kolommen van de tabel overspant. Normaal gaan we de Widgets (componenten) in de eerst beschikbare cel plaatsen. We kunnen dat patroon ook veranderen door met het attribuut android:layout_column aan te geven in welke cel een bepaalde component moet komen. Bijvoorbeeld android:layout_column = “2” Hieronder volgt een volledige layout bestand gebaseerd op een TableLayout


(2) We geven aan dat onze Layout gebaseerd is op een TableLayout. (4-5)De tabel zal zowel naar breedte als naar hoogte ons volledig scherm innemen. (6) Kolom nummer 1 (is dus de tweede kolom) zal zoveel mogelijk plaats innemen als ze nodig heeft ĂŠn als er plaats is. (7)Onze eerste rij (8)We plaatsen er een TextVIew in (9)We plaatsen er een EditText in (10)Ons EditText mag drie cellen innemen. (12)Afsluiten van onze eerste rij (13)We plaatsen een View in onze TableLayout. Vermits we hier geen TableRow hebben aangemaakt zal deze View een volledige rij innemen. In ons voorbeeld is dat een blauwe lijn met een hoogte van 5dp (15)Aanmaken van een volgende rij (16)We plaatsen een Button in de cel (17)We geven aan dat deze in de eerste cel moeten komen (19)We plaatsen een nieuwe Button in de eerste rij (20)Nu geven we aan dat hij niet in de volgende cel moet komen maar wel in de 3e kolom. (kolom 4)


4. FrameLayout Een FrameLayout class zal elk widget vastankeren op de linker bovenkant van deze layout. Dat betekent dat elk child component zal getekend worden boven het vorige. (we kunnen dat vergelijken met de CARDLAYOUT van AWT). We kunnen een bepaalde component zichtbaar maken met de methode View.setVisible terwijl de andere op de achtergrond blijven staan. Een FrameLayout tekent al zijn visible children. Het kan voor speciale effecten zorgen, in het voorbeeld gaan we zien hoe het gebruik kan worden voor een semi-transparante layer. Door een FrameLayout object te combineren met andere widgets die het volledige scherm willen innemen kan je door gebruik te maken van het gravity attribuut bepaalde widgets boven andere plaatsen. Het is dus een zeer krachtige layout wanneer er moet gewerkt worden met layers (bv in game applicaties,…)

De achtergrond van een scherm is een afbeelding. Er zijn twee drukknoppen aanwezig. Bij het activeren van de drukknop met opschrift ‘Toon Overlay’ …

Zal er een tekst verschijnen. Wanneer we de drukknop met het opschrift ‘Einde’ activeren zal het scherm zich afsluiten.

De listing van het strings.xml bestand laten we hier achterwege. Uit het layout bestand zal duidelijk zijn welke waarden we uit dat strings.xml bestand halen.


(2-4)declareren van een FrameLayout die alle beschikbare ruimte kan innemen. (6)In het FrameLayout gaan we een ImageView aanmaken die zal dienen als achtergrond afbeelding. (8-9)zal ook het volledige scherm innemen (10)We kunnen een afbeelding inschalen op meerdere manieren. Daar komen we in latere sessies op terug. Hier gebruiken we de enumeratie waarde CENTER_CROP (de enumeratie ScaleType bevindt zich in de ImageView class) om aan te geven dat de afbeelding zowel in hoogte en breedte (met behoudt van de afbeelding ratio) moet worden aangepast aan de View. (11)We geven aan wat de bron is van de afbeelding. We hebben deze afbeelding in de map drawable geplaatst van onze resources. (13)We declareren een LinearLayout waar we twee Button object zullen op plaatsen. Deze LinearLayout komt boven onze afbeelding te liggen. (17)We geven aan dat de componenten onderaan deze layout moeten geplaatst worden. (20)aanmaken van een Button die zal dienen om een layer naar voor te brengen (dus zichtbaar maken) en opnieuw te verbergen. (26)aanmaken van een Button die zal dienen om de Activity te sluiten.


Op ons scherm hebben we dus een achtergrondafbeelding geplaatst en een View van het type LinearLayout met daarin twee Button Views. In onze FrameLayout gaan we nu een volgende layer aanmaken namelijk een TextView dat we met onze drukknop (20) zichtbaar en onzichtbaar kunnen maken. We zullen het op de default waarde van de status verborgen plaatsen. (34)Aanmaken van onze TextView (36-37)neemt ook het volledige scherm in beslag. Als je bijvoorbeeld een achtergrondkleur geeft aan deze TextView zou je dat kunnen zien. (38) De TextView uitlijnen in het centrum van het scherm (41) De fontgrote instellen op 30sp (42)instellen dat de TextView niet zichtbaar is. Het attribuut android:visibility kan drie waarden aannemen (visible, invisible en gone). Het verschil tussen visible en invisible is duidelijk. De waarde gone en invisible hebben uiterlijk gezien hetzelfde effect maar er is wel een wezenlijk verschil. Opdracht lay_5: Geef het verschil tussen de waarde gone en invisible en maakt dat duidelijk met een kort codefragment in een nieuwe Android Project waarvan de uitwerking in de emulator het verschil aantoont. Exporteer dat project en plaats dat bestand in de dropbox met de naam lay_5_naam_voornaam.

In onze Activity gaan we het gedrag van de drukknoppen implementeren.


(10)Onze class is een uitbreiding op een Activity en implementeert de OnClickListener interface (voor het luisteren naar events van onze buttons.) Deze interface is een geneste interface van de View class. Ze bezit één abstracte methode namelijk onClick() (11-12)Aanmaken van twee referentievariabelen voor onze drukknoppen (17)We plaatsen onze layout resource file voor deze activity op de layout die we daarnet hebben besproken. We doen dit met de methode setContentView(…) (18-19)Zoeken van de overlay en de quit Button die we hebben gedefinieerd in ons layout resource bestand. (21-22)Onze drukknoppen toevoegen aan een Listener class. Vermits we onze Activity zelf fungeert als Listener class kunnen we deze ook meegeven als parameter. (31)Zoals we daarnet hebben aangegeven bezit de interface OnClickListener één abstracte methode onClick(). Die moeten we hier dan ook overschrijven. Als parameter geven we een View object mee. Dat is het object die de event heeft getriggerd. (32)We gaan de id opvragen van het View object die we als parameter hebben binnengekregen. Dit is de ID die we binnen R.java zullen terugvinden. (33)is dat van onze overlay_button (we gaan de id van de overlay_button vergelijken met de ID die we als parameter (32) hebben meegekregen. (34)We gaan onze TextView met de naam overlay (dat is onze tekst view waar een bericht zal inkomen) opvragen en plaatsen dat een lokale variabele van het datatype View. (35)We gaan de zichtbaarheid instellen van onze TextView. Hiervoor vragen we de huidige zichtbaarheid op : indien deze verschillend is van VISIBLE dan maken we deze view visible met aan de methode setVisibility de waarde View.VISIBLE mee te geven. In het andere geval (als deze TextView zichtbaar was) geven we de waarde View.GONE mee om aan te geven dat we onze TextView onzichtbaar willen maken. (38)Indien de trigger van het event de Button is met id quit wordt de finish() methode (39) van de class Activity aangeroepen. Hierdoor zal de Activity uit het geheugen worden genomen. Dat gaan we nog bespreken in de levenscyclus van een Activity.

In bovenstaande listing is het belangrijk op te merken dat we met drie layers zitten. De FrameLayout, de LinearLayout en de TextView. Het is belangrijk op te merken dat niet enkel de achtergrond van de TextView transparant is maar dat het ook de click events delegeert naar de layer die er eigenlijk onder ligt. Merk op dat de TextView de volledige breedte en hoogte inneemt van de volledige FrameLayout. Dat is niet onmiddellijk merkbaar, maar als je de achtergrond van de TextView een kleur zou geven is dat verschil wel merkbaar. Toch worden de events gedelegeert naar de onderliggende layer (de LinearLayout waar de Buttons op staan). Dat werkt omdat dit meer te maken heeft met het feit dat de TextView niet aanklikbaar is. Indien we onze TextView ook hadden toegevoegd aan de OnClickListener, dan zou de onderliggende Button stoppen met werken. Opdracht lay_6: voeg de TextView met de id overlay toe aan de OnClickListener methode in de Activity en laat de toepassing draaien. Klik op de drukknop en geef aan waarom de tekst éénmaal verschijnt maar als we nog eens drukken zal de tekst niet meer verschijnen. Exporteer je project met de naam lay_6_naam_voornaam.

5. ScrollView Is een Layout container voor een hiërarchische view waarin een gebruiker kan scrollen. Een ScrollView is een FrameLayout.


Vermits een ScrollView een FrameLayout is moeten we één component definiëren die de andere componenten bijhoudt waarin gescrold moet worden. Dat kan een layout manager zijn (bijvoorbeeld LinearLayout die een complexe hiërarchie van componenten bevat. Het is raadzaam om geen ScrollView met een ListView te gebruiken (ListView gaan we later behandelen) omdat een ListView zelf het gedrag van vertikaal scrollen definieert. ScrollView ondersteunt enkel het verticaal scrollen. Om horizontaal te scrollen gebruiken we HorizontalScrolView. Dat komt hier niet aan bod.

In het string.xml bestand gaan we enkele waarden definiëren.


In deze app worden kleurblokken weergegeven met daarnaast hun waarde. Hiervoor gebruiken we een ScrollView waarin we een TableLayout hebben geplaatst. Zoals je ziet kan elk soort View in deze TableLayout terecht.

(2)Definiëren van onze ScrollView layout. (6)Onze TableLayout widget waar we in kunnen scrollen (9)We geven aan dat we kolom 0 (1e kolom) kunnen uitbreiden indien dat nodig is. (10…)we definiëren enkele TableRow Views waar we een View inplaatsen (de kleurtjes) en een TextView. Elke TextView zal verticaal gecentreerd worden in de cel (16). Omdat het merendeel van deze Layout hetzelfde schema heeft namelijk <TableRow> <View> <TextView> laten we een groot stuk van dit bestand achterwege in onze listing.


(59)DefiniĂŤren van een TableRow (64)In deze rij plaatsen we enkel een ImageView widget. De bron van de afbeelding zit in de map drawable van de resources.

Layout lab