Publié le 11/03/2015
Auteur Axel
Réseaux sociaux
0 partages
0 tweets
0 plus
0 commentaires

Source d'une calculatrice pour android

Après plusieurs jours de tatonnement, voici le code source d'une application calculatrice. Après compilation du code, on obtient un apk qui s'installe et se lance sous Android. Nous allons voir dans le tuto la construction du layout et notamment le code Java avec l'interpréteur mathématique.

Interface graphique de la calculatrice

Comme on peut le voir sur la copie d'écran ci-dessous, la calculatrice comporte les éléments graphiques suivants:
zone de visualisation: il s'agit d'un EditText qui afficher l'opération mathématique à calculer,
17 boutons: les boutons servent à saisir les chiffres et les opérateurs de calcul.
En ce qui concerne le layout, nous avons choisi de combiner au LinearLayout un TableLayout. En effet, ce type de conteneur correspond parfaitement à la disposition des boutons en forme de tableau. De plus, la largeur des boutons est adaptées automatiquement à la taille de l'écran de sorte que l'ensemble de l'interface graphique soit occupé.

1164-ma-calculatrice

Contenu du layout
Dans un premier temps, le fichier xml était assez long car il comportait la définition et le style pour les 17 boutons. Sur les conseils de Fobec, j'ai retiré les styles du layout pour ne conserver que la disposition des boutons dans le layout. On le verra par la suite, les propriétés de chaque élément graphique est ajouté par la suite dans une boucle puisque le style est le meme.
<?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">
    <TableLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/table_layout"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentBottom="true">
        <TableRow
            android:id="@+id/tableRow1"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content" >
            <EditText
                android:id="@+id/et_calc"
                android:background="#ffffff"
                android:layout_weight="1"
                android:layout_width="0dip"
                android:layout_height="wrap_content"
                android:textSize="36sp"
                android:inputType="none" 
                android:editable="false" 
                android:focusable="false">
            </EditText>     
        </TableRow>        
        <TableRow
            android:id="@+id/tableRow2"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content" >     
            <Button android:id="@+id/bt_1"
                android:text="1" />    
            <Button android:id="@+id/bt_2"
                android:text="2"/>
            <Button android:id="@+id/bt_3"
                android:text="3"/>     
            <Button android:id="@+id/bt_div"
                android:text="/"/>     
        </TableRow>
        <TableRow
            android:id="@+id/tableRow3"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content" >     
            <Button android:id="@+id/bt_4"
                android:text="4" />    
            <Button android:id="@+id/bt_5"
                android:text="5"/>
            <Button android:id="@+id/bt_6"
                android:text="6"/>     
            <Button android:id="@+id/bt_div"
                android:text="*"/>     
        </TableRow>
        <TableRow
            android:id="@+id/tableRow4"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content" >     
            <Button android:id="@+id/bt_7"
                android:text="7" />    
            <Button android:id="@+id/bt_8"
                android:text="8"/>
            <Button android:id="@+id/bt_9"
                android:text="9"/>     
            <Button android:id="@+id/bt_div"
                android:text="-"/>     
        </TableRow>
        <TableRow
            android:id="@+id/tableRow5"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content" >     
            <Button android:id="@+id/bt_0"
                android:text="0" />    
            <Button android:id="@+id/bt_clear"
                android:text="C"/>
            <Button android:id="@+id/bt_dot"
                android:text="."/>     
            <Button android:id="@+id/bt_div"
                android:text="+"/>     
        </TableRow>
        <TableRow
            android:id="@+id/tableRow6"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content" >     
            <Button android:id="@+id/bt_result"
                android:text="=" />
        </TableRow>
    </TableLayout>
</LinearLayout>

Activity de la calculette et interpréteur mathématique


Le code source de la calculatrice comporte des fonctions standards pour lier l'interface graphique à l'interpréteur mathématique puis afficher le résultat dans le EditText en haut de fenêtre. Pour partager mon expérience, je voudrais détailler deux parties de l'application:

Fixer le style dynamiquement
: comme dit, reprendre 17 fois la même définition de style était inutile. De plus, dans l'activity, il fallait revenir sur chaque bouton puis lui assigner l’évènement du click souris.
Au lieu de donner à chaque bouton une variable, nous avons trouvé une autre solution. Une boucle est lancée dans initComponents() pour chercher les composants graphiques enfants de TableRow. Lorsque la class du composant enfant est un bouton, un listener est attaché et le style est définit dynamiquement.

Interpréteur mathématique: comment calculer le résultat de l'opération ?
La première idée était de calculer le résultat au fur et à mesure de l'appui des touches. Par contre, l'opération de multiplication et de division est prioritaire à l'addition et la soustraction. La deuxième idée était d'utiliser l'interpréteur javascript dans un WebView pour obtenir le résultat de l'opération mathématique. Au fil des recherches, nous avons le code source publié sur Stackoverflow par Boann. Nickel !

import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TableLayout;
import android.widget.TableRow;
 
/**
 * Une calculatrice pour android
 * http://www.fobec.com/java/1164/source-une-calculatrice-pour-android.html
 * Axel 11/03/2015
 */
public class MaCalculatrice extends Activity {
 
    private EditText editTextCalc;
 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        //Initialisation des composants graphiques
        initComponents();
        editTextCalc = (EditText) findViewById(R.id.et_calc);
    }
 
    /**
     * Evenement lors de l'appui sur une touche
     * @param command 
     */
    public void appendCommand(String command) {
        //Calculer le résultat de l'opération
        if (command.equalsIgnoreCase("=")) {
            String formula = editTextCalc.getText().toString();
            double sum = eval(formula);
            editTextCalc.setText(fmt(sum));
        } 
        //Effacer la zone de saisie
        else if (command.equalsIgnoreCase("C")) {
            editTextCalc.setText("");
        } 
        //Ajouter un chiffre ou un opérateur mathématique
        else {
            //[0-9] ou .,+,*,/,-
            editTextCalc.append(command);
        }
    }
 
    /**
     * Initialisation des composants avec l'ajout du style et de l'evenement click
     * Il y a plusieurs boucles qui cherchent les enfants jusqu'aux buttons
     */
    public void initComponents() {
        TableLayout table_layout = (TableLayout) findViewById(R.id.table_layout);
        for (int i = 0; i < table_layout.getChildCount(); i++) {
            View table_row = table_layout.getChildAt(i);
            if (table_row instanceof TableRow) {
                TableRow row = (TableRow) table_row;
                for (int j = 0; j < row.getChildCount(); j++) {
                    View table_button = row.getChildAt(j);
                    if (table_button instanceof Button) {
                        Button button = (Button) table_button;
                        //Récupère le layout existant
                        TableRow.LayoutParams layoutParams = (TableRow.LayoutParams) button.getLayoutParams();
                        //Ajouter les propriétés de taille
                        layoutParams.width = TableLayout.LayoutParams.MATCH_PARENT;
                        layoutParams.height = TableLayout.LayoutParams.WRAP_CONTENT;
                        layoutParams.weight = 1.0f;
                        layoutParams.setMargins(2, 2, 2, 2);
                        //Fixer les LayoutParams
                        button.setLayoutParams(layoutParams);
                        //Change la couleur des boutons
                        String c = button.getText().toString();
                        if (android.text.TextUtils.isDigitsOnly(c)) {
                            button.setBackgroundColor(Color.parseColor("#555555"));
                        } else {
                            button.setBackgroundColor(Color.parseColor("#333333"));
                        }
 
                        button.setTextColor(Color.parseColor("#ffffff"));
                      //  button.setTextAppearance(this, R.style.largeText);
 
                        //Fixer le listener pour l'appui
                        button.setOnClickListener(new OnClickListener() {
                            public void onClick(View v) {
                                String buttonPressed = ((Button) v).getText().toString();
                                appendCommand(buttonPressed);
                            }
                        });
                    }
                }
            }
        }
    }
    /**
     * Formater un nombre sans virgule au besoin
     * @param d
     * @return 
     */
    private static String fmt(double d) {
        if (d == (long) d) {
            return String.format("%d", (long) d);
        } else {
            return String.format("%s", d);
        }
    }
 
    /**
     * Interpréteur mathématique dont l'auteur est
     * http://stackoverflow.com/users/964243/boann
     * @param str
     * @return 
     */
    public static double eval(final String str) {
        class Parser {
 
            int pos = -1, c;
 
            void eatChar() {
                c = (++pos < str.length()) ? str.charAt(pos) : -1;
            }
 
            void eatSpace() {
                while (Character.isWhitespace(c)) {
                    eatChar();
                }
            }
 
            double parse() {
                eatChar();
                double v = parseExpression();
                if (c != -1) {
                    throw new RuntimeException("Unexpected: " + (char) c);
                }
                return v;
            }
 
            // Grammar:
            // expression = term | expression `+` term | expression `-` term
            // term = factor | term `*` factor | term `/` factor | term brackets
            // factor = brackets | number | factor `^` factor
            // brackets = `(` expression `)`
            double parseExpression() {
                double v = parseTerm();
                for (;;) {
                    eatSpace();
                    if (c == '+') { // addition
                        eatChar();
                        v += parseTerm();
                    } else if (c == '-') { // subtraction
                        eatChar();
                        v -= parseTerm();
                    } else {
                        return v;
                    }
                }
            }
 
            double parseTerm() {
                double v = parseFactor();
                for (;;) {
                    eatSpace();
                    if (c == '/') { // division
                        eatChar();
                        v /= parseFactor();
                    } else if (c == '*' || c == '(') { // multiplication
                        if (c == '*') {
                            eatChar();
                        }
                        v *= parseFactor();
                    } else {
                        return v;
                    }
                }
            }
 
            double parseFactor() {
                double v;
                boolean negate = false;
                eatSpace();
                if (c == '(') { // brackets
                    eatChar();
                    v = parseExpression();
                    if (c == ')') {
                        eatChar();
                    }
                } else { // numbers
                    if (c == '+' || c == '-') { // unary plus & minus
                        negate = c == '-';
                        eatChar();
                        eatSpace();
                    }
                    StringBuilder sb = new StringBuilder();
                    while ((c >= '0' && c <= '9') || c == '.') {
                        sb.append((char) c);
                        eatChar();
                    }
                    if (sb.length() == 0) {
                        throw new RuntimeException("Unexpected: " + (char) c);
                    }
                    v = Double.parseDouble(sb.toString());
                }
                eatSpace();
                if (c == '^') { // exponentiation
                    eatChar();
                    v = Math.pow(v, parseFactor());
                }
                if (negate) {
                    v = -v; // exponentiation has higher priority than unary minus: -3^2=-9
                }
                return v;
            }
        }
        return new Parser().parse();
    }
}

La calculatrice est ma première application pour Android pour laquelle je partage le code source. Bien évidement, la calculatrice est installée sur mon téléphone portable, on est mieux servi par soi-même ;)
Après une petite phase de test, l'application MaCalculatrice sera peut-être publiée sur le site de Fobec.com, je voudrais ajouter une fonctions d'historiques des opérations mathématiques avant.

Ajouter un commentaire

Les champs marqués d'un * sont obligatoires, les adresses emails se sont pas publiées.

A lire aussi

Réseaux sociaux
Présentation de l'article
Catégorie
java - android
Mise a jour
11/03/2015
Visualisation
vu 5079 fois
Public
Internaute
Auteur de la publication
Axel
Membre junior
Auteur de 51 articles
|BIO_PSEUDO|
Commentaires récents

Publié par Vincent dans tuto

Sympa votre petit script :) Oui il existe des bibliotheques deja toutes faites mais elles sont en general 50x plus lourdes que ce petit bout de code qui fait exactement ce qu'il faut. En plus votr...

Publié par Fobec dans news

Bonjour,
la localisation des adresses ip utilise plusieurs algo de recherche de position geographique. La precision du rapport d'analyse correspond la qualite de la localisation:
9/10 la locali...

Publié par Kezouille dans java

Bonjour Axel,

Merci pour ce petit guide qui m'a bien aide dans ma tache. En effet j'ai developpe une petite application afin de detecter l'ajout d'un fichier une image dans un repertoire et de...

Publié par hisy dans java

Merci pour le script mais si un champ text contient un ";" ...

Publié par jadu dans tuto

Et comment met-on en route Xinha lorsque l'extension a été chargée dans Firefox ?????

je n'ai rien trouvé !!!