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é.
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.