Publié le 11/03/2015
Auteur Axel
Réseaux sociaux
0 partages
0 tweets
3 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 3574 fois
Public
Internaute
Auteur de la publication
Axel
Membre junior
Auteur de 51 articles
|BIO_PSEUDO|
Commentaires récents

Publié par Fokoneh dans java

Je teste d'abord, Je reviens

Publié par Roy2014 dans tuto

Bonjour et merci pour ce tuto interessant. Je pense que tout le monde souffre avec 1and1 du fait que l'on puisse pas gziper directement via le htaccess!!
j'aurais une petite question:
tu dis a p...

Publié par JMC dans logiciel

L'adresse donnée est celle du provider et non celle de l'utilisateur apparemment, il me localise a Braine l'Alleud et je me trouve à Huy ?
Sinon génial qui en est l'auteur ?
Cordialement...

Publié par Fobec dans CMS

Norton emet une alerte de type 'Suspicious.Cloud', hasard de compilation ou menace serieuse ? Faites attention en installant le logiciel.<br>Merci JM de l'avoir signaler.

Publié par Fred dans tuto

J'suis d'accord avec Do, PHP est deja un moteur de template.
Disons que le moteur de template permet de separer le code de la vue pour les debutants qui ont besoin qu'on leur impose des bornes...