Wat gaan we doen
In deze tutorial gaan we een simpele reken-app maken die de tafels uit kan rekenen van het getal dat je invoert. De app bevat een tekstveld waar je een getal in moet voeren en een knop waarmee je opdracht kan geven de tafel van het ingevoerde getal te berekenen. Wanneer de knop ingedrukt wordt, wordt de tafel van het getal berekent tot aan het getal 60. Terwijl je deze app maakt (en de werking ervan gaat inspecteren) ga je de volgende nieuwe concepten leren:
Dit is een DevTutorial waar je vaak gevraagd wordt het zelf ook na te doen, dus hij is best actief (en er wordt je, zoals je gewend bent, ook weer het nodige uitgelegd). Hier is de naar de forum thread die bij deze DevTutorial hoort. Succes ermee!
De app ontwerpen: ScrollView
Door de mogelijkheden van de LinearLayout en de RelativeLayout te gebruiken is het je tot nu toe gelukt om al je Views in het scherm te tonen. Maar wat nou als je met geen mogelijkheid alle Views kunt tonen in hetzelfde scherm, terwijl het toch noodzakelijk is dat je de Views tegelijkertijd toont? In dit geval kan de ScrollView je helpen: de ScrollView is een layout die wanneer hij te groot wordt automatisch scrollbars krijgt en ervoor zorgt dat je de View die in de ScrollView zit kan scrollen. Zoals je misschien al verwacht, is een ScrollView een ViewGroup net zoals de RelativeLayout of de LinearLayout (als je het verschil niet meer weet tussen een gewone View en een ViewGroup, kijk dan even naar de vorige tutorial). Een beperking van de ScrollView is dat hij maar één View kan bevatten. Je kan deze beperking omzeilen door nog een andere ViewGroup te plaatsen in de ScrollView, en dan kan je vervolgens zoveel Views kwijt in die laatste ViewGroup als je maar wil. Een voorbeeld is dan dat een ScrollView een RelativeLayout bevat en dat de RelativeLayout dan meerdere gewone Views kan bevatten. Wanneer de RelativeLayout hoger wordt dan de ScrollView waar hij zit, dan krijgt de ScrollView automatisch scrollbars en kan de gebruiker de RelativeLayout scrollen. Voor het berekenen moet je een aparte method in je Activity maken die wordt uitgevoerd wanneer je op de knop (Button) drukt. De berekeningen die worden uitgevoerd worden weergegeven in een tekstveld waar je alleen tekst in kan tonen. In Android heet een tekstveld waarin je alleen tekst kan tonen een TextView, in de Graphical Layout Editor Tab kan je meerdere soorten TextViews gebruiken, waarvan de simpelste de naam Plain Text heeft (alle TextViews staan in de Graphical Layout editor tab links in het lijstje onder het mapje Text Fields). Doe nu het volgende:
Element | Property | Waarde |
---|---|---|
Button | Id | @+id/btnCalculate |
Button | Text | Bereken! |
Button | On click | calculateTables |
RelativeLayout | Padding | 10dip |
EditText | Id | @+id/etxtNumber |
EditText | Layout to right of | @+id/btnCalculate |
EditText | Text | |
EditText | Layout width | fill_parent |
EditText | Input type | numberDecimal |
EditText | Hint | Voer een getal in |
TextView | Id | @+id/txtvResult |
TextView | Layout below | @+id/btnCalculate |
TextView | Layout margin top | 10dip |
TextView | Text | \n |
TextView | Text size | 14dip |
TextView | Text color | #FFFFFF |
[gist id=1076150] Toen je net de method calculateTables gebruikte, bereidde je de method er op voor om een berichtje te ontvangen bij het uitvoeren: de View die zojuist was ingedrukt. Zo’n berichtje wordt in Android een parameter genoemd: dit wordt in de volgende paragraaf uitgelegd.
App tussendoortje
Als tussendoortje wordt je uitgelegd wat method parameters en variables zijn. Je maakt hierbij meteen een kleine app zodat je goed door hebt dat je door middel van method parameters hetzelfde stukje geschreven code op verschillende manieren uit kan voeren. De hoofdrolspelers hierin zijn variables, die worden daarna als belangrijke achtergrondinformatie uitgelegd.
Method parameters
Parameters zijn berichten die je mee kan geven aan een method zodat je kan aansturen hoe de method wordt uitgevoerd. Zoals je waarschijnlijk nog weet is een method een verzameling van instructies (regels code) die samen een naam zijn gegeven en in een method block zijn geplaatst. Een voorbeeld van een simpele method is een method met één regel code: [gist id=1076623]
[gist id=1076706]
De method zorgt ervoor dat er een korte tijd een zwart balkje getoond wordt met daarin de tekst: “Dit is een voorbeeldtekst die getoond wordt.”. Deze method kan je uitvoeren door de naam ervan te typen gevolgd door een ‘(‘ en een ‘)‘ : [gist id=1076628]
Als je de app opstart zie je een korte tijd het balkje en daarin de tekst die je net hebt ingevoerd. (als je de tekst niet ziet, druk in de emulator op de back knop en start de app opnieuw op vanuit het hoofdmenu in de emulator) Het lastige van bovenstaande method is dat het alleen maar handig is om hem te gebruiken als je een balkje wil met precies deze tekst: “Dit is een voorbeeldtekst die getoond wordt.”. Als je een andere tekst zou willen tonen in een zwart balkje dan moet je een compleet nieuwe method schrijven. Dit is niet handig: je schrijft bijna precies dezelfde method, het enige dat je dan anders zou doen bij een andere tekst is een andere tekst schrijven tussen de dubbele aanhalingstekens: [gist id=1076634] Zou het dan niet handiger zijn als je deze twee methods kan combineren en dat je de kleine verandering in het resultaat van de method op een andere manier kan aanpassen? Je zou dan op een of andere manier moeten laten weten dat het enige dat je anders wilt in het resultaat de tekst zelf is en dat je in alle gevallen van de method gewoon een zwart balkje wilt met daarin de aanpasbare tekst. Wanneer je de method aanroept, zou je eigenlijk een bericht mee moeten kunnen geven die aangeeft wat je als tekst wilt tonen in het zwarte balkje. Aan de andere kant zou je in de method zelf ook iets moeten aanpassen zodat je een bericht kan ontvangen en dat je deze vervolgens kan gebruiken in het aanroepen van het zwarte balkje. Berichten die je method kan ontvangen schrijf je tussen de haakjes die achter de naam van je method staan. In ons geval willen we een verzameling van letters/woorden als bericht kunnen ontvangen en zoals je weet heet dat in Android een String. Maar hoe kan je nou die String gebruiken terwijl je in je method bent? Net zoals je een Button een naam gaf en een EditText een naam gaf in je MainActivity in de vorige tutorials, moet je het bericht dat je ontvangt in je method ook een naam geven. Ook bij een method zeg je dat je het berichtje moet definiëren zodat je er later naar toe kan verwijzen. Het definiëren doe je op dezelfde plek waar je aangeeft wat voor berichtje je wil ontvangen: namelijk tussen de haakjes die achter de naam van je method staat. Dus stel dat we een method willen maken die een String als bericht moet kunnen ontvangen en we willen die String de naam message geven, dan doe je dat op deze manier: [gist id=1076730] Elke keer als deze method wordt aangeroepen, zit in de String message het bericht dat getoond moet worden (want de String die tussen de haakjes staat, wordt elke keer bij het aanroepen van deze method opnieuw gedefinieëerd). De String message gebruik je dan meteen in de (enige) eerste regel van method: de tekst die eerst tussen haakjes stond is nu vervangen door de String message.
Wanneer je nu deze method aan wil roepen (vanuit bijvoorbeeld de method onCreate) moet je het extra bericht meegeven. Zoals je misschien kan raden doe je dat tussen de haakjes wanneer je method aanroept: [gist id=1076752] Op deze manier kan je de method makeToast op oneindig veel verschillende manier gebruiken doordat je steeds een andere tekst meegeeft. Dit is veel handiger, omdat je nu niet voor elke nieuwe zin een aparte method aan hoeft te maken.
[gist id=1076769] Als je nu je app opstart, dan krijg je achter elkaar drie verschillende berichten, probeer dat zelf ook uit!
Variables
In de vorige paragraaf heb je een method gemaakt waarbij je de String message bij het aanroepen een andere inhoud van letters/woorden kan geven: hierdoor kan je door middel van de method makeToast verschillende teksten laten zien in een zwart balkje. Je zou kunnen zeggen:
Het Engelse woord hiervoor is variable en wordt om die reden veel gebruikt in Android. Een variable is dus een verwijzing naar een bepaalde waarde die je kan aanpassen en uit kan lezen. Een variable kan je definieren tussen de haakjes die horen bij het begin van een method block of gewoon ‘los’ op een willekeurige plek in een block zoals in een class block of in een method block zelf (beide voorbeelden ben je al meerdere keren tegengekomen). Een variable moet je altijd een naam geven voordat je hem kan gebruiken en je moet van tevoren ook altijd aangeven wat voor type het is. Wanneer je bijvoorbeeld een int hebt die je de naam ‘number’ geeft: [gist id=1076809] dan heb je dus een variable van het type int met de naam number. Je kan een waarde erin stoppen door erachter een = teken te schrijven en daarachter de waarde: [gist id=1077622] Je kan ook meteen de waarde opgeven: [gist id=1077624] Of wanneer je een String hebt die je ‘address’ noemt: [gist id=1076810] dan heb je een variable van het type String met de naam address. Daaronder zie je hoe je een waarde opgeeft voor de String address, en daaronder zie je hoe de variable met de naam city van het type String meteen een waarde Amsterdam krijgt. Op dezelfde manier kan je ook een TextView hebben die je txtvName noemt (of een TextView txtvAddress): [gist id=1076813] dat noem je dan de variable van het type TextView met de naam txtvName, en daaronder zie je hoe de variable met de naam txtvAddress van het type TextView meteen een waarde krijgt, namelijk de TextView met het id txtvAddress die in de geladen layout is. Wanneer je het type beschrijft en daarachter de naam, dan reserveer je technisch gesproken ruimte in het geheugen van het Androidapparaat zodat je daarna een waarde erin kan opslaan. Afhankelijk van wat voor type je gebruikt, worden er meer of minder bits/bytes in het geheugen van je Androidapparaat gereserveerd (bits of bytes: 8 bits vormen samen één byte). Hoeveel dat is en wat het verschil is tussen een simpele waarde opslaan (een int, float, bolean, byte, short, long, double of void) en een Object opslaan (bijv een String, een TextView of een DatePicker, Activity) leer je in DevTutorial 7. Zolang je geen waarde toewijst aan een variable blijven alle gereserveerde bits op het getal 0 staan: bij een int of een float is dat gewoon het getal nul (dus 0), bij een Object noem je dat de speciale waarde null. Pas wanneer je een waarde toewijst met het = teken wordt de variable gevuld (technisch gesproken verander je dan eigenlijk de waarde: want 0 of null is op zichzelf natuurlijk ook een waarde). De rest van de tutorial gaat weer over de andere app: ScrollView Demo. Als je opdracht gegeven wordt iets aan te passen moet je dat nu weer in die app doen.
De app programmeren: for block
In het if block in de method calculateTables moet je de code schrijven die de tafels uitrekent en de uitkomst ervan laat zien in het TextView mTxtvResult. We willen de tafel uitrekenen van het ingevoerde getal tot aan het getal 60. Om dit te maken zou je 60 regels op kunnen schrijven die allemaal één van de 60 berekeningen uitvoeren. Met programmeren kan dit echter een stuk gemakkelijker. Je kan hiervoor het for block gebruiken. Het for block kan je zien als een uitbreiding op het gewone if block: ze beginnen beiden met haakjes waarin je een vraag kan stellen en vervolgens begint het code block. Maar wat anders is aan het for code block, is dat je binnen de haakjes ook kan aangeven hoe vaak het block uitgevoerd moet worden: [gist id=1077845] Met bovenstaande for code block wordt 10 keer de tekst “hallo” op het beeldscherm getoond. Je kan de tekst binnnen de ‘(‘ en de ‘)‘ opdelen in drie stukken:
Wat je dan krijgt als dit code-block wordt uitgevoerd is het volgende:
[gist id=1077878]
[gist id=1077879]
[gist id=1077887] Nu heb je een String waarin het getal staat dat is ingevoerd. Maar Android ziet de String niet als een nummer, maar als tekst, net zoals bijvoorbeeld de letter a of het woord hallo. Daar kan Android nog niet van raden wat voor getal het is. Je moet die String nog omzetten naar een getal, een int: [gist id=1077891]
Je zou misschien denken dat je gewoon int number =(int) numberString; kan schrijven. Dit is echter niet het geval: een String is een heel ander soort variable dan een int: een int is een simpel type dat een getal kan opslaan tussen een bepaalde bandbreedte (tussen -2,147,483,648 en 2,147,483,648), terwijl een String een Object is waarbij de lengte van de woorden aanpasbaar is en je ook allerlei methods aan kan roepen op de String. Wanneer weet je dan wél of variables naar elkaar gecast kunnen worden? Dat heeft te maken met of de classes die je gebruikt superclass en subclass van elkaar zijn: je kan een class alleen casten als de bestemming class een superclass is, of als het object dat je gaat casten niet eigenlijk al een subclass-type is waar je het object naartoe wilt casten. Dit laatste is vaak het geval bij findViewById zoals geschreven in de vorige DevTutorial: je krijgt als resultaat een View terug, maar omdat je zelf de layout ontworpen hebt, weet je dat je te maken hebt met een specifiek subclass type zoals bijvoorbeeld een EditText of een DatePicker. |
Nu kan je gaan rekenen met het getal. In java wordt een heel getal een int genoemd. Als je een getal wilt hebben dat ook getallen achter de komma’s kan opslaan, dan moet je een float of een double gebruiken (en je moet dan Float.parseFloat(numberString); gebruiken). Je method calculateTables moet er nu als volgt uit zien: [gist id=1077897]
[gist id=1077903]
[gist id=1077906]
[gist id=1077926]
Bovenstaande regel zou raar over kunnen komen: je gebruikt zomaar 3 ints (counter, number en result) en voegt ze samen met de karakters x en =. Dit is een handigheidje van Java: wanneer je een int samenneemt met een String (een verzameling van karakters), dan gaat Java er vanuit dat je de int ook wil gebruiken als een String en converteert hij de int automatisch naar een String (maar andersom werkt dit dus niet). |
Als je de code nu zou uitvoeren dan wordt er 60 keer een berekening uitgevoerd. En die berekening wordt opgeslagen in de String calculationResult. Maar elke 60 keren als Android het for block uitvoert, worden alle variabelen (de int result en de String calculationResult) opnieuw aangemaakt. Dit komt omdat alle variables (variables is het Engelse meervoud van variable) die je aanmaakt, gekoppeld zijn aan het block waarin ze zijn gedefinieerd. Dus als Android buiten het block komt waar de variable in is gedefinieerd, dan kan je de variable niet meer opvragen (Android geeft dan een foutmelding dat hij niet weet wat je bedoelt met die variables). In programmeertermen heet dit de scope van een variable: deze is hetzelfde als het block waarin hij gedefinieerd is. Wat je dus moet doen om die uitkomsten van de berekening te onthouden is een String definieren die niet steeds opnieuw wordt aangemaakt, maar “door” blijft bestaan als er een nieuwe berekening uitgevoerd. Dit kan je doen door de String te definieren in het block dat om het for block heen zit: in dit geval is dat het if block. Dit heb je net al gedaan toen je de String allCalculationsResult aanmaakte. Omdat deze in een hoger gelegen block is gedefinieerd blijft allCalculationsResult bestaan en wordt hij niet opnieuw uitgemaakt bij elke nieuwe berekening. In programmeertermen zou je kunnen zeggen: het for block ligt in zijn geheel in de scope van de String allCalculationsResult.
Weet je nu ook waarom we de Views steeds definieasdasren in het hoogst gelegen block van je class, namelijk in het block van de class zelf? Hierdoor is de scope van alle View variables net zo groot als het block van de class zelf: omdat alle method blocks en alle andere blocks die daar weer in zitten altijd in het class block zitten, zijn je Views variables overal in je class toegankelijk. Oh trouwens, ga nou niet meteen altijd alle variables in je class block definieren: je code wordt er rommeliger door (dit is een belangrijk argument) en het vergt onnodig veel geheugen. Probeer een variable altijd in een zo “laag” mogelijke scope te definieren. |
[gist id=1077963] Wat je hier zegt, is dat je de inhoud van allCalculationsResult wil veranderen. En als inhoud geef je mee de oude versie van allCalculationsResult en daar achteraan plaats je de berekening die je net hebt uitgevoerd. Die \n in het midden staat voor een nieuwe regel: dus op deze manier worden alle berekeningen netjes onder elkaar gezet, gescheiden door een enter.
[gist id=1077964] Je method calculateTables moet er nu als volgt uit zien: [gist id=1077971] In Eclipse is dat als volgt. Let er even op dat de regelnummers precies gelijk zijn (dus maak of verwijder de benodige enters in je class), dit heb je straks nodig. Nu kan je de app opstarten. En wanneer je een getal invoert en op de knop drukt worden de tafels voor je uitgerekend. Zoals je merkt wordt het scherm automatisch scrollbaar gemaakt omdat je een ScrollView hebt gebruikt.
De app debuggen
Wanneer je fouten gaat oplossen die in je app aanwezig zijn, heet dat debuggen. Zometeen gaan we de app expres laten crashen, maar om je daar even mentaal op voor te bereiden, vertel ik je eerst wat over een speciaal scherm die je na het crashen kan gebruiken voor bij het debuggen. Dit scherm heet het Debug Perspective. Je kan hem openen door rechts bovenin het scherm op Debug te klikken of op het plusje links van Java te klikken en Debug te kiezen als je Debug er nog niet hebt staan (het plusje is aangegeven met een rood vierkantje). Wanneer je het Debug perspective opent, toont Eclipse de windows die je nodig hebt als je gaat debuggen (fouten opsporen). Wanneer je in het Debug perspective werkt, zie je in het midden het bekende Editor Window, rechts daarvan de Outline Window. Bovenin het scherm zie je drie nieuwe windows, opgedeeld in de linkerhelft en de rechterhelft. Links zie je het Debug Window: hier zie je tijdens het debuggen een overzicht van de actieve sub-processen (threads) die horen bij je app. Rechts zie je in tabjes ingedeeld de Variables Window en de Breakpoints Window. In deze tutorial wordt van deze windows alleen de Breakpoints Window uitgelegd. Onderin het scherm zie je het Console window, het Tasks Window en het LogCat Window. Alleen het LogCat Window wordt uitgelegd in deze tutorial: in het LogCat Window staat het technisch verslag van wat er in de Emulator gebeurt, opgeschreven in aparte regels tekst. Wanneer je het LogCat Window gebruikt kan je hem het beste groter maken door op het tabje van het LogCat Window te klikken en hem na gebruik weer kleiner te maken door er dan weer op te dubbelklikken. Wanneer je klaar bent met debuggen en je wilt weer verder coderen, dan kan je terug schakelen naar het Java Perspective door rechts bovenin het scherm op Java te klikken.
LogCat
Je zou kunnen zeggen dat je app prima werkt. Totdat je op een gegeven moment vergeet een getal in te voeren en op de knop Bereken! drukt. Dan krijg je dit scherm. Het enige dat je nu weet is dat er “ergens iets is fout gegaan” (op zich ook wel logisch, als er een fout optreedt, ga je de doorsnee Androidgebruiker niet lastig vallen met ingewikkelde technische foutmeldingen waar hij toch niks van snapt). Als je wil weten wat er precies fout ging dan moet je kijken naar het LogCat Window: hierin staat een technisch verslag van wat er in de Emulator (of het aangesloten Androidapparaat) gebeurt. Als je daarna naar het Debug Perspective gaat en het LogCat Window groter maakt door op het tabje ervan te dubbelklikken, dan zie je de foutmelding. We gaan nu de app laten crashen en vervolgens met LogCat uitlezen wat er fout ging:
Wanneer je in de LogCat kijkt moet je altijd eerst kijken naar de fout die als eerste gegenereerd wordt, omdat de fouten die daarna komen vaak het gevolg zijn van de eerdere fout. De fout die als eerste gegenereerd is, staat bovenaan. Je ziet daar staan: Uncaught handler: thread main exiting due to uncaught exception. Dit bekent vrij vertaald in het Nederlands “er is een fout opgetreden en die is niet netjes verwerkt (afgevangen) door de app zelf, dus daarom wordt de app afgesloten”. Deze foutmelding zie je in de LogCat verschijnen als je op je Androidemulator (of Androidtoestel) de popup krijgt met daarin: “Sorry! De toepassing X is onverwacht gestopt. Probeer het opnieuw” of in het Engels: “Sorry! The application x has stopped unexpectedly. Please try again”. In de LogCat zie je daaronder een lange beschrijving met wat er precies is fout gegaan. Het begint met java.lang.IllegalStateException: Could not execute method of the activity”. Vrij vertaald betekent dit: “de method kon niet worden uitgevoerd”. Hier heb je nog niet zoveel aan. Daaronder staat een lange opsomming van hoe die fout tot stand is gekomen. Als je nog iets verder omlaag scrollt dan zie je op een gegeven moment staan Caused by: java.lang.reflect.InvocationTargetException. “Caused by” betekent zoiets als “veroorzaakt door”. Dus de fout die helemaal bovenaan staat, is veroorzaakt door deze fout. Als je naar de regel daaronder kijkt dan zie je waar die fout is opgetreden: at nl.androidworld.scrollviewdemo.MainActivity.calculateTables(MainActivity:31). Dit betekent dat toen de hele ketting van deze foutmelding is geproduceerd, Android onder andere bezig was op regel 34 van MainActivity.java. Daaronder zie je nog een extra uitleg van de foutmelding: Caused by: java.lang.numberFormatException: unable to parse ” as integer. Deze fout gaan we verder onderzoeken door stap voor stap door onze code te lopen als hij wordt uitgevoerd. Dat kan je doen door middel van breakpoints.
Breakpoints: stap voor stap door je code heen lopen
Wanneer je met LogCat hebt uitgevonden waar de fout ongeveer zit, kan je breakpoints gebruiken om de uitvoer van je code rond de foute code te pauzeren en te kijken wat op dat moment de interne status van je app is. Wanneer je breakpoints wilt gebruiken moet je je app opstarten in Debug modus en moet je in je bestand AndroidManifest.xml aangeven dat je je app wilt kunnen opstarten in Debug modus. Wanneer je dat gedaan hebt, kan je breakpoints instellen in je code. In Eclipse kan je dit doen door een regel uit te zoeken waar je een breakpoint wilt plaatsen en dan op de rand van het Editor Window links ervan te klikken (op de plek waar de regel nummers staan). Dan verschijnt er een blauw bolletje in de linkerrand. Wanneer je je app vervolgens opstart in Debug modus en je app komt bij een regel waar een breakpoint staat dan wordt je app gepauzeerd en kan je kijken wat de waarde is van verschillende variables die in de scope zijn van de regel waar de breakpoint staat. Nadat je de variables hebt bekeken of andere Debug-dingen hebt gedaan kan je verder gaan met je code door weer op de play knop in het Debug Window te klikken. Andere optie is om regel-voor-regel door je code te stappen door de knoppen met de gele pijltjes te gebruiken uit je Debug Window. We gaan nu de fout uit de vorige paragraaf verder onderzoeken en vervolgens oplossen:
Kijk naar het schermpje van MainActivity.java, deze staat links in het midden van het scherm. Ga nu naar regel 31 van MainActivity.java. Zoals je misschien nog weet is dit de regel waarop het de tekst die je hebt ingevuld in de EditText wordt omgezet naar een getal. Maar waarom krijg je dan een foutmelding? Om dit te onderzoeken, dubbelklik met je muis links van het regelnummer 34 (dus helemaal aan de linkerkant van het Eclipse scherm) zodat er een bolletje verschijnt. Wanneer je de app uitvoert en Android staat op het punt deze regel uit te voeren dan wordt de app gepauzeerd.
Ik wil je graag nog iets anders laten zien: voer opnieuw een getal in (bijvoorbeeld 84) en druk op Bereken!. Wanneer je weer in Eclipse bent als de uitvoering van de app is gepauzeerd, klik dan niet op de de Debug-Play knop, maar kijk op hetzelfde balkje als waar de Debug-Play knop op zit: daar zie je ook 3 knoppen met gele pijltjes naast elkaar staan, een pijltje dat met een hoek naar beneden wijst tussen twee streepjes, een pijltje dat met een boog naar beneden wijst over een streepje heen en een pijltje dat met een haakse hoek naar rechts wijst uit twee streepjes. Klik een paar keer op het middelste pijltje (die met een boog naar beneden wijst): hiermee geef je Android opdracht alleen de regel uit te voeren die op dit moment geselecteerd is. Als je dit bijvoorbeeld 10 keer doet dan merk je hoe Android door het for block heen gaat. Als je dan je muis stil laat staan boven counter dan zie je dat die steeds wordt opgehoogd bij elke iteratie. Je kan ook nu goed zien hoe de String allCalculationsResult stapje voor stapje wordt opgebouwd. Je hoeft trouwens niet persé je muis stil te hangen boven de variables: je kan ook rechts bovenin Eclipse in het Variables Window kijken. Als daar een variable geel wordt gekleurd betekent dat dat de waarde van de variable ten opzichte van de vorige stap veranderd is. |
try block & catch block(s)
Wanneer een uitzondering (exception) optreedt in je code dan moet je deze opvangen en verwerken. Als je dat niet doet, dan ziet Android dat als een fout (error): de code gedraagt zich dan immers op een manier waar hij niet door de programmeur op voorbereid is. De gebruiker krijgt dan zo’n “Sorry!” popup-scherm en de app wordt afgesloten. Wanneer je zo’n popup-scherm krijgt en je app wordt vervolgens afgesloten noem je dat in Android een forced close (een geforceerde afsluiting). Nu je weet wat er gebeurt kan je deze bug gaan fixen. Het opvangen van een uitzondering (in Android heet een fout een Exception) doe je in Android met twee blocks die je onder elkaar zet: [gist id=1086241] In het try block schrijf je code die wilt proberen uit te voeren (vandaar het woord try). Wanneer Android een try block tegenkomt, begint Android gewoon met het uitvoeren van de code die in het try block staat. Maar wanneer er een uitzondering optreedt binnen een try block, dan springt Android door naar het catch block dat daaronder staat (de rest van je try block wordt dan niet meer uitgevoerd). catch is het Engelse woord voor vangen: in dit geval heeft dat betrekking op dat een catch block een uitzondering kan opvangen zodat er geen forced close komt. Wanneer je een catch block schrijft dan schrijf je in de haakjes achter het woordje catch wat voor soort uitzondering dit catch block op kan vangen. Bij het optreden van een Exception in het try block wordt gekeken welk type Exception het is, en vervolgens wordt per catch block gekeken of hij geschikt is om dat type Exception op te vangen. Het eerste catch block dat geschikt is om de Exception op te vangen wordt uitgevoerd en de uitzondering wordt in de variable e gestopt die gedefinieerd is tussen de haakjes. De rest van de catch blocks worden overgeslagen. Je kan dus ook meerdere catch blocks onder elkaar zetten: [gist id=1086253] In bovenstaande code zie je drie catch blocks staan: eentje die een Exception opvangt van het type NumberFormatException, eentje die een Exception opvangt van het type SocketException en een catch block dat een Exception opvangt van het type IOException. Stel dat in het bovenstaande try block een uitzondering zou optreden, dan wordt eerst gekeken of het een Exception is van het type NumberFormatException. Is dat het geval, dan wordt het eerste catch block uitgevoerd en wordt de fout in de variable e gestopt die van het type NumberFormatException is. Is de fout niet van het type NumberFormatException dan wordt gekeken of het van type SocketException is of daarna of de fout van het type IOException is. Als voor alle catch blocks geldt dat ze de fout niet kunnen opvangen (het type fout is van een nog ander type) dan wordt alsnog een forced close gegeven: de fout werd dan immers niet opgevangen. We gaan nu de try – en catch blocks aanmaken:
Eclipse analyseert nu je code vanaf regel 29 t/m regel 40 en kijkt wat voor Exceptions daar kunnen op treden. Vervolgens genereert hij automatisch catch blocks voor elk type fout dat in de geselecteerde code kan voorkomen. Je method calculateTables ziet er nu als volgt uit: [gist id=1086268] Alle code die je net hebt geselecteerd staat in het try block. Er is ook meteen een catch block aangemaakt. Tussen de ronde haakjes staat: NumberFormatException e. Dit betekent dat dit block alleen wordt uitgevoerd wanneer er een NumberFormatException is opgetreden. In gewoon Nederlands betekent dit: dit code block wordt alleen uitgevoerd wanneer er een fout is opgetreden die te maken heeft met een String die niet omgezet kan worden naar een getal. Deze NumberFormatException wordt de naam e gegeven. Hierdoor kan je binnen het catch block nog de foutmelding (die e wordt genoemd) bekijken en hier eventueel iets nuttigs mee doen. In dit geval word de method printStrackTrace aangeroepen: dit zorgt ervoor dat de foutmelding wordt doorgegeven aan Android en terecht komt in de foutmeldingen log.
[gist id=1086272]
En vergeet uiteraard niet de benodigde import te doen. Tip: dit kan je heel gemakkelijk doen door stil te blijven staan met je muis op Toast en vervolgens de optie import Toast aan te klikken. |
Extra oefening: shapes met een gradient
Op verzoek heeft deze DevTutorial een oefening voor mensen die nog verder uitgedaagd willen worden tijdens het programmeren voor Android. Bij deze oefening staan alleen hints over hoe je deze op kan lossen en er wordt geen theorie-uitleg gegeven. Kom je er niet uit dan kan je het internet gebruiken of kijken of het forum. Deze oefening gaat over de layout-hiërarchie uit de vorige DevTutorial en over het maken van een shape xml-bestand.
De titelbalk die we nu gaan gebruiken is eigenlijk niet mooi. Ook is het kleurgebruik van de app een beetje saai. We gaan hem opfleuren.
[gist id=1229788]
- Maak opnieuw eenzelfde xml-bestand met de naam blue_intense_gradient.xml aan en vul hem met deze code:
[gist id=1229798]
Als je nu de app opstart, krijg je dit: . Beetje jammer van die grijze balk bovenin he? Dat gaan we nu fixen:
- Kijk naar je Package Explorer Window, open het bestand Androidmanifest.xml en ga naar de Application Tab. Zoek in dat scherm naar het veld Theme en zet die op @android:style/Theme.Light.NoTitleBar .
Als je nu je app opstart, is de balk weg en heb je een aardig mooie app. In de twee shape xml-bestanden die je net hebt gemaakt kan je ook andere kleuren invullen om ermee te experimenteren (gebruik hiervoor bijvoorbeeld deze pagina). Wil je nog meer oefenen dan kan je proberen ervoor te zorgen dat de EditText en de Button niet mee scrollen als je de uitkomst van de berekening naar beneden scrollt.
Source Code
Als je de sourcecode van deze app wilt bekijken kan je dat hier doen op github. Als je MainActivity.java wil bekijken, klik dan op scr –> me –> moop –> scrollviewdemo. Wil je de layoutbestanden bekijken dan moet je klikken op res –> layout en als je de xml shapes wil bekijken moet je klikken op res –> drawable.
Reacties
Inloggen of registreren
om een reactie achter te laten
Hallo, Ik kan de stukken code in de tekst niet lezen ik krij telkens onderstaande foutmelding.
Wat kan ik hieraan doen?
Could not embed GitHub Gist 1086268: API Rate Limit Exceeded for 87.255.36.156
ik vind de uitleg zeer goed op de website. Heeft er iemand ook een probleem met cijfers?
Telkens ik cijfers gebruik blokkeert heel mijn programma. Weet iemand hier een oplossing voor?
heyo,
ik snap er niet veel van en volgens mij kloppen de links van devtut 1 niet eens want de versies die u gebruikt zijn verouderd. (ik wil gewoon een leuk spelletje voor android maken)
hoop dat u reageerd
Weer een geslaagde tutorial & extra oefening, op naar de volgende!
Echt top! Kom er steeds meer in.
Vond (volgens mij) een foutje in de tekst in de volgende regel:
“Tip: voor je TextView moet je volgende regel gebruiken: mTxtvResult = (TextView) findViewById(R.id.etxtResult);”
“R.id.etxtResult” moet volgens mij “R.id.txtvResult” zijn.
Onderweg naar devtutorial 10!
Dag Wouter,
ik ben hard bezig al je leerzame en goede tutorials te volgen. Ben blij dat ik op deze manier een keer in aanraking kan komen met de waanzinnig grote en belangrijke wereld achter digitale technologie!
Ondervind nu echter mijn eerste probleem: wat wordt bedoeld met “textview leegmaken”, wat wordt hiermee bedoeld? de verwijzing mtxtvresult leegmaken? Toen ik dat deed kreeg ik een foutmelding die betrekking had op regel 40, terwijl mijn regelnummers identiek zijn aan die in het voorbeeld.
Zet deze “vraag”in de reacties zodat het eventueel ook andere helpt.
nog een puntje trouwens: de vertalingen van engels naar nederlands zijn wat ten overvloeden: Ik denk dat een ieder die deze cursus volgt op de hoogte is van de betekenis van “caused by” en kan plaatsen dat het meervoud van variable, variables is;)
Een mooi vervolg op de voorgaande tuts!
Het wordt duidelijk uitgelegd met mooie plaatjes.
Helaas zitten er wel twee mini foutjes in:
TextView Layout below @+id/btnCalulate
En deze regel mist in het method overzicht nadat hij genoemd is.
mTxtvResult.setText(allCalculationsResult);
Verder mooi uitgewerkt!
Kan niet wachten tot nummer 5
Weer een leerzame tuto!!
Is het niet handiger om, bij de volgende tutorials, layouts via XML te ontwerpen. In plaats van Views naar Outline Window te slepen? Ik vind namelijk beter en sneller werken.
Ziet er inderdaad gelikt uit!
“[..] je kan een class alleen casten als de bestemming class een superclass is, of als het object dat je gaat casten niet eigenlijk al een subclass-type is waar je het object naartoe wilt casten.”
Niet helemaal waar, bijvoorbeeld een double casten naar een int is geen probleem (uiteraard met de voetnoot dat je alles achter de komma kwijtraakt). Double en int hebben wel dezelfde superclass (Number), maar geen van beide is een superclass van de andere.
Hier zullen we vanavond maar eens naar gaan kijken! vorige drie zijn tot nu toe gelukt, en heb al lekker wat lopen spelen met eclipse!
Hij was weer fijn. Vooral de extra oefening is leerzaam, omdat je dan zelf veel moet uitzoeken. Volgende keer graag weer!
Wouter erg bedankt voor het delen.. Loop flink achter maar dat gaat goed komen
Hier ga ik morgen maar eens aan beginnen. De vorige devtuts waren erg interessant iig 😉
Oftopic Wouter ik heb een probleem met mijn rom als ik wil video filmen laat hij een wit scherm zien
toch bedankt voor je tuts! ik begin er in de vakantie wel aan!
Vanavond maar eens even DevTut 3 doen en daarna door met deze. Vind het nog steeds leuk en leerzaam 🙂
ik heb er de kracht niet meer voor
n plaats van een LinearLayout willen we een RelativeLayout gebruiken, dus de Linearlayout moet weg: klik in de Outline Window met de rechtermuisknop op de de LinearLayout en klik op delete zodat je layout er als volgt uit ziet:
Dat doet hij niet bij mij, maakt het iets uit als je rechtermuisknop op linearlayout doet en dan change layout?