wiki:BensaLaskuri
Last modified 7 years ago Last modified on 2011-05-17 18:49:07

Takaisin Android -etusivulle

1. Bensalaskuri - Tiedon tallentaminen tietokantaan Androidissa

1.1 Luo uusi android projekti

  • Project Name: BensaLaskuri
  • Build Target: Google APIs 2.2
  • Application Name: Bensalaskuri
  • Package Name: fi.jyu.mit.android.bensalaskuri
  • Create Activity: BensaLaskuri

Järjestelmä luo automaattisesti tarvittavat tiedostot, niihin ei tarvitse kiinnittää huomiota.

[Image(bensalaskuri1.png)]]

1.2 GUI

Ensin lähdetään muokkaamaan ulkoasua. Uudemmassa SDK:ssa on jokseenkin toimiva graafinen työkalu ulkoasun suunnittelemiseen. XML-tiedosto löytyy BensaLaskuri -> res -> layout -> main.xml

Piirretään sopiva GUI. Esimerkkitapauksessa päädyttiin kolmeen EditTextiin, kahteen Buttoniin ja yhteen kulutuksen kertovaan TextViewiin ja yhteen tietokantatestejä varten olevaan TextViewiin.

Vinkki: Tyhjissä EditText kentissä selite on näppärä laittaa Hint propertyyn.

XML:stä tulee suunnilleen seuraavanlainen

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" 

android:hint="Hinta" android:id="@+id/hinta"></EditText>
    <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" 

android:hint="Litroja" android:id="@+id/litroja"></EditText>
    <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" 

android:hint="Kilometrejä" android:id="@+id/kilometreja"></EditText>
    <Button android:layout_width="fill_parent" android:layout_height="wrap_content" 

android:id="@+id/laske" android:text="Laske"></Button>
    <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" 

android:id="@+id/kulutus" android:text="-----"></TextView>
    <Button android:layout_width="fill_parent" android:layout_height="wrap_content" 

android:id="@+id/tallenna" android:text="Tallenna"></Button>
    <TextView android:layout_width="fill_parent" android:layout_height="fill_parent" 

android:id="@+id/testikentta" android:text="---------"></TextView>
</LinearLayout>

[Image(bensalaskuri2.png)]]

Layouttia tehdessä kannattaa myös EditTexteihin lisätä Properties kohdasta rajoitus, joka estää kirjainten syöttämisen kenttiin. Tätä ei esimerkkitapauksessa ole tehty, rajoituksen voi lisätä parametrillä android:inputType="numberDecimal".

1.3 Pääohjelman aloitus

Lähdetään muokkailemaan BensaLaskuri.javaa

Ensin täytyy esitellä käytettävät komponentit ja ja yhdistää ne toimivaksi kokonaisuudeksi.

public class BensaLaskuri extends Activity {
    //Esitellään tarvittava määrä vekottimia
    Button laske, tallenna;
    TextView textViewKulutus;
    EditText editTextHinta;
    EditText editTextMaara;
    EditText editTextKilometreja;
    TextView output; 
    Consumption c; //Kulutusolio

  @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        laske = (Button)findViewById(R.id.laske);
        tallenna = (Button)findViewById(R.id.tallenna);
        textViewKulutus = (TextView)findViewById(R.id.kulutus);
        editTextHinta = (EditText)findViewById(R.id.hinta);
        editTextMaara = (EditText)findViewById(R.id.litroja);
        editTextKilometreja = (EditText)findViewById(R.id.kilometreja);    
        tallenna.setEnabled(false);
        
        output = (TextView) findViewById(R.id.testikentta);
}

1.4 Kulutusolio

Lähdetään luomaan kulutusoliota, jota käytetään tiedon säilyttäjänä ja keskikulutuksen laskijana. Tätä varten tehdään Consumption.java

Kulutusoliolla on muutama tarpeellinen ominaisuus, sen tulee pystyä

  1. tallentamaan hintatieto
  2. tallentamaan litramäärä
  3. tallentamaan ajettu matka
  4. tallentamaan ja laskemaan keskikulutus

Tämän jälkeen koodi näyttää tältä:

 public class Consumption {
        private float price;
        private float amount;
        private float distance;
        private float consumption;

       public float getConsumption() {
            calculateConsumption();
            return this.consumption;
        }


     private void calculateConsumption() {
            this.consumption = this.amount/(this.distance/100);
           
            
        }
}

+ get ja set metodit muille. Periaatteessa get-metodia ei tarvitse kuin kulutukselle. Syy miksi getConsumption kutsuu calculateConsumption -metodia on se, että tällä varmistetaan että keskikulutus on aina laskettu olemassaolevilla arvoilla. Jos ohjelman EditText kenttiin on määritelty sallituiksi merkeiksi vain numerot, ei ongelmia pitäisi tulla. (XML Atribuutti android:inputType="numberDecimal")

1.5 Kenttien tarkistukset

Palataan takaisin BensaLaskuri.javaan.

Ohjelman pitäisi pystyä siis Laske -nappia klikkaamalla hakemaan tiedot tekstikentistä, luomaan Consumption olio ja laittamaan olion laskemat tiedot tekstikenttiin. Ennen näiden toimintojen mahdollistamista, tulee kuitenkin huolehtia siitä, ettei käyttäjä erehdy klikkailemaan Laske namiskaa tyhjillä arvoilla.

Tätä varten tehdään kenttien tarkastusmetodi:

    private boolean validifyFields() {
        if (editTextHinta.getText().length()<1) {
            Toast.makeText(getBaseContext(), " Invalid input: Price", 

Toast.LENGTH_SHORT).show();  
            return false;
        }
        if (editTextMaara.getText().length()<1) {
            Toast.makeText(getBaseContext(), " Invalid input: Amount", 

Toast.LENGTH_SHORT).show();
            return false;
        }
        if (editTextKilometreja.getText().length()<1) {
            Toast.makeText(getBaseContext(), " Invalid input: Distance", 

Toast.LENGTH_SHORT).show();
            return false;
        } 
return true;
    }

Tämän jälkeen luodaan metodi tiedon parsimiseen:

    private void parseValues(Consumption c) {
        
        try {
//Parsitaan arvot
            Float price = Float.parseFloat(editTextHinta.getText().toString());
            Float amount = Float.parseFloat(editTextMaara.getText().toString());
            Float distance = Float.parseFloat(editTextKilometreja.getText().toString());
//Asetetaan arvot kulutusoliolle
            c.setPrice(price);
            c.setAmount(amount);
            c.setDistance(distance);

            } catch (NumberFormatException e) {
                textViewKulutus.setText("Ei ole numero");  //Ei pitäisi olla mahdollinen, jos 

rajoite annettu XML puolella.
            }
        
    }

Ja yksinkertainen metodi tiedon tulostamiseen oikeassa muodossa

   private void printValues(Consumption c) {
//Haetaan kulutus
        double consumption = c.getConsumption();  
//Muotoillaan              
        DecimalFormat df = new DecimalFormat("#.##");   
//Ja laitetaan kenttään    
        textViewKulutus.setText(df.format(consumption).toString() + " l/100km");
        
    }

1.6 Kuuntelijat

Nyt päästään tekemään itse toiminnallisuutta, joka onkin helppo toteuttaa. Luodaan laske napille kuuntelija:

        laske.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {          
                if (validifyFields() == true) { //Jos kaikissa kentissä on tieto      
                c = new Consumption(); //Uusi kulutus
                parseValues(c); //Haetaan arvot
                printValues(c); //Laitetaan arvot kenttiin
                tallenna.setEnabled(true); //Tallenna nappi mahdolliseksi
 
                }
        }
    });

Tämän jälkeen ohjelman pitäisi osata laskea kulutus ja pistää se näkyville tekstikenttään. Seuraavaksi työskentelemään tietokannan pariin. Kannattaa testata :-)

1.7 DbAdapter

Tietokantatyöskentelyä helpottamaan on suotavaa tehdä DbAdapter.java. Yksinkertaisin tapa tehdä DbAdapter luokka tällaiselle yksinkertaiselle projektille on käyttää GitHubista löytyvää Python skriptiä. https://github.com/fedepaol/Android-sql-lite-helper

Skriptin käyttäminen on helppoa:

  • 1. Varmista että sinulla on Python asennettuna koneellesi. Jos ei ole, hae Python osoitteesta www.python.org ja asenna se ohjeiden mukaan.
  • 2. Lataa ja pura Python skripti koneellesi
  • 3. Avaa notepad tai jokin muu tekstiohjelma, ja luo uusi tiedosto db.txt
  • 4. Kirjoita alla oleva tiedostoon ja tallenna se samaan hakemistoon Python skriptin kanssa.
CLASS Event
Date Time	
Float Price
Float Amount
Float Distance
Float consumption
ENDCLASS

  • 6. Avaa komentotulkki (Windowsissa Command Prompt, Linuxmiehet osaavat tämän jokatapauksessa)
  • 7. aja Python skripti komennolla:

pythin sql_lite_helper.py -i db.txt -n DbAdapter -p fi.jyu.mit.android.bensalaskuri

  • 8. Etsi syntynyt DbAdapter.java ja kopioi se projektiisi.

Tämän jälkeen sinulla pitäisi näkyä DbAdapter.java samassa hakemistossa kuin BensaLaskuri.java ja Consumption.java ovat.

Voit tutkia DbAdapterin toimintaa, ja muokata esimerkiksi tietokannan nimeä jne. Tämä ei ole kuitenkaan pakollista toiminnan kannalta. Python skriptin pahin rajoite koskee tietotyyppejä, sillä se tukee ainoastaan String, Float, Long ja Date -tyyppejä. Ei siis kirjoitushetkellä tue Doublea!! Tarkista uuden version tuki GitHubista

1.8 Tietokantatoiminnallisuuden lisääminen

Seuraavaksi tehdään metodi, joka lukee kaikki tietokantamerkinnät

   /**
     * Reads all DB entries into ArrayList
     * @return Results ArrayList<String>
     */
    protected ArrayList<String> readAllDbEntries() {
        ArrayList<String> results = new ArrayList<String>();
        db.open();
        Cursor cu = db.getAllEvent();
//Määritellään aloituskohdat, voi toteuttaa nätimminkin
        int timeColumn = cu.getColumnIndex("Time");
        int priceColumn = cu.getColumnIndex("Price");
        int amountColumn = cu.getColumnIndex("Amount");
        int distanceColumn = cu.getColumnIndex("Distance");
        int consumptionColumn = cu.getColumnIndex("consumption");
        
       //cu.moveToFirst palauttaa false jos palautunut Cursor
//olio on tyhjä. Siirtää myös osoituksen ensimmäiseen merkintään.
            if (cu.moveToFirst() == true) {
                int i = 0;
                do {
                    i++;
//Haetaan tiedot
                    String time = cu.getString(timeColumn);
                    Float price = cu.getFloat(priceColumn);
                    Float amount = cu.getFloat(amountColumn);
                    Float distance = cu.getFloat(distanceColumn);
                    Float consumption = cu.getFloat(consumptionColumn);
//Lisätään merkkijonona ArrayListiin. Ratkaisu on ns. quick and dirty
//mutta helpoin.
                    results.add("" + i + ": "+ 
                            time + " " + 
                            price  + " " + 
                            amount + " " +
                            distance  + " " +
                            consumption
                            );
                } while (i<cu.getCount());
                
            
        }
            db.close();
            return results; 
        
    }

Tämän jälkeen lisätään tallenna nappiin kuuntelija:

            tallenna.setOnClickListener(new View.OnClickListener() {
                
                @Override
                public void onClick(View v) {
                    GregorianCalendar cal = new GregorianCalendar();
                    db.open();
                    //Lisää tapahtuman tietokantaan
                    db.addEvent(cal.getTime(), new Float(c.getPrice()), new Float(c.getAmount

())
                    , new Float(c.getDistance()), new Float(c.getConsumption()));
db.close();
//Asettaa napin pois käytöstä ettei vahingossa naputella useita tapahtumia.
                    tallenna.setEnabled(false);
                   
                    
                }

            });

Tämän jälkeen huomaat, että eclipse herjaa ettei db nimistä härveliä ole esitelty. Esittele DbAdapter db; luokan alussa.

1.9 Tietokantayhteyden avaaminen/sulkeminen ja testidatan tulostus

Tietokantayhteys täytyy avata ennen kuin sitä voidaan käyttää. Mene BensaLaskurin onCreate metodiin, ja kirjoita seuraavat rivit entisten jatkeeksi:

  db = new DbAdapter(this);

Tietokantayhteys tulee myös sulkea. Avaamisen ja sulkemisen voi tehdä jokaisen luku/kirjoituskerran yhteydessä, tai kerran ohjelman avautuessa ja sulkeutuessa. Tässä esimerkissä tietokanta avataan jokaisen luvun ja kirjoituksen yhteydessä.

Sammutusta varten lisää tiedoston loppuun. (Ei pakollinen, jos olet sulkenut tietokantayhteyden jokaisen luvun- ja kirjoituksen jälkeen)

  @Override
    public void onDestroy() {
      db.close();
    }

Tietokantatietojen tulostus output ruutuun Lisää printValues -metodin viimeiseksi riviksi

  output.setText(readAllDbEntries().toString());

Ja nyt jokaisen laskennan yhteydessä alalaitaan ilmestyy aikaisemmat rivit. Tulostus on ruma, mutta testikäyttöön riittävä.