5 Currency Converter

Leerdoelen

Na het maken van deze app wordt van je verwacht dat je:

  • een Spinner kunt maken
  • een ListView kunt maken
  • met behulp van ArrayAdapters de items van een Spinner en een ListView kunt vullen
  • een listener aan de items van een Spinner kunt koppelen met behulp van de methode setOnItemSelectedListener
  • weet hoe de structuur van een json object er uitziet
  • data van een eenvoudige json api kunt binnenhalen
  • een achtergrond thread start met de AsyncTask klasse
  • weet hoe je een app meertalig maakt met behulp van de strings.xml bestanden

Opdracht

Bekijk onderstaand filmpje en maak de app.

Gebruikte methodes/klassen

Een Spinner

Met een spinner kan gebruiker een keuze maken uit een aantal dropdown items. De items van een spinner worden aangemaakt met een ArrayAdapter. Hiervoor heb je een string-array of een ArrayList met strings nodig waar de item strings in zitten. In onderstaand codefragment wordt een ArrayAdapter gemaakt met behulp van de volgende paramaters:

  1. de context (this)
  2. een standaard layout uit de Android resources
  3. een ArrayList genaamd currencies met daarin de Strings van de valuta
Spinner spinner = (Spinner) findViewById(R.id.spinner);
ArrayAdapter<String> adapter = new ArrayAdapter<>(
        this,
        android.R.layout.simple_spinner_item,
        currencies);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);

Een listener aan een spinner koppelen

De bedoeling van een spinner is dat de gebruiker een item kan selecteren. Hiervoor moet je de methode setOnItemSelectedListener op het spinnerobject aanroepen:

spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    @Override
    public void onItemSelected(AdapterView parent, View view, int position, long id) {
        //TODO zorg dat juiste currency wordt gekozen mbv position param
    }

    @Override
    public void onNothingSelected(AdapterView parent) {

    }
});

In de methode onItemSelected kun je de paramater position gebruiken om te bepalen op welk item is geklikt.

Een ListView

Een ListView is een ViewGroup met items waar je doorheen kan scrollen. De items worden net als bij een spinner gemaakt met een Adapter.

In onderstaand filmpje uit de cursus developing Android Apps van Udacity wordt uitgelegd waarom een adapter wordt gebruik en waarom dit efficiënter is dan bijvoorbeeld een LinearLayout in een ScrollView plaatsen, wat hetzelfde effect zou hebben.

Onderstaand codefragment toont hoe je met behulp van een ArrayList met Strings een adapter maakt en aan een ListView koppelt. De parameters zijn, afgezien van de gebruikte layout hetzelfde als bij de spinner.

ArrayList<String> listItems = new ArrayList<>();
//TODO vul list met items
ArrayAdapter<String> listViewAdapter = new ArrayAdapter<String>(
            this,
            android.R.layout.simple_list_item_1,
            listItems);
listView.setAdapter(listViewAdapter);

JSON objecten

De currency converter maakt gebruik van de json api van fixer.io. Je kunt bijvoorbeeld een get-request naar de volgende url sturen om de beschikbare wisselkoersen afgezet tegen de Amerikaanse Dollar op te halen:

http://api.fixer.io/latest?base=USD

Het resultaat is een String met de structuur van een JSON object

{
   "base":"USD",
   "date":"2016-04-22",
   "rates":{
      "AUD":1.2936,
      "BGN":1.7365,
      "BRL":3.5708,
      "CAD":1.2734,
      "CHF":0.97585,
      ...
      ...
   }
}

Een JSON object begint en eindigt met een accolade. Daartussen zie je zogenaamde key-value pairs. De key is een string waarmee de value kan worden opgehaald. De eerste twee ("base" en "date") in bovenstaand resultaat zijn gewone key-value pairs waarbij de values strings zijn. De derde ("rates") is ook een key-value pair, alleen is de value ervan op zijn beurt weer een JSON object met zijn eigen key-value pairs. Op deze manier kunnen JSON objecten in JSON objecten worden genest.

Het JSON object wordt binnengehaald als een string. Hiermee kun je eenvoudig een object van de JSONObject klasse maken met behulp van één van zijn constructors:

JSONObject jsonObject = null;
try {
    jsonObject = new JSONObject(jsonString );
} catch (JSONException e) {
    e.printStackTrace();
}

We kunnen nu verschillende values opvragen met behulp van hun keys.

String date = jsonObject.getString("date");
JSONObject rates = jsonObject.getJSONObject("rates");
Double usd = rates.getDouble("USD");

De AsyncTask klasse

De werking van threads

Standaard werkt een Android app met één thread: de "UI thread". Deze thread toont de user interface die wacht op (luistert naar) events die plaatsvinden als gebruiker iets doet. De user interface hoort altijd te reageren op een handeling. Je wilt dus niet dat de app bevriest tot een bepaalde taak is uitgevoerd. Tijdrovende taken zoals het ophalen en verwerken van data moeten in een aparte thread worden afgehandeld.

Asynchrone taken starten

Een asynchrone taak (asynchronous task) is een taak die in een aparte thread op de achtergrond draait. Omdat Android apps dit soort taken vaak gebruiken heeft het Android framework hiervoor een speciale klasse: AsyncTask die het makkelijk maakt om dit soort taken uit te voeren. De klasse heeft een aantal methoden die je kunt gebruiken:

  1. onPreExecute(): deze methode kan iets doen voor de thread wordt gestart.
  2. execute(Params... params) deze methode start de thread
  3. doInBackground(Params... params): deze methode bevat de code die op de achtergrond wordt uitgevoerd. De return-waarde wordt aan de methode onPostExecute meegegeven
  4. publishProgress(Progress... values): publiceert de voortgang vanuit de achtergrondtaak. Deze methode zorgt er voor dat de volgende methode wordt gestart in de UI thread
  5. onProgressUpdate(Progress... values): hiermee kun je bijvoorbeeld een voortgangsindicator in de UI maken.
  6. onPostExecute (Result result): deze methode doet iets nadat doInBackground() is geëindigd. De result parameter is de return-waarde van doInBackground()

De methoden van AsyncTask kennen drie soorten parameters:

  1. params: dit zijn de parameters die worden meegegeven aan de methode execute(Params... params) en die door de methode doInBackground(Params... params) kunnen worden gebruikt. De parameter params is altijd een array van objecten, ook al wordt er maar één object meegegeven.
  2. values: dit zijn de parameters die worden meegegeven aan de methode publishProgress(Progress... values) en die door de methode onProgressUpdate(Progress... values) kunnen worden gebruikt. De parameter values is ook hier een array van objecten.
  3. result: de waarde die de methode doInBackground teruggeeft en die door de methode onPostExecute(Result result) kan worden gebruikt.

Om AsyncClass te gebruiken moet je er een binnenklasse van maken. Daarbij moet je aangeven uit wat voor type objecten de bovengenoemde parameters bestaan. Onderstaande code laat zien hoe een AsyncTask binnenklasse er uitziet, waarbij alleen de (verplichte) methode doInBackground en de methode onPostExecute zijn gebruikt.:

class DownloadTaak extends AsyncTask<String, Void, String> {
    @Override
    protected String doInBackground(String... params ) {
        String url = params[0];
        String result = "";
        //TODO  definieer IO taak
        return result;
    }
    @Override
    protected void onPostExecute(String result) {
        //TODO  ververs UI
    }
}

//start de thread bijvoorbeeld in de onCreate methode
@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    ...
    String url = BASE_URL + "USD";
    new DownloadTaak.execute(url);
}

Meertalige apps maken

In Android sla je strings op in een strings.xml resource bestand in de map values. Dit wordt vooral gedaan om je app geschikt te maken voor meerdere talen. Je kunt namelijk meerdere strings.xml bestanden maken en bij het maken ervan aangeven voor welke taal het bestand bedoeld is met de qualifier locale. Je hoeft dan alleen de string waardes te veranderen. De taal van je app wordt dan aangepast aan de ingestelde taal van de gebruiker.

strings.xml nederlands