September discount week
Pouze tento týden sleva až 80 % na e-learning týkající se MS Office
50 % bodů zdarma na online výuku díky naší Slevové akci!

Lekce 6 - Android programování - Implementace Java kódu kalkulačky

V minulé lekci, Android programování - První aplikace, návrh kalkulačky, jsme se věnovali návrhu jednoduché kalkulačky.

V dnešní lekci si konečně ukážeme Java kód budoucí kalkulačky a dostaneme se tedy k programování!

Java kód aplikace

Kód budeme psát do souboru MainActivity.java, který byl vygenerován Android Studiem při vytváření projektu kalkulačky. Při prvním pohledu do MainActivity.java uvidíme tento kód, obsahující jedinou metodu:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

Zatím jediné dva řádky v onCreate() volají metodu předka a nastavují vzhled grafického uživatelského rozhraní odkazem na XML soubor návrhu activity_main.xml.

Nyní si ukážeme několik dalších částí kódu, které budeme do MainActivity.java postupně přidávat.

Deklarace proměnných

V souboru MainActivity.java, nad metodou onCreate(), nejprve deklarujeme proměnné pro uložení referencí na ty komponenty z XML návrhu, se kterými budeme pracovat v Java kódu:

EditText etNumber1;
EditText etNumber2;
Spinner spinnerOperation;
TextView labelResult;

Všimněte si, že chybí proměnná pro tlačítko =. Jediná práce s tlačítkem je nastavení obslužné metody při kliknutí. A protože jsme toto udělali již v minulé části, přímo v XML návrhu tlačítka parametrem android:onClick="calculate", nemusíme se o to již starat v Java kódu. Lze říci, že jsme tím také ušetřili pár řádků kódu, protože v Java kódu je tato činnost pracnější.

Metoda onCreate()

Metoda onCreate() je tou nejdůležitější metodou naší aplikace. Zde totiž, po spuštění aplikace, vše začíná.

Inicializace proměnných

Zde na konec doplníme inicializaci proměnných na potřebné komponenty z XML návrhu. Díky tomu budeme moci s těmito objekty pracovat zde v Java kódu (získávat obsah zadávacích políček, nastavovat text s výsledkem výpočtu komponentě TextView atd.):

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    etNumber1 = findViewById(R.id.etNumber1);
    etNumber2 = findViewById(R.id.etNumber2);
    spinnerOperation = findViewById(R.id.spinnerOperation);
    btnCalculate = findViewById(R.id.btnCalculate);
    labelResult = findViewById(R.id.labelResult);
}

Jistě jste si všimli, že metoda onCreate() přijímá jeden parametr savedInstanceState. Ten je po spuštění aplikace null. Slouží pro ukládání stavu aplikace a například po změně orientace zařízení již null nebude a bude obsahovat některá data aplikace.

Položky pro Spinner

Na konec metody připíšeme další kód pro přípravu položek pro Spinner:

String[] operatorsArray = getResources().getStringArray(R.array.operators);
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!

Vytváříme zde pole textových řetězců operatorsArray se čtyřmi operátory: ["+", "-", "*", "/"] pro početní operace kalkulačky, které budeme uživateli nabízet v rozbalovacím menu Spinner. Toto pole neinicializujeme přímo, ale hodnotami z pole operators, nacházejícího se v resources v souboru res/values/strings.xml. Pojďme si tam hodnoty deklarovat.

strings.xml

Soubor strings.xml byl při vytvoření projektu automaticky vygenerován Android Studiem a my do něj přidáme deklaraci zmíněného pole operators:

<resources>
    <string name="app_name">SimpleCalc</string>

    <string-array name="operators">
        <item>+</item>
        <item>-</item>
        <item>*</item>
        <item>/</item>
    </string-array>
</resources>

První položka <string name="app_name">SimpleCalc</string> v souboru strings.xml byla automaticky vygenerována při vytvoření projektu a obsahuje textový řetězec s názvem aplikace, který je zobrazován například v toolbaru aplikace nebo v seznamu nainstalovaných aplikací v uživatelově zařízení.

Všechny textové řetězce se v Android aplikacích píší do strings.xml, aby se aplikace dala jednoduše přeložit do více jazyků. U znamének to zatím nedává smysl, ale co kdybychom tam přidali pak nějakou další operaci, co by se jmenovala slovně.

Vrátíme se do metody onCreate() a pokračujeme:

List list = new ArrayList(Arrays.asList(operatorsArray));
ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this, R.layout.spinner_item, list);

Pole možností dosadíme do komponenty Spinner pomocí adaptéru. Pokud jste dělali formulářové aplikace v Java Swing, je to podobné. Z vytvořeného pole operatorsArray vytváříme kolekci List, kterou předáme adaptéru pro Spinner, jehož úkolem je správa položek zobrazovaných uživateli. Konstruktor adaptéru přijímá tři parametry:

  • referenci na aktuální kontext
  • referenci na XML návrh vzhledu té položky Spinner, která je aktuálně vybrána
  • kolekci s položkami Spinner

spinner_item.xml

Ještě musíme vytvořit soubor spinner_item.xml s XML návrhem zvolené položky, tedy jak se položky v něm budou zobrazovat. To uděláme kliknutím pravým tlačítkem myši na složku res/layout/. V menu dále přes New na položku Layout Resource File:

V otevřeném okně, do políčka File name, napíšeme název vytvářeného souboru a obsah políčka Root element nahradíme textem TextView. Při vyplňování políčka Root element nám pomůže našeptávač. Zadání potvrdíme tlačítkem OK:

Vytvořený soubor bude otevřen a my jej upravíme tak, aby obsahoval následující kód:

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:textColor="@android:color/black"
    android:textSize="36sp"
    android:textStyle="bold">
</TextView>

Jak vidíte, každá položka Spinner je tvořena jednou komponentou TextView. Samozřejmě existuje i možnost složitějšího návrhu, obsahujícího více různých komponent, ale takové řešení vyžaduje vlastní adaptér pro Spinner a tím pádem složitější Java kód, jehož ukázka a výklad by vydal na celý článek. My se zde snažíme o maximální jednoduchost.

spinner_dropdown_item.xml

Po inicializaci adaptéru komponenty Spinner nastavujeme vzhled položek, které budou zobrazené v rozbaleném menu, opět prostřednictvím reference na XML soubor:

dataAdapter.setDropDownViewResource(R.layout.spinner_dropdown_item);

Soubor nyní vytvoříme ve složce res/layout/ podobným způsobem jako v předchozím kroku a pojmenujeme jej spinner_dropdown_item.xml. Obsah tohoto souboru bude následující:

<?xml version="1.0" encoding="utf-8"?>
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal"
    android:textSize="36sp"
    android:textStyle="bold">
</CheckedTextView>

V posledním řádku metody onCreate() komponentě Spinner vytvořený adaptér přiřadíme:

spinnerOperation.setAdapter(dataAdapter);

Metoda calculate()

Nyní si popíšeme jedinou naší vlastní metodu, kterou přidáme do souboru MainActivity.java a která bude obsluhovat událost kliknutí na tlačítko =. Jak jsme si již řekli, tlačítko již máme v XML na metodu napojené pomocí atributu android:onClick="calculate".

Aby metoda calculate() mohla na událost kliknutí reagovat, musí přijímat parametr typu View, který bude obsahovat, při volání metody, referenci na ten objekt z XML, od kterého událost přišla. Pokud tento parametr bude chybět, dojde po kliknutí na tlačítko = k pádu aplikace s chybou IllegalStateException:

Kód metody calculate() máme zde:

public void calculate(View view) {
    double number1;
    double number2;
    double result = 0;

    try {
        number1 = Double.parseDouble(etNumber1.getText().toString());
        number2 = Double.parseDouble(etNumber2.getText().toString());

        if (spinnerOperation.getSelectedItem().equals("+")) {
            result = number1 + number2;
        } else if (spinnerOperation.getSelectedItem().equals("-")) {
            result = number1 - number2;
        } else if (spinnerOperation.getSelectedItem().equals("*")) {
            result = number1 * number2;
        } else if (spinnerOperation.getSelectedItem().equals("/")) {
            if (number2 == 0) {
                labelResult.setText("NULOU DĚLIT NELZE!");
                return;
            }

            result = number1 / number2;
        }

        NumberFormat nf = new DecimalFormat("#.###");
        labelResult.setText(nf.format(result));
    } catch (NumberFormatException e) {
        labelResult.setText("Chybné zadání...");
    }
}

V úvodu metody calculate() deklarujeme proměnné number1 a number2, do kterých budeme ukládat uživatelem zadaná čísla a proměnnou result pro uložení výsledku výpočtu.

Další blok kódu je umístěn v bloku try - catch pro odchycení případné výjimky NumberFormatException, ke které dojde při zadání nečíselné hodnoty do jednoho z políček. Konkrétně by k chybě došlo při převodu datových typů, kdy bude na místě výsledku zobrazeno hlášení o chybném zadání. Do proměnných number1 a number2 ukládáme obsah zadávacích políček, který je pomocí třídy Double převáděn z typu String na typ double.

Po úspěšném převodu datových typů dochází podmínkou if k větvení programu do čtyř možných scénářů podle typu matematické operace, která je uživatelem vybrána ve Spinner. Ve všech případech do proměnné result ukládáme výsledek výpočtu. Pokud je požadovanou operací dělení, bude druhé zadané číslo otestováno na nulovou hodnotu. Při nulové hodnotě druhého čísla by došlo k porušení jedenáctého přikázání - dělení nulou :-). Pokud uživatel bude chtít dělit nulou, vypíšeme mu, místo výsledku, hlášku o tom, že dělit nulou nelze a metoda bude ukončena příkazem return.

Po provedení úspěšného výpočtu přichází čas k zobrazení výsledku. Než výsledek zobrazíme v komponentě TextView, "proženeme" jej třídou NumberFormat, která výsledek zaokrouhlí na definovaný počet desetinných míst. Až po této úpravě TextView nastavujeme text v podobě získaného výsledku výpočtu, aby byl zobrazen uživateli.

Kód kalkulačky tímto máme hotový :)

Příště, v lekci Android programování - Dokončení implementace kalkulačky, se budeme věnovat ladění a generování .apk souboru.


 

Předchozí článek
Android programování - První aplikace, návrh kalkulačky
Všechny články v sekci
Základy vývoje Android aplikací v Javě
Článek pro vás napsal Jiří Frank
Avatar
Jak se ti líbí článek?
7 hlasů
Autor se věnuje programování v C# a vývoji aplikací pro platformu Android a dalším věcem spojeným s Android OS
Aktivity (13)

 

 

Komentáře

Avatar
B42P6
Člen
Avatar
B42P6:23.5.2015 23:09

Prečo musia byť objekty (V tomto prípade TextView) inicializovane v onCreate()
a nie už v atributoch

public class MainActivity extends Activity {

    TextView number=(TextView)findViewById(R.id.number2);
    TextView number2=(TextView)findViewById(R.id.number2);



    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        number.setText("Text");
        number.setText("Text");
    }

takto mi to hneď po zapnutí padlo.

Odpovědět
23.5.2015 23:09
'long long long' is too long for GCC
Avatar
Jan Vargovský
Redaktor
Avatar
Odpovídá na B42P6
Jan Vargovský:23.5.2015 23:14

Protože to nejde a je to nepřehledné.

 
Odpovědět
23.5.2015 23:14
Avatar
d0d0s8.rv
Člen
Avatar
d0d0s8.rv:9.6.2015 21:04

Cau, zacinam podle zdejsich tutotrialu ucit. projel jsem nejaky tutotrialy s javou a neco zkousel. Ale tady mi podle tohodle tutotrialu nejde kalkulacka vytvorit, v kodu:

@Override
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getSupportActionBar().hide();//skryje vrchní lištu
        setContentView(R.layout.activity_calc);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR); //zabrání změnám orientace

        Obraz=(TextView)findViewById(R.id.Obrazovka);
        Obraz.setText("0");

        Cislo1=(TextView)findViewById(R.id.Cislo1);
        Cislo1.setText("NaN");

        Cislo2=(TextView)findViewById(R.id.Cislo2);
        Cislo2.setText("NaN");

        Operace=(TextView)findViewById(R.id.Znamenko);
        Operace.setText("...");
}

My "R" zcervena a pise: cannot resolve symbol "R"
A pismeno R zcervena i kdyz kody kopiruju primo odsud. Jediny co jsem udelal jinak bylo ze jsem vybral API od verze 4.1. ActionBarActivity jsem mel preskrutnute. Nejsem moc dobrej anglictinar ale na internetu jsem nasel ze ActionBarActivity mam nahradit AppCompatActivity a tim se to vyresilo (i jine chyby) jen cervene R pretrvava. Zkousel jsem podle jednoho navodu jit do File>|Settings>Com­piler a odskrknout (zrusit fajfku) u "Use in-process build" a po té dát "Rebuild Project". Ani to nepomohlo a R.java se nevytvorilo a "R" v mem programu zustalo cervene. V anglictine jsem nasel snad desitky postupu jak to opravit ale jelikoz nejsem moc zbehli v anlictine a vubec zkouset vsechny by trvalo dlouho tak víte nekdo jak to opravit? Android studio jsem nainstaloval ted a zatím s tím mám jen problemy :D (hned nazacatku mi nesel mod Desing, psalo to, ze mam stahnout novejsi Android Studio, vyreseno nastavenim jineho Theme). Kdyby nekdo vedel jak na to byl bych vdecny. Diky moc a omlouvam se za chybejci diakritiku :).

 
Odpovědět
9.6.2015 21:04
Avatar
B42P6
Člen
Avatar
Odpovídá na d0d0s8.rv
B42P6:10.6.2015 23:26

Skús Build>Clean Project

Odpovědět
10.6.2015 23:26
'long long long' is too long for GCC
Avatar
d0d0s8.rv
Člen
Avatar
Odpovídá na B42P6
d0d0s8.rv:11.6.2015 11:07

To jsem zkousel a nic :/

 
Odpovědět
11.6.2015 11:07
Avatar
dirtyjobs
Člen
Avatar
Odpovídá na d0d0s8.rv
dirtyjobs:11.6.2015 14:10

Špatná cesta ke zdrojům, několikrát tam musíš změnit cestu, mám dojem, že ten projekt co je ke stažení nelze jen tak zkopírovat a buildnout.

 
Odpovědět
11.6.2015 14:10
Avatar
d0d0s8.rv
Člen
Avatar
Odpovídá na dirtyjobs
d0d0s8.rv:14.6.2015 11:09

Ten jsem ani puvodne nestahoval az kdyz me to furt neslo, ale uz to jede zkousel sem par veci z netu ani si nejsem ted jistej co zabralo jak sem toho zkousel tolik :D ale tusim, ze to zaclo jit po tom co jsem zmenil neco v nastaveni verze Buid tools.

 
Odpovědět
14.6.2015 11:09
Avatar
Blaze
Člen
Avatar
Blaze:26.9.2015 11:44

Som slepy alebo ozaj tu nieje ten archiv so zdrojovym kodom?

 
Odpovědět
26.9.2015 11:44
Avatar
Taskkill
Redaktor
Avatar
Odpovídá na Blaze
Taskkill:26.9.2015 12:53

Archiv je v následující lekci - komplet.

 
Odpovědět
26.9.2015 12:53
Avatar
Drezir
Člen
Avatar
Odpovídá na B42P6
Drezir:28.10.2015 16:18

Asi to bude tím, že metoda onCreate() inicializuje komponenty, takže až po inicializaci můžeš získávat komponenty. Nicméně globální úroveň z toho jde taky udělat tak, že jen deklaruješ objekty a pak k nim přiřadíš hodnotu.

 
Odpovědět
28.10.2015 16:18
Avatar
Jan Praks
Člen
Avatar
Jan Praks:21.7.2018 17:38

Nejak mi nejde zdedit od tridy ActionBarActivity, nevite co muze byt chybou?

 
Odpovědět
21.7.2018 17:38
Avatar
Libor Šimo (libcosenior):6.10.2018 12:22

Ahoj, je možné vypisovať niekde výsledné hodnoty pre kontrolu, okrem toho, že appku spustím na mobile?

Odpovědět
6.10.2018 12:22
Aj tisícmíľová cesta musí začať jednoduchým krokom.
Avatar
zitekv
Člen
Avatar
Odpovídá na Libor Šimo (libcosenior)
zitekv:7.10.2018 11:26

Ahoj,
Můžeš použít třídu Log např. Log.d("varianta", "zvoleno: " + volba);
Pro více info
https://developer.android.com/…ug/am-logcat

 
Odpovědět
7.10.2018 11:26
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!
Avatar
Odpovídá na zitekv
Libor Šimo (libcosenior):8.10.2018 8:02

Díky, vyskúšam to.

Odpovědět
8.10.2018 8:02
Aj tisícmíľová cesta musí začať jednoduchým krokom.
Avatar
Libor Šimo (libcosenior):9.10.2018 20:02

Wau, stalo sa mi, že som niečo testoval a určite som nemal niečo košér, ale že mi to zmazalo veľkú časť kódu, to som vážne nečakal. Tiež riešenie. Keď si debil, píš všetko znovu a nauč sa to poriadne. :-D

Odpovědět
9.10.2018 20:02
Aj tisícmíľová cesta musí začať jednoduchým krokom.
Avatar
Libor Šimo (libcosenior):9.10.2018 20:03

Zatiaľ výsledky nič moc, ale zááááábava. :-D

Odpovědět
9.10.2018 20:03
Aj tisícmíľová cesta musí začať jednoduchým krokom.
Avatar
Odpovídá na Jan Praks
Libor Šimo (libcosenior):9.10.2018 20:05

import android.app.Ac­tivity;

public class MainActivity extends Activity {
....

Odpovědět
9.10.2018 20:05
Aj tisícmíľová cesta musí začať jednoduchým krokom.
Avatar
Libor Šimo (libcosenior):26.10.2018 21:17

Prišiel som na ďalšiu srandu.
V prílohe je kus kódu, ktorý označuje premennú R ako neplatnú. Nedivím sa. Nikde nie je definovaná.
Je zaujímavé, že pred časom mi to fungovalo aj bez definície a deklarácie R.

Odpovědět
26.10.2018 21:17
Aj tisícmíľová cesta musí začať jednoduchým krokom.
Avatar
Odpovídá na Libor Šimo (libcosenior)
Ing. Petr Štechmüller:26.10.2018 21:19

Ahoj, R je automaticky generovaná třída. R znamená resources a obsahuje unikátní identifikátory na všechny resourcy přiložené k projektu.

Odpovědět
26.10.2018 21:19
Pokud spolu kód a komentář nekorespondují, budou patrně oba chybné
Avatar
Libor Šimo (libcosenior):26.10.2018 21:26

Príloha, zabudol som.

Odpovědět
26.10.2018 21:26
Aj tisícmíľová cesta musí začať jednoduchým krokom.
Avatar
Odpovídá na Ing. Petr Štechmüller
Libor Šimo (libcosenior):26.10.2018 21:27

Tk potom nechápem, prečo mi ju farbí na červeno ako chybu.

Odpovědět
26.10.2018 21:27
Aj tisícmíľová cesta musí začať jednoduchým krokom.
Avatar
Odpovědět
26.10.2018 21:29
Aj tisícmíľová cesta musí začať jednoduchým krokom.
Avatar
Odpovídá na Ing. Petr Štechmüller
Libor Šimo (libcosenior):28.10.2018 11:23

Bolo to tým, že som v android studiu otvoril projekt, ktorý používal nesprávny layout.

Odpovědět
28.10.2018 11:23
Aj tisícmíľová cesta musí začať jednoduchým krokom.
Avatar
Petr Novosad
Člen
Avatar
Petr Novosad:11. června 14:25

Ahoj.
V souboru MainActivity mám pořád červený btnCalculate. Píše mi to " Cannot resolve symbol 'btnCalculate' ". Nevíte někdo čím by to mohlo být?

 
Odpovědět
11. června 14:25
Avatar
Abe
Člen
Avatar
Odpovídá na Petr Novosad
Abe:9. srpna 13:28

Ona je tu totiž chybka. V deklaraci btnCalculate uvedený není, u inicializace je však řádek

btnCalculate = findViewById(R.id.btnCalculate);

Jiří Frank nebo někdo, kdo má možnost – spravte to prosím, ať to nikoho nemate.

 
Odpovědět
9. srpna 13:28
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 25 zpráv z 25.