Naar content
Trending apps
  • Google Duo: videogesprekken van hoge kwaliteit

  • Gmail

  • Maps: Navigatie en OV

  • WhatsApp Messenger

  • Messenger: gratis sms'en en videobellen

Trending games
  • Fortnite

  • Minecraft Earth

  • Dr. Mario World

  • Harry Potter: Wizards Unite

  • Breaking Bad: Criminal Elements

Trending smartphones
  • POCO X3

  • Google Pixel 5

  • Google Pixel 4a

  • OnePlus Nord

  • Samsung Galaxy A51

Nieuwste tablets
  • Samsung Galaxy Tab S6

  • Samsung Galaxy Tab A 10.5

  • Samsung Galaxy Tab S4

  • Samsung Galaxy Tab S3 9.7

  • Asus Zenpad 3S 10

DevTutorial 3 - De relatie tussen Views en de Activity

· 14 september 2011

Dit is de derde DevTutorial uit een reeks van tien Android-tutorials die je opleiden van absolute beginner met totaal géén programmeerervaring tot een Androidprogrammeur met een goede basis. Leuk om te zien hoe de vorige DevTutorial was opgepakt: ik zag dat veel van jullie de app een stukje hebben uitgebreid met jullie eigen code. Ik hoop niet dat jullie te goed worden anders heb ik straks geen werk meer ;). De DevTutorial van vandaag is nog eentje met basistheorie, deze keer over Views en Layouts. De volgende DevTutorial beschrijft nog wat basis code-begrippen zodat je in DevTutorial 5 en 6 kan beginnen met de internetverbinding en het maken van je eigen classes. Daarna vallen de puzzelstukjes écht in elkaar en maak je vanaf DevTutorial 7 aardig indrukwekkende apps.

Let op!

Als je vragen hebt over deze DevTutorial  ('ik heb een fout bij deze stap', 'hoe doe je dit of dat in Android' etcetera), stel ze dan alsjeblieft niet in de comments onderaan deze pagina, maar doe dat hier op het forum! Omdat programmeervragen ingewikkelde vragen zijn, kunnen deze veel beter beantwoord worden op het forum: daar heb je uitgebreide mogelijkheden om je vraag te stellen en om geholpen te worden. Ik heb het er expres deze keer wat duidelijk neer gezet omdat er de afgelopen DevTutorial nog steeds een paar mensen waren die vragen in de comments plaatsen. Een leesfoutje kan gebeuren hoor! Maar ik hoop dat dat nu niet meer gebeurt. En weer bedankt voor de andere comments op de vorige DevTutorial, ik vond ze leuk om te lezen!

Wat gaan we doen

Deze DevTutorial beschrijft uitgebreid de werking achter Views (en Layouts die eigenlijk ook Views zijn). Dit is belangrijk om te weten omdat een app meestal begint met het ontwerpen van de schermen, daarna wordt pas meestal de code geschreven. Verder zijn Layouts en Views hele herkenbare begrippen voor iemand met weinig programmeerervaring: iedereen weet wat een knop, een invoerveld of een leegruimte op een scherm is. Door deze herkenbare begrippen nog even goed uit te legen kunnen we hier de komende komende DevTutorials op uitbouwen, hierdoor wordt je Androidkennis geleidelijk stevig uitgebreid. De volgende onderwerpen worden behandeld:

  • Wat het verschil is tussen twee belangrijke soorten Views: gewone Views en ViewGroups.
  • Wat het DNA is van de Activity class zodat je snapt hoe hij met Views kan samenwerken.
  • Hoe het bestand R.java de verbindingsbrug vormt tussen je Activity en je Layouts & Views.
  • Wat een class-hierarchy is en hoe deze ervoor zorgt dat je met één method alle verschillende soorten Views kan opvragen.

Zodra je die achtergrondkennis hebt gaan we bezig met een app die de volgende werkdag uitrekent op basis van een datum die je invoert met de DatePicker. Ook gebruiken we de CheckBox zodat aangegeven kan worden of zaterdagen wel of niet als werkdag gerekend moeten worden. Het resultaat is een app die voor jou een afspraak op de volgende werkdag kan plannen: . Dit gaan we in eerste instantie doen met een LinearLayout, vervolgens gebruiken we een Qualifier om een RelativeLayout-variant van de layout te laden in landschapmodus. Succes ermee en veel plezier gewenst!

De Relatie tussen Views, Layouts, Activities en R

Hoe Views, Layouts, Activities en de R class met elkaar samenwerken is een aardig op zichzelf staand stukje kennis, één van de belangrijke basisconcepten uit Android programmeren. Eerst wordt je het verschil uitgelegd tussen gewone Views en ViewGroups. Daarna wordt verder uitgelegd hoe een Activity werkt, wat zijn Methods zijn (het DNA van de Activity class) en hoe deze samenwerken met de twee belangrijkste groepen van Views. Daarna wordt je R.java uitgelegd, en hoe deze als verbindingsbrug werkt tussen Activities en Views. Daarna wordt er aan de hand van de het Class Hierarchy concept uitgelegd hoe alle Views (dus ook ViewGroups) afstammeling zijn van de View Class.

Twee belangrijke soorten Views: ViewGroups en gewone Views

Zoals in de vorige tutorial verteld is, is alles dat je ziet op een Androidscherm het resultaat van een View. Op het scherm is een View een rechthoekig vlak met een bepaalde breedte en hoogte. In dit vlak mag de View zelf bepalen wat hij laat zien.

Bijvoorbeeld bij een knop (Button) is dat een plaatje van een knop met daar bovenop een tekst geprojecteerd waardoor de gebruiker die View ziet en herkent als een knop. Bij bijvoorbeeld een tabblad houder (TabHost) deelt de view zichzelf op in twee gedeelten: (1) het bovenste gedeelte waarin alle tab-knoppen getoond worden en (2) het grote gedeelte daaronder waarin de inhoud van de verschillende tabs getoond kan worden, afhankelijk van welke tab actief is.

De Views die je het meest gebruikt, zijn op te delen in twee soorten: ViewGroups en gewone Views. Bij gewone Views moet je denken aan knoppen, selectievakjes, afbeeldingen van plaatjes, tekstvelden. De tweede soort, ViewGroups, zijn views die andere views kunnen bevatten: vandaar ook de naam ViewGroup --> Groep van Views. Wanneer je een ViewGroup gebruikt, dan reserveer je ruimte op het scherm (door de hoogte en breedte van de ViewGroup in te stellen) zodat je die ruimte vervolgens kan gebruiken om andere views op te laten zien zoals bijvoorbeeld een EditText of een Button. Dit principe waar een ViewGroup in zichzelf andere views kan bevatten noem je de layout-hiërarchie. Ter verduidelijking is hier de layout-hiërarchie van de layout van de eerste tutorial: Je ziet dat de EditText en de Button "in" de LinearLayout zitten (een LinearLayout is een typisch voorbeeld van zo'n ViewGroup). Ook als je de xml-code bekijkt van layout.xml zie je dit terug;  op regel 3 wordt de openingstag van LinearLayout gebruikt, dan volgen de tags van de EditText en de Button en daarna pas op regel 25 wordt de LinearLayout gesloten: (ik heb even wat extra enters in het bestand gezet zodat hij overzichtelijk is). Deze layout-hiërarchie is nog een vrij simpele: meestal heb je meerdere ViewGroups die in elkaar zitten en dan weer hun eigen gewone Views bevatten. De manier hoe de Views gepositioneerd kunnen worden, hangt af van het type layout waar ze in zitten. In een LinearLayout worden alle views (knoppen/tekstvelden/keuzeknopje etcetera) die je erin stopt onder elkaar getoond; er worden dan geen Views naast elkaar gezet. In een RelativeLayout stel je de posities van de views in aan de hand van elkaar en de randen van de layout. In een ListView worden de views onder elkaar geladen met scrollmogelijkheden zodat de gebruiker het idee krijgt dat je  door een lijstje aan het scrollen bent. Zometeen gaan we twee apps maken waarin we de LinearLayout en de RelativeLayout gebruiken. Voordat het zover is, wordt eerst nog beschreven hoe in Eclipse de Layout xml-bestanden gekoppeld worden aan de sourcecode die je schrijft in de map src.

public class MainActivity extends Activity

We gaan nu dieper in op wat een Activity is en hoe die samen kan werken met een layout. Dit gaat voornamelijk door middel van de methods die je kan gebruiken in je Activity class. Er wordt uitgelegd waar die methods vandaan komen om in de paragraaf die hierna komt uit te leggen hoe die methods inhaken op R.java, een bestand dat door de ADT (Android Developer Tools) gegenereerd wordt. Een erg belangrijk element hierin is het woordje extends: dit wordt nu uitgelegd voor je eigen class MainActivity, maar kan je ook gebruiken in (bijna) alle andere classes die je ooit schrijft. Een layout gebruik je altijd in combinatie met een Activity. Wanneer een Activity actief is, dan is die de baas over het scherm en bepaalt dan wat er op het scherm getoond moet worden. Een voorbeeld hiervan is toen je dit gebruikte in de vorige DevTutorial door de method setContentView aan te roepen in je Activity class, dit deed je bijvoorbeeld op regel 19 van de class MainActivity in de vorige DevTutorial:  (open zelf ook nog even Androidproject MoneyConvert in Eclipse, ik ga het voorlopig even als voorbeeld gebruiken). Zoals in de vorige tutorial beschreven, is een method een stukje functionaliteit van een class die je afzonderlijk uit kan voeren. Dit uitvoeren kan je doen door de naam van de method op te schrijven, gevolgd door '(' en ')'. Tussen de haakjes kan je, wanneer de method daarvoor geschikt is,  een extra bericht meesturen om te zorgen dat de method op een bepaalde manier wordt uitgevoerd. Dit kan je zien op regel 19 van bovenstaand screenshot wat in gewoon Nederlands het volgende betekent: "zorg ervoor dat het scherm er anders uit komt te zien: ga de layout veranderen. En ik heb er nog extra bericht bij: je moet dit doen met de layout main." Het vreemde aan bovenstaand stukje code is dat de method setContentView wordt aangeroepen terwijl die nergens te bekennen is in de class: als je naar je eigen bestand MainActivity.java kijkt zie je namelijk nergens de method setContentView. Dit heeft te maken met hoe je class MainActivity gedefinieerd is in je bestand MainActivity.java. Of anders gezegd: dit hangt af van je beschrijving van je class block waar je de class MainActivity begint. Zoals je weet uit de vorige DevTutorial maak je een class block door een block (met een '{' en een '}') neer te zetten en daar vóór te beschrijven dat je een class block maakt door het woordje class te gebruiken. Dit kan je zien op regel 10:Met class MainActivity zeg je: "de code in dit block is een class, en de class geef ik de naam MainActivity". Wat public betekent is nu nog niet belangrijk.  Maar waar we het nu over gaan hebben is wat daarachter staat, namelijk extends Activity. Waarbij vooral het woord extends erg belangrijk is. Wanneer je het woord extends gebruikt in Android dan betekent dat je een een uitbreiding maakt op de class die je daarna noemt, in dit geval Activity (extends is Engels voor 'uitbreiden' of eigenlijk 'breidt uit'). En alles wat je verder in je huidige class (in dit geval MainActivity) schrijft, wordt gemengd met de oorspronkelijke class: je neemt een andere class als basis en daar werk je mee verder en dat wordt je eigen class inclusief de functionaliteit (hoofdzakelijk methods) van de class die je uitbreidt. De class die je als basis neemt heet de superclass, de class waar je mee verder werkt heet de subclass. In dit voorbeeld is Activity de superclass en MainActivity de subclass. Dus wanneer je een class subclasst door het woord extends te gebruiken, dan werk je eigenlijk in een al bestaande class en voeg je alleen maar dingen toe. Maar hoe weet jij nou wat het woordje Activity achter extends betekent, en nog belangrijker: hoe weet Android nou wat Activity betekent? Dit werkt op dezelfde manier als wat je in de vorige DevTutorial deed met View, Button, EditText of Toast. Op regel 3 van MainActivity.java staat:Hiermee vertel je aan Android dat je met de Activity class wil werken en dat Android hem kan vinden in de package android.app. Wanneer Android je code inleest om die te gebruiken en hij komt op regel 10 dan denkt hij ongeveer dit:

"ah, de programmeur wil een nieuwe class aanmaken, met de naam MainActivity. Oh wacht even, ik zie aan het woordje extends dat het een uitbreiding is op de class Activity. Wat is Activity ook alweer? Oh ja, dat heeft de programmeur aan het begin van het bestand beschreven bij zijn import statements: ik heb op regel 3 gelezen dat de class Activity in de package android.app zit."

Vervolgens kopieert Android de code van de class Activity uit de package android.app bij je code van je eigen class MainActivity. De methods die gekopieerd worden kan je hier zien: http://developer.android.com/reference/android/app/Activity.html. Als je iets naar beneden scrollt naar het kopje public methods zie je alle methods die aanwezig zijn in de Activity class: . Dit betekent dat al deze methods door jou gebruikt kunnen worden omdat Activity de superclass is van je subclass MainActivity (daaronder staan de protected methods, waarom je deze ook mag gebruiken kom ik in een latere DevTutorial op terug). Doordat je weet welke methods de Activity class heeft, kan je afleiden wat je ermee kan doen en dus ook hoe de Activity class zich gedraagt. Hierdoor zou je de beschrijving van alle methods van een class samen ook het DNA van een class kunnen noemen. Als je iets naar beneden scrollt zie je de method findViewById staan: (en als je op findViewById klikt, kom je op de uitgebreide method-beschrijving: ). Deze heb je in de vorige DevTutorial gebruikt om een Button en een EditText op te zoeken. Toen je deze code schreef:Maakte je dus gebruik van de method findViewById die je op die webpagina van de Activity class ziet staan. Scroll je nog iets verder naar beneden, dan zie je de method "setContentView" staan: (en als je op setContentView klikt kom je op de uitgebreide method beschrijving: ). Deze method gebruikte je dus op regel 19:En toen maakte je dus gebruik van de method setContentView die je op de webpagina van de Activity class ziet staan. Op dezelfde manier zijn er veel meer methods die je al kan gebruiken in je class MainActivity omdat je hem subclasst op basis van de Activity superclass. Kijk eens door de lijst van methods zodat je een beetje door hebt dat er al erg veel functionaliteit voor je beschikbaar is die kan gebruiken voor je eigen Activities. Kijk bijvoorbeeld ook even naar de method getLocalClassName en probeer te begrijpen wat die doet. Bij het subclassen van de Activity class geef je je eigen class al een hele duidelijke basis: hij beschikt over meerdere methods (zoals setContentView, findViewById en onCreate) die je dwingen je eigen class op een bepaalde manier verder op te bouwen. Maar aan de andere kant is dat ook erg handig, probeer maar eens zelf een scherm op te bouwen zonder gebruik te maken van de methods die je in de Activity class vindt:

  • om dit beter kunnen begrijpen, haal even op regel 10 van MainActivity.java 'extends Activity' weg zodat er alleen nog maar dit staat op regel 10:

Daar sta je dan met je eigen mini-class..... er staan overal foutmeldingen, simpelweg omdat je niet meer gebruik kan maken van de methods die in de Activity class zitten. Ik heb er even een screenshot van gemaakt waar je in de onderste helft van het scherm ook de beschrijvingen van de foutmeldingen ziet staan: . Er staat een fout op regel 20 en 21 omdat je niet meer de method findViewById kan gebruiken (of je moet hem nu zelf maken) met als foutmelding: 'The method findViewById(int) is undefined for the type MainActivity' dat in gewoon Nederlands betekent 'ik zie nergens een method findViewById staan, dan mag je die method ook niet aanroepen'. Eenzelfde soort fout is ook te zien op regel 19 omdat de method setContentView niet meer aanwezig is. De andere foutmeldingen hebben ook te maken met ontbrekende methods. Behalve regel 28,  dat is een iets andere soort foutmelding, daar kom ik een andere DevTutorial nog op terug.

  • Nu kan je 'extends Activity' weer terugzetten zodat je foutmeldingen weer verdwijnen (wel jammer van die mooie rode kleur trouwens... maar goed).
Ik schreef net dat door de Activity class te subclassen je gebruik kan maken van onder andere de method onCreate. Deze method kan je ook zien op de webpagina van de Activity class die ik je net gaf. Maar het rare is dat wanneer je naar je eigen MainActivity class kijkt je óók een method onCreate ziet staan. Wat er hier eigenlijk gebeurt is het overschrijven van een method, hiervoor moet je een regel boven je method de tekst @Override zetten (zie regel 16). Hierdoor zeg je tegen Android dat je de method wil overschijven en als gevolg hiervan controleert Android of de method die je wil overschrijven wel aanwezig is in de superclass. De tekst @Override is niet verplicht maar is wel handig om te doen, dan weet je beter wat je code doet, en je krijgt een foutmelding als er geen zelfde method in de superclass aanwezig is: dit voorkomt fouten. Wanneer je een method overschrijft, kan je deze alsnog aanroepen. Dat doe je met het woordje super wat je kan zien op regel 18. Bij sommige methods is het verplicht om de super method aan te roepen (zoals bij onCreate, onPause, onDestroy en onResume). Het handige aan methods overschrijven is dat je weet wanneer de methods worden uitgevoerd, hierdoor kan je code schrijven in specifieke methods zodat je weet dat je code op vaste momenten wordt uitgevoerd (zoals je al gedaan hebt met onCreate in de vorige DevTutorial en je nog vaak zult doen).

Nu we aan het einde zijn van de uitleg over extends hoop ik dat je het volgende blijft onthouden: door een class te subclassen bouw je voort op die class (superclass) en kan je zo de methods gebruiken van de superclass in je subclass die je zelf verder schrijft. Om zelf een Activity te maken moet je de Activity Class subclassen, je eerste Activity kan je automatisch aanmaken bij een nieuw Androidproject door een vinkje te zetten bij Create Activity. Door de Activity class te subclassen krijg je onder andere de belangrijkste layout-gerelateerde methods setContentView en findViewById.

Layouts, R.java, setContentView en findViewById

Nu wordt beschreven hoe de methods setContentView en findViewById via R.java inhaken op de layouts die je maakt voor je app. Kijk nog eens naar de uitgebreide beschrijvingen van de methods findViewById en setContentView: Je ziet achter de naam van de method tussen haakjes de volgende teksten staan: int id en int layoutResID. Dat betekent dat wanneer je deze methods aanroept je een bericht mee moet geven dat beschrijft welke View je wilt opzoeken of welke layout je wilt gebruiken. Het woord int betekent dat het bericht een afgerond getal moet zijn (zoals 9342802 of 123022), en id en layoutResId moet je zien als een uitleg die beschrijft wat het nummer betekent: deid (identificatie) van de View die je wil opzoeken of de layout die je wilt laden. Views en layouts zijn beiden resources, dat zijn externe bestanden (of delen daarvan) die in je app aangeroepen worden door je code. Bij layouts zijn deze resources op zichzelf staande layout xml-bestanden en bij Views zijn de resources delen van deze xml-bestanden. Zoals je misschien al weet staan deze resources in de map res die je ziet staan in het Package Explorer Window. De id (dat afgeronde getal) dat je als bericht moet meegeven bij de methods is een unieke verwijzing naar de betreffende layout of View. Die id (dat afgeronde unieke getal) hoef je niet zelf te onthouden maar kun je krijgen door R.id.id_van_view of R.layout.naam_van_layout te typen. Let er dan wel op dat je id_van_view en naam_van_layout respectievelijk moet vervangen door de id van de View en de  bestandsnaam van de layout die je wil gebruiken (zoals bijvoorbeeld R.id.etxtAmount verwijst naar de EditText uit main.xml of R.layout.main verwijst naar het layout-bestand main.xml).  Wanneer je R.id.id_van_view gebruikt, dan maak je eigenlijk een verwijzing naar het getal id_van_view (dus bijvoorbeeld etxtAmount) die in de class id staat die op zijn beurt in de class R staat. En wanneer je R.layout.naam_van_layout gebruikt, dan maak je eigenlijk een verwijzing naar het getal naam_van_layout (dus bijvoorbeeld main) die in de class layout staat die op zijn beurt in de class R staat. Deze class R kan je vinden in het Package explorer window in het bestand R.java in de map gen. Als je een layout bestand hebt aangepast en hebt opgeslagen zonder foutmeldingen, dan ververst Eclipse automatisch het bestand R.java. Pas wanneer je layout of View daadwerkelijk een getal toegewezen heeft gekregen in in R.java, dan kan je hem gebruiken in je java class. Als er een fout zit in een layout-bestand dan wordt R.java niet automatisch ververst omdat er een fout optreedt waardoor het automatisch genereren wordt afgebroken. Dus stel je bent in een main.xml bezig maar in een ander layout bestand zit nog een fout, dan moet je die fout eerst oplossen voordat je R.layout.main kan gebruiken in je Activity. Dit heeft te maken met hoe Eclipse het bestand R.java genereert. Weet je nog dat je de namen die je gaf aan je Button en EditText moest laten beginnen  met @+id/? Het apenstaartje is een signaal voor Eclipse dat hij een verwijzing tegenkomt die te maken heeft met een nummer (in het geval van een Button is dat een id). Het plusje is vervolgens een signaal dat Eclipse hier een nummer voor moet genereren en "id/" betekent dat het nummer geplaatst moet worden in het class block genaamd "id" in R.java. De naam die daarna komt is de naam die het nummer gegeven wordt in het code block. Dit staat ook uitgelegd in de volgende screenshot: . In deze screenshot staat in het Package Explorer Window een paars rechthoek om de map layout. Alle layout-bestanden die je ooit zult maken komen automatisch terecht in het paarse rechthoek in het bestand R.java. Op dezelfde manier worden alle Views die je ooit aanmaakt in alle layout-bestanden automatisch gegenereerd in de class id, de class die boven layout staat. Een belangrijke eigenschap van het bestand R.java is dat het automatisch gegenereerd wordt. Om deze reden mag je R.java zelf niet aanpassen; dat moet je automatisch door Eclipse laten doen. Je moet dit niet forceren of zelf dingen gaan bijschrijven in R.java: het enige dat je kan doen is .xml bestanden schrijven zonder fouten erin. Als je in de menubalk bovenin Eclipse bij Project geen vinkje hebt staan bij Build Automatically dan wordt je R.java alleen ververst als je je app opstart of als je op Project --> Build Projectklikt. Doordat R.java automatisch wordt bijgehouden door Eclipse kan je in je java code op een makkelijke manier verwijzen naar de views, layouts en andere dingen die je beschrijft in de  de map res: R.java is de verbindingsbrug tussen de bestanden die je in de map res maakt/aanpast en de java code die je schrijft in de map src (alle andere soorten bestanden in de map res hebben ook hun eigen class in de class R, hier leer je over in een volgende tutorial).

Als je goed kijkt zie je dat R.java eigenlijk een class is: op regel 10 begint het block van de class met de naam R. Binnenin het block van de class R staan zogenaamde inner classes beschreven: een class die weer staat in een andere class. Deze inner classes zijn atrr, drawable, id, layout en string. Op regel 8 zie je staan bij welke package (groep van classes) deze class hoort. Als je die naam vergelijkt met het begin van MainActivity.java zie je dat ze bij dezelfde package horen. Was dit niet het geval geweest, dan had je nog het woord import moeten gebruiken als je in MainActivity wilde verwijzen naar de layouts en Id's (voor classes die in dezelfde package zitten hoef je geen imports te gebruiken, weet je nog?).

Wanneer je findViewById of setContentView gebruikt dan geef je dus eigenlijk een getal mee die staat in het bestand R.java. R.java is gegenereerd door de ADT van Eclipse en Android gebruikt de nummers uit R.java om tijdens het draaien deze te koppelen aan de layouts en de Views. Wanneer je dus dit schrijft:

Zeg je eigenlijk in gewoon Nederlands: "zorg ervoor dat het scherm er anders uit komt te zien: ga de layout veranderen. En ik heb er nog extra bericht bij: je moet dit doen met de layout waarvan het nummer met de naam main staat in de class layout die in de class R staat." Wanneer je dit schrijft:Zeg je eigenlijk in gewoon Nederlands: "van de layout die je nu geladen hebt in het scherm, ga op zoek naar een View die als nummer de naam etxtAmount heeft. Dit nummer kan je vinden in de class id die in de class R zit." Als gevolg daarvan gaat Android op zoek naar de View met dat nummer en geeft je een View terug.

The class hierarchy (hiërarchie van classes)

Het begin van deze DevTutorial beschreef dat alle knoppen, velden, leegruimtes en andere elementen die je op scherm ziet, Views zijn. Bij een EditText kan je dat zien door naar de beschrijving ervan te gaan. Je ziet daar bovenin staan extends Textview , dat betekent dat de EditText een subclass is van TextView. Dus alle methods die aanwezig zijn in een TextView zijn ook aanwezig in een EditText: hierdoor is elke EditText die je ooit gebruikt voor Android ook een TextView, maar niet elke TextView is een EditText (net zoals dat alle koeien dieren zijn, maar niet alle dieren zijn koeien). Kijk je naar de beschrijving van de TextView, dan zie je staan extends View , dat op dezelfde manier betekent dat elke elke TextView (en dus ook een EditText) een View is. Dit kan je ook andersom bekijken: als je op de beschrijving van de View class kijkt zie je bovenin staan Known Direct Subclasses en Known Indirect Subclasses: . Je ziet daar bij subclasses ook de class ViewGroup staan. Als je daarop door klikt, zie je de LinearLayout en de RelativeLayout beide een subclass zijn van ViewGroup: . Of andersom gezegd: de LinearLayout class en de RelativeLayout class zijn allebei een subclass van ViewGroup, en omdat ViewGroup een subclass is van de View class zijn de LinearLayout class en de RelativeLayout class ook allebei Views. Dit is wat ik bedoelde dat alles wat je ooit ziet in je Androidscherm een View is. Belangrijk puntje daarbij is dus dat er veel verschillende soorten Views zijn met hun eigen categorieën. Nu kan je deze regels code ook beter begrijpen:Deze roepen zoals je weet deze method aan: Bij de beschrijving van de method staat in gewoon Nederlands: "Deze method zoekt een View in de layout xml-bestanden met het opgegeven unieke nummer". Wanneer je deze method gebruikt om een View op te halen (én de View is geladen in de layout die je hebt geladen met setContentView) dan krijg je een View terug. Het enige wat je dan nog moet doen is de View omvormen naar het juiste subclass-type. Op de bovenste regel van de regels code hierboven weet je dat een View terugkrijgt die ook nog als subclass type een EditText is: dat heb je immers zelf ingevuld in de Graphical Layout Editor Tab toen je je layout aan het ontwerpen was. Dit omvormen doe je door het class-type er tussen haakjes voor te zetten. Dit heb je al met de EditText gedaan hebt door er (EditText) voor te schrijven en dat vervolgens in de EditText mEtxtAmount te stoppen. In Java (en Android) noem je dit omvormen casten. Wanneer je in plaats van een EditText een Button ophaalt (zoals hierboven in de onderste regel code) dan moet je er (Button) voor zetten. Of ander voorbeeld: stel dat je de LinearLayout in je layout xml-bestand ook een id meegeeft dan kan je de LinearLayout ophalen met findViewById (een LinearLayout is uiteindelijk ook een subclass van de class View, weet je nog?). Daarbij kan je hem in een LinearLayout in je code te stoppen door de verkregen View met (LinearLayout) te casten (=om te vormen) naar een LinearLayout. Afhankelijk van wat voor type je een object toewijst kan je bepaalde methods wel of niet gebruiken. Stel dat je de EditText niet zou casten naar een EditText maar gewoon een View zou laten zijn door dit te typen:dan kan je alleen nog maar de methods op het object mEtxtAmount gebruiken die horen bij de View class. Wil je later toch de methods van de EditText class gebruiken dan moet je hem alsnog casten:Door op http://developer.android.com/reference/packages.html op de superclass of een van de subclasses te klikken van een class kan je alle classes van Android uitpluizen. De allerhoogste superclass van alle classes is de class Object. In DevTutorial 6 gaan we dieper in op de class hierarchy.

Gefeliciteerd! Je weet nu aardig goed hoe normale Views en ViewGroups en de layouts zelf in een layout xml-bestand geplaatst worden, deze vertaald worden naar een nummer in de R class en hoe je de methods setContentView en findViewById van de Activity class moet gebruiken om deze normale Views, ViewGroups en de layout te gebruiken. Ook weet je hoe het komt waarom je deze methods kan gebruiken in je eigen Activity subclass (bijvoorbeeld MainActivity) en hoe je de Views die je krijgt uit de method findViewById naar het juiste subclass-type van de View class kan casten. Deze Androidconcepten samen zijn een aardig op zichzelf staand stukje kennis. Er zijn een paar (ik schat 8-12) belangrijke Android-basisconcepten, waarvan je het "Layout-R-findViewById&setContentView"-concept nu vrij goed snapt.

De app ontwerpen: LinearLayout

Om met de LinearLayout te leren werken gaan we een afspraakplanner maken: een app waarop je een datum kan invoeren waarop je een afspraak wil plannen. Valt die geselecteerde datum op een zondag of een zaterdag, dan wordt de afspraak ingepland op de daarna komende maandag. De gebruiker kan kiezen of hij ook afspraken wil accepteren die op zaterdag vallen. Je leert ook te werken met de CheckBox en de DatePicker.

  • Maak een nieuw Androidproject aan met de volgende instellingen: .

De LinearLayout is een typisch voorbeeld van een ViewGroup: met een LinearLayout reserveer je een rechthoekig vlak op het scherm. De Views die je dan weer in je LinearLayout plaatst worden getoond in het rechthoekig vlak. De Views die je in de LinearLayout stopt worden in één lijn getoond: deze lijn kan je door middel van de property Orientation horizontal of vertical instellen (dus alle Views worden onder elkaar óf naast elkaar getoond). Het aanmaken van een nieuwe LinearLayout kan je doen met de Android xml file creator: een scherm waarin je in kan aangeven wat voor type bestand je wil hebben. Als je kiest voor het type Layout kan je vervolgens aangeven dat een Layout van het type LinearLayout wilt maken. Dat betekent dat de ViewGroup die alle andere Views in je layout bevat een LinearLayout is. De ViewGroup die alle andere Views bevat noem je ook wel het Root element (root is het engels voor wortel, en betekent in dit geval de basis/oorsprong van de rest van de layout). Zodra je een nieuwe LinearLayout aanmaakt verschijnt hij in het bestand R.java en kan je hem gebruiken in je Activity met de method setContentView. Wanneer je je layout aan gaat passen in de Graphical layout editor tab kan je heel gemakkelijk de Views die in je layout wilt hebben slepen in het Outline window: dan worden de View automatisch achter of onder elkaar neer gezet, afhankelijk van de Orientation property. We gaan nu een LinearLayout aanmaken, de oriëntatie aanpassen, views aanpassen en hem laden in de class MainActivity.

  • Verwijder de standaard layout main.xml door er met de rechtermuisknop op te klikken en vervolgens te klikken op "Delete" . (en klik op Ok)
  • Als je nu naar R.java gaat (die zit in de map gen) dan zie je "main" er niet meer in staat (). Dit komt omdat je hem net hebt verwijderd. Dit kan je ook zien door te kijken naar MainActivity.java, er is een foutmelding op regel 11. Deze foutmelding bestaat omdat de verwijzing naar het nummer main dat staat in de inner class layout van R (R.layout.main) niet meer bestaat zoals je hebt kunnen zien bij de screenshot aan het begin van stap 3.
  • We gaan nu een nieuw layout maken, en wel een LinearLayout. Klik met rechtermuisknop op de map layout en klik op New --> Android XML File.
  • Kijk nu bij de tekst 'What type of resource would you like to create?', let er op dat je Layout aanvinkt dat betekent dat je een layout-xml-bestand wil maken.
  • Je moet nu in het file vakje een naam opgeven. De naam van de layout is tegelijkertijd de naam van het xml-bestand. We willen de layout linearlayout noemen dus, dus vul bij file de volgende naam in: linearlayout.xml.
  • Vervolgens moet je onderaan het scherm het type Layout opgeven, je moet hier LinearLayout opgeven.
  • Je scherm moet er nu als volgt uit zien: . klik op Finish. Je hebt nu een layout aangemaakt die je kan laden in je app.
  • We gaan nu MainActivity opdracht geven om de layout te laden die we net gemaakt hebben. verander regel 11 van MainActivity.java naar:

Je ziet dat de foutmelding is verdwenen. Omdat je net de layout linearlayout hebt aangemaakt, is het nummer linearlayout aangemaakt in de subclass layout van de class R. Dus wanneer je een verwijzing maakt naar de class R, en daarvan de inner class layout, en daarvan het nummer linearlayout, dan weet Android nu weer wat je bedoelt

  • (vergeet niet het bestand op te slaan).
  • Ga nu weer terug naar je layout-bestand die je linearlayout.xml genoemd hebt en open hem in de Graphical Layout editor-tab. Zorg ervoor dat je Android versie op 2.1 staat: (kijk naar het rode vierkant).
  • Sleep een Button van de lijst links van het zwarte rechthoek op het rechthoek en laat de muisknop los. Je hebt nu een Button in je layout geplaatst. Doe hetzelfde met een CheckBox: sleep een CheckBox naar het zwarte rechthoek en laat hem daar los. Sleep nu een DatePicker naar het rechthoek (die staat links onder het mapje Time & Date), en laat hem onder de CheckBox los. Je ziet nu alle drie de Views onder elkaar staan: .
  • Ik wil je even iets laten zien van de LinearLayout: klik in de het Outline window op LinearLayout en ga vervolgens naar het Property window. Ga op zoek naar de property Orientation en zet die op horizontal. Nu zie je al je Views in de LinearLayout achter elkaar staan in plaats van onder elkaar: . Als je ooit de LinearLayout in zijn horizontale stand wil gebruiken moet je dus deze property aanpassen. Zet de Orientation property nu weer terug op vertical: .
  • Ga nu eens naar de properties van de Button die je hebt toegevoegd aan je layout (dus klik in het Outline window op button1). Scroll helemaal naar beneden in de properties-lijst: je komt dan op een gegeven moment het woordje Misc tegen: . Dit is een afkorting van het Engelse woord miscellaneous dat in het Nederlands "overig" betekent. Bij Misc kan je de instellingen van de Button aanpassen die te maken hebben met het weergeven van de Button. Omdat deze Button in een LinearLayout staat, kan je maar een beperkt aantal dingen aanpassen. Ik noem even de belangrijkste:
ElementBeschrijving
Layout heightDe hoogte van de Button. Je moet dit invullen met dip door bijvoorbeeld 60dip in te vullen, maar je kan er ook voor kiezen dat de Button zo groot wordt als hij zelf vindt dat nodig is door er wrap_content in te vullen. Een andere optie is om de Button zo groot mogelijk te maken, dit doe je met fill_parent.
Layout widthZelfde als Layout height, maar dan in de breedte.
Layout marginhoeveel extra ruimte moet er geplaatst worden tussen de Button en andere widgets die op het scherm zitten. Je moet dit invullen in dip, een voorbeeld is om in te vullen: 20dip.

 

dip

dip is een afkorting voor de Engelse term "Density-independent Pixels", dat in gewoon Nederlands betekent: een maat voor het meten van de afstand op het scherm gebasseerd op de fysieke grootte. Op een normaal scherm (bijvoorbeeld het scherm van de HTC legend of de HTC Hero is 1dip gelijk aan 1 pixel. Maar op een scherm waar er meer pixels per vierkante centimeter zijn (bijvoorbeeld het scherm van de Samsung Galaxy S), dan is 1 dip gelijk aan 1,5 pixel. Wanneer je alle afstanden en groottes beschrijft in dip heb je in de meeste gevallen een redelijk aardige maatstaf om je scherm op te bouwen, onafhankelijk van het toestel waarvoor je ontwikkelt.
  • Om te kijken wat deze instellingen voor effect hebben, zet de Layout margin op 20dip en de Layout width op fill_parent. Sla het bestand op, bekijk vervolgens weer je layout en dan zie je dit scherm: . Je kan zien dat de Button nu over de hele breedte getoond wordt, dit komt omdat je de layout width van de Button gezet hebt op fill_parent. fill_parent betekent dat een View (in dit geval de dus de Button) wordt uitgestrekt tot aan de randen van de view waar hij in zit (in dit geval dus de LinearLayout). je ziet ook dat er een lege ruimte is aan alle kanten van de Button. Dit komt omdat je Layout margin op 20dip hebt gezet.
  • We gaan deze layout gebruiken om een app te maken die een afspraak inplant op je volgende werkdag, op basis van de datum die je opgeeft. Pas de volgende properties aan:
ElementPropertyWaarde
ButtonTextPlan mij in
ButtonOn clickplanMe
ButtonId@+id/btnPlanMe
CheckBoxId@+id/cbSaturdaysAllowed
CheckBoxTextZaterdagen toegestaan
CheckBoxLayout margin left20dip
DatePickerId@+id/dpDate
DatePickerLayout margin left20dip
DatePickerLayout margin top20dip
  • Sla het bestand op. Je scherm moet er nu als volgt uit zien: .

De app programmeren: CheckBox en DatePicker

De app moet geprogrammeerd worden zodat hij de geselecteerde dag als antwoord teruggeeft. Selecteer je per ongeluk een datum die op een zaterdag of een zondag valt, dan moet de app automatisch als antwoord geven de maandag die daarop volgt. Wil je ook op zaterdagen werken, dan moet de app daar ook rekening mee houden. Dit moet net zoals in de eerste tutorial geprogrammeerd worden in MainActivity.java.

De komende punten gaan over het definiëren van Views en de onClick method van de button aanmaken. Her en der worden je nog tips gegeven hoe je dit aan moet pakken, maar er wordt eigenlijk vanuit gegaan dat je dit al vanuit jezelf kan doen zoals je leerde aan het einde van de vorige DevTutorial (lees dat anders nog een keer opnieuw).

Views definiëren en onClick method maken

  • Aan het begin van de class MainActivity, definieer een Button die je mBtnPlanMe noemt, definieer een CheckBox die je mCbSaturdaysAllowed noemt en definieer een DatePicker die je mDpDate noemt.
  • Wederom zie je foutmeldingen staan. Deze foutmeldingen betekenen dat Android niet weet wat je met een Button, een CheckBox en een DatePicker bedoelt. Al deze Views staan in package android.widget, dus je moet de volgende classes importeren: android.widget.Button, android.widget.CheckBox en android.widget.DatePicker. Dit moet je doen door het woord import te gebruiken en deze drie zinnen te plaatsen boven het begin van het code block van je class (op dezelfde plek waar de andere imports ook staan).
  • Koppel nu de widgets die je in je layout bestand hebt beschreven aan de widgets die hebt beschreven in je class. Het koppelen moet je doen in je methode onCreate, nadat je de layout linearlayout geladen hebt.
  • Je class moet er nu als volgt uit zien:

  • Toen je de Button mBtnPlanMe beschreef in de layout linearlayout heb je On Click gezet op planMe. Dit betekent (zoals je misschien nog weet van de vorige tutorial) dat wanneer op mBtnPlanMe geklikt wordt, Android zoekt naar de method planMe in je class MainActivity en die vervolgens uitvoert. Om te zorgen dat je class dit ondersteunt moet je de method planMe toevoegen, ook moet je net zoals in de vorige tutorial controleren of de ingedrukte View wel de button mBtnPlanMe is:

  • En voeg het benodigde import statement weer toe (als je niet meer weet wat je moet doen: kijk naar DevTutorial 2 en kijk bij het kopje "Button: On click")

DatePicker: Datum uitlezen en bewerken

In de method planMe willen we de berekening uitvoeren die deze app moet doen. We gaan eerst de datum opvragen die geselecteerd is. Deze datum willen we ergens kwijt. Om een datum op te slaan kan het Calendar object gebruikt worden. Dit object komt van een class die methods heeft om de datum te onthouden en vervolgens je kan vertellen op welke dag van de week die datum valt.

  • Om een Calendar aan te maken en te vullen met de datum van de DatePicker moet je het volgende toevoegen aan de method planMe in het if block:

Eerst wordt een Calendar aangemaakt, daarna worden methods aangeroepen van de DatePicker om geselecteerde dag, maand en jaar op te vragen. Daarna wordt de method set aangeroepen. Dit is een method die drie berichten nodig heeft en op basis daarvan zichzelf instelt op een bepaalde datum (Vergeet niet de class java.util.Calendar te importeren).

  • Om te zorgen dat de datum waarop ingepland wordt niet een zondag is, gaan we een if block maken die alleen wordt uitgevoerd wanneer de geselecteerde dag een zondag is. Doe het controleren of de geselecteerde dag een zondag is als volgt:

Zoals eerder verteld kan je met een if-code block een vraag stellen die je binnen de haakjes opschrijft. Wordt die vraag met ja beantwoord, dan wordt het code block dat je daarna beschrijft uitgevoerd. Aan de linkerkant van de == wordt de method get opgeroepen op je Calendar object cal. Deze method kan het jaartal (Calendar.YEAR), nummer van de maand (Calendar.DAY_OF_MONTH) of een andere waarde teruggegeven aan de hand van welk bericht je mee stuurt. In dit geval word dagnummer van de week opgevraagd. Aan de rechterkant van de == wordt een naam van een getal gebruikt. In dit geval wordt de naam SUNDAY gebruikt, dat staat voor het getal 1: de eerste dag van de week. In gewone taal wordt dus de volgende vraag gesteld: "wat is het dagnummer van de week die geselecteerd is, is dat het dagnummer 1 dat ik beschrijf door de naam Calendar.SUNDAY te gebruiken?". Wanneer die vraag met ja beantwoord wordt moet je de datum die je in cal hebt opgeslagen met een dag ophogen.

  • Doe dit door in het if block de method add op het Calendar object cal aan te roepen en daarbij twee berichten mee te geven.

Hiermee geef je aan dat je dag van de maand (DAY_OF_MONTH) wil ophogen en dat je dat 1 keer wilt doen. Hetzelfde gaan we doen met wanneer de geselecteerde datum een zaterdag is. Het verschil is dat we twee vragen willen stellen voordat we de dag van de maand met twee dagen willen ophogen. De extra vraag die we willen stellen is: staat er géén vinkje bij de CheckBox mCbSaturdaysAllowed? Om dit doen moeten we de method isChecked gebruiken van de CheckBox. Maar hiermee komen we er niet: want als er juist een vinkje staat dan pas wordt de vraag beantwoord met ja. We willen juist dat wanneer er geen vinkje staat bij de CheckBox de vraag wordt beantwoord met ja. Het antwoord dat je krijgt wanneer je met een if-code block werkt kan je omdraaien met een '!'. Dus door het volgende te schrijven stel je de vraag "staat er géén vinkje bij de CheckBox":Zoals je weet moeten we twee vragen stellen voordat het if block wordt uitgevoerd. Je kan twee vragen stellen door && te gebruiken.

  • Voeg dit if block toe onderin je method planMe (maar nog wel binnen het if block waarin je controleert of de ingedrukte View wel de juiste is):

Hiermee vraag je dus: "staat er geen vinkje bij de checkbox én is de geselecteerde dag een zaterdag? Wanneer deze beide vragen met ja worden beantwoord moet de geselecteerde datum met 2 dagen ophogen.

  • Schrijf dit binnen het if block dat je zojuist aanmaakte:

Wanneer Android de bovenstaande code heeft uitgevoerd heeft hij de datum waarop je afspraak ingepland moet worden. Die datum is ingesteld in het Calendar object cal. Die datum staat er in als een verzameling van getallen, deze kan je niet zomaar uitlezen om een mooie datum notatie te krijgen. Hiervoor gebruik je de volgende code;

  • voeg deze toen in je method planMe onder de if-code blocks die je al geschreven hebt:

Bovenstaande regels zorgen ervoor dat de datum komt te staan in de String genaamd date. Een String is een object dat een woord of een zin of een combinatie van letters kan onthouden zodat je deze op het scherm kan laten zien of kan gebruiken om een zin mee te bouwen. Hoe deze twee bovenstaande regels verder werken is te uitgebreid voor deze tutorial en wordt dus niet besproken. Als je wil weten wat de combinatie "dd-MM-YYYY" betekent raad ik je aan te kijken naar de beschrijving van de class SimpleDateFormat (en kijk op die pagina naar de tabel "Symbol - Meaning - Presentation - Example").

  • Je ziet  dat DateFormat en SimpleDateFormat onbekend zijn in je class. Voeg daarom bovenaan je class de volgende imports toe:

  • Nu we een String hebben met de juiste datum in tekst-formaat hoeven we deze alleen nog maar te tonen. Voeg deze code toe aan het einde van de method planMe:

(en vergeet uiteraard niet de class android.widget.Toast te importeren bovenaan de class MainActivity) De class moet er nu als volgt uit zien:

  • Je app is nu af. Je kan hem starten. Als je niet meer weet hoe je hem moet starten op de emulator, kijk dan naar de het laatste gedeelte van de vorige tutorial. Als het goed is ziet je app er bijvoorbeeld als volgt uit als je hem uitvoert: .

De app ontwerpen: RelativeLayout

Je hebt net een mooie simpele app gemaakt. Je mag jezelf daar best een complimentje voor geven. Er doet zich echter een klein probleempje voor: wanneer je je telefoon op zijn kant draait, dan klopt de indeling (layout) van het scherm niet meer. Wanneer je de app hebt opgestart in je emulator kan je het scherm van je telefoon draaien door op Ctrl+F11 te drukken (krijg je een zwart scherm, druk dan enkele keren op Ctrl+F11 of Ctrl+F12). Je ziet nu dat de minnetjes van de DatePicker niet meer worden getoond: . Je loopt nu tegen een probleem aan van de LinearLayout: Views kan je alleen maar in één lijn onder elkaar óf naast elkaar tonen. Een layout die dit probleem niet heeft is de RelativeLayout. Wanneer je een RelativeLayout gebruikt, krijgen de Views die in de RelativeLayout zitten extra properties. Je kan dan per View instellen of je hem links, rechts, onder of boven wilt uitlijnen, of dat je hem links, rechts, onder of boven een andere View wil plaatsen. Ook kan je dan instellen per View, of je de grootte aan wilt passen aan een andere View. Doordat je deze properties aan kan passen, kan je Views op heel veel manieren neerzetten in je RelativeLayout. Hierdoor kan je je layout veel beter voorbereiden op lastige situaties waarin je nog steeds wilt dat je layout er netjes uitziet. Als je een LinearLayout om wilt zetten naar een RelativeLayout, hoef je niet alle Views opnieuw in je layout te slepen in de Graphical Layout editor tab. Wat je veel beter kan doen, is met de Android XML Creator een nieuwe layout aanmaken en als Root element de Relative Layout kiezen. Daarna kan je in de Sourcecode editor tab (dus de meest rechtse editor tab) van het LinearLayout-bestand alle Views die in de LinearLayout zitten kopiëren in je RelativeLayout. Wanneer je twee layouts in één Activity wilt gebruiken, kan je hiervoor resource qualifiers gebruiken. Resource qualifiers zijn woordjes die je toe moet voegen aan de naam van de map layout, zodat je een extra map krijgt waarin alternatieve bestanden zitten voor je layouts. Wanneer je bijvoorbeeld de resource qualifier land gebruikt, maak je een nieuwe map layout-land aan in je res map in het Package Explorer window. Als je app vervolgens in landschap-modus is, kijkt hij bij het laden van layouts eerst in de map layout-land en als hij daar niet het benodigde bestand kan vinden kijkt hij pas in de map layout. Op dezelfde manier heb je onder andere ook qualifiers voor portret-modus, de taal-instellingen waarmee de app wordt opgestart of de schermgrootte van het toestel. Aan het einde van deze tutorial moet het je duidelijk zijn hoe je de extra propertjes in de RelativeLayout kan gebruiken om je Views dynamischer neer te zetten in je layouts. Dit ga je ontdekken door een layout aan te maken voor wanneer het scherm gekanteld is en daarvoor de RelativeLayout te gebruiken:

  • Eerst moet de naam van de layout aangepast worden. Klik met linkermuisknop op linearlayout.xml en druk op Alt+Shift+R (voor Mac OS: Alt+Command+R) en geef als nieuw naam op dynamiclayout.xml . We noemen hem dynamiclayout.xml, omdat we straks twee layout-bestanden hebben: één layout-bestand voor wanneer het scherm in portret-modus is en één layout-bestand voor wanneer het scherm in landschap-modus is.
  • Er is nu een fout in je class MainActivity: je verwijst in de method onCreate(), wanneer je setContentView() gebruikt, naar de layout linearlayout, maar die naam heb je zojuist aangepast. Pas de verwijzing aan, zodat er  dynamiclayout staat en sla het bestand op:

  • Maak een nieuwe layout aan in de map res/layout-land (klik rechtermuisknop op de map res, vervolgens New-->Android XML File). Als je niet weet hoe dat moet, kijk dan even naar het kopje "LinearLayout: Aanmaken en instellen". Geef als bestandsnaamdynamiclayout.xml op en zorg ervoor dat als root element van je layout een RelativeLayout is geselecteerd. Om ervoor te zorgen dat dit layout-bestand gebruikt wordt wanneer je scherm gekanteld is, moet je een qualifier toevoegen. Een qualifier kan je het beste begrijpen als een extra regel die bepaalt wanneer je layout geladen wordt. In het scherm zie je verschillende qualifiers staan: Country Code, Network Code, Region, etc. Zoek in het lijstje naar de Orientation qualifier en druk op de knop met het pijltje naar rechts, zodat de Orientation qualifier in het rechterlijstje komt te staan. Selecteer vervolgens Landscape bij de Screen Orientation van de Landscape Qualifier. Je scherm moet er nu als volgt uit zien: . Klik op Finish.

Er is er een nieuw layout-bestand aangemaakt in de map layout-land. Wanneer Android een layout moet laden en het scherm is in landschap-modus, dan kijkt hij eerst in de map layout-land. Vindt hij daar geen layout met dezelfde naam, dan gebruikt hij de layout die hij vindt in de map layout.

Zoals je kon zien op het vorige scherm, kan je op dezelfde manier ook andere qualifiers gebruiken of ze zelfs combineren. Je kan bijvoorbeeld een layout aanmaken en de qualifiers Orientation en Language toevoegen en deze respectievelijk op portret en nl zetten. Op deze manier wordt de layout alleen geladen als: (1) je in portret-modus bent én (2) het wordt gebruikt op een toestel die als landinstelling Nederland heeft geselecteerd.
  • Ga naar het layout-bestand dat je zojuist hebt gemaakt. Zoals je ziet is hij nog leeg. Je zou nu alle widgets opnieuw naar het scherm kunnen slepen, maar je kan ook de sourcecode van de andere layout kopiëren.
  • De sourcecode van de layout kan je zien als je je layout dynamiclayout.xml opent in de Sourcecode editor tab: .
  • Selecteer nu alle sourcecode die in de LinearLayout staat (dus niet de LinearLayout zelf) en zorg dat je regels vanaf het begin tot het einde hebt geselecteerd: .
  • Klik vervolgens met de rechtermuisknop op de geselecteerde tekst en klik op Copy .
  • Ga nu weer terug naar de nieuwe layout in de map layout-land en open hem. De Sourcecode editor tab paste de tekst die je net kopieerde tussen de <RelativeLayout> tags (Voeg een witregel toe boven de regel met </RelativeLayout> als die er nog niet stond: . Klik vervolgens met de rechtermuisknop op de regel die je zojuist hebt gemaakt en klik op Paste: . Sla je layout op. Je layout moet er nu als volgt uit zien:

  • Schakel weer terug naar de design-weergave door op Graphical Layout te drukken. De widgets die je gebruikt staan nu niet meer netjes onder elkaar . Dit komt doordat je een RelativeLayout gebruikt: de Views worden niet meer automatisch onder of achter elkaar neergezet, je moet nu expliciet aangeven waar je je Views wilt hebben, of anders blijven ze linksbovenin hangen.

Vergelijk nu eens de properties van de DatePicker in je oude LinearLayout () met de properties van de DatePicker in je nieuwe RelativeLayout(). Als je bij beide in het Properties window bij Misc kijkt, dan zie je bij de RelativeLayout dat je bij de DatePicker meer properties kan instellen bij Misc: Je kan daar bijvoorbeeld Layout to left of of Layout align parent right instellen. Wanneer je een View (bijvoorbeeld een DatePicker) gebruikt in een RelativeLayout, kan je meer properties instellen in de View, zodat je hem preciezer kan positioneren. Wanneer je een View in een RelativeLayout zet krijgt de View onder andere de volgende eigenschappen (properties):

ElementBeschrijving
Layout abovePositioneert de View boven de View die je hier invult.
Layout align baselinePositioneert de View op dezelfde hoogte als de View die je hier invult, gebaseerd op de baseline van beide Views. De baseline is een denkbeeldige horizontale lijn die over een View loopt en gebruikt wordt om Views voor het oog netjes op "dezelfde" hoogte te positioneren.
Layout align leftPositioneert de linkerrand van de View aan de dezelfde horizontale afstand van de linkerrand van de View die je hier invult.
Layout align parent rightPositioneert de rechterrand van de View aan de rechterrand van de RelativeLayout.
Layout to left ofPositioneert de rechterrand van de View aan de linkerrand van de View die je hier invult.
  • We gaan nu een paar van deze eigenschappen gebruiken om de layout geschikt te maken voor een horizontale weergave. Pas de volgende properties aan:
ElementPropertyWaarde
DatePickerLayout below@+id/btnPlanMe
DatePickerLayout align right@+id/btnPlanMe
DatePickerLayout margin left 
DatePickerLayout margin top 
CheckBoxLayout align left@+id/btnPlanMe
CheckBoxLayout below@+id/btnPlanMe
CheckBoxLayout margin left 
  • Sla je layout op. Hij moet er zo uitzien: .
  • Je layout voor de horizontale weergave is bijna af. Er is echter nog een klein probleempje: bij een klein scherm is de tekst "Zaterdagen toegestaan" te breed en verdwijnt achter de DatePicker: . Om dit op te lossen gaan we instellen dat de rechterrand van de CheckBox niet achter de DatePicker mag verdwijnen. Dit kan je instellen door Layout to left of te gebruiken. Van de CheckBox zet je de property Layout to left of op @+id/dpDate .

Als gevolg hiervan wordt bij een klein scherm de rechterrand van de CheckBox op dezelfde horizontale afstand gezet als de linkerrand van de DatePicker: de CheckBox wordt ingekort en daardoor worden de woorden "Zaterdagen" en "toegestaan" onder elkaar geplaatst: .

  • Als je nu de app opstart en je kantelt het scherm, dan worden alle Widgets wél correct weergegeven: En als je weer terug kantelt (Ctrl+F11 of Ctrl+F12), wordt het scherm op zijn normale manier weergegeven: 
Toen je de properties van de DatePicker en de CheckBox aanpaste (Layout below, Layout align right etcetera) en daar steeds @+id/btnPlanMe invulde, heb je ingesteld dat de Widgets relatief van de Button moesten worden weergegeven. De DatePicker moet rechts uitgelijnd worden ten opzichte van de Button en moet onder de Button gepositioneerd worden. Op dezelfde manier moet de CheckBox links uitgelijnd worden ten opzichte van de Button en ook onder de Button gepositioneerd worden. Door van de CheckBox de property Layout to left of op @+id/dpDate te zetten, heb je ervoor gezorgd dat de CheckBox niet achter de DatePicker verdwijnt en als gevolg daarvan de tekst altijd zichtbaar is.

Dit is het einde van deze DevTutorial. Hopelijk vond je het leuk om deze te lezen en ik zie je graag terug op het forum of in de volgende DevTutorial! Volgende week leg ik je uit wat een ScrollView is, wat parameters en variables zijn en wat de logcat is. Ook leer je daar hoe je een for block en een try-catch block maakt en hoe je door je eigen code kan lopen met breakpoints.

Spelfouten, taalfouten of inhoudelijke fouten ontdekt? Stuur dan een mailtje naar de auteur van dit artikel!

Reacties (34)
Bezig met laden van reacties...