• 30 hours
  • Medium

Free online content available in this course.

course.header.alt.is_video

course.header.alt.is_certifying

Got it!

Last updated on 1/13/20

TP : Codez votre propre calculatrice de poche

Log in or subscribe for free to enjoy all this course has to offer!

Je vous propose de réaliser une petite application qui fera une synthèse des principaux thèmes abordés. Nous voudrions que cette application mette en œuvre quelques widgets et qu'elle ait une interface graphique peu complexe, tout en utilisant différents Layouts. Il n'est pas nécessaire que la partie algorithmique soit complexe, puisque ce n'est pas l'objet de ce cours.

Je vous propose donc de réaliser une petite calculatrice simple, avec le visuel suivant :

Visuel proposé pour la calculatrice
Visuel proposé pour la calculatrice

Pour créer cette application, je vous propose la démarche suivante :

  1. créer un nouveau projet ;

  2. faire un Layout satisfaisant au cahier des charges ;

  3. définir les actions des boutons.

Je vous laisse travailler. Si vous coincez quelque part, alors vous trouverez un guide de réalisation du TP ci-dessous et un corrigé en fin de chapitre.

Éléments de correction

Production du visuel de l'application

L'organisation générale demandée peut se décomposer en un texte en haut de l'écran et un tableau de boutons en bas de l'écran. En utilisant le ConstraintLayout fourni par défaut, on peut donc placer un TextView en haut de l'écran sur l'intégralité de la largeur avec un texte aligné à droite :

<TextView
android:layout_width="368dp"
android:layout_height="wrap_content"
android:id="@+id/screen"
android:text="0"
android:textSize="18sp"
android:fontFamily="monospace"
android:textAlignment="viewEnd"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:gravity="end"
android:layout_marginTop="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginEnd="8dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toRightOf="parent" />

Pour le tableau de boutons en bas d'écran, il y a plusieurs manières d'obtenir cette organisation. Par exemple, avec des LinearLayout  imbriqués et un jeu de poids (layout_weight) pour que le bouton = soit plus grand. On aurait aussi pu utiliser un Grid_Layout. Je vous propose d'utiliser unTableLayout  qui permettra de garantir l'égalité des tailles des boutons où on pourra utiliser la notion de colspan  pour que le bouton = soit sur exactement 2 cases.

On déclare donc un TableLayout  aligné sur le bas de l'écran, à qui l'on donne l'intégralité de la largeur de la fenêtre et à qui on dit que les cases sont redimensionnables si nécessaire (stretchColumnsetshrinkColumns) :

<TableLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:stretchColumns="*"
android:shrinkColumns="*"
tools:layout_editor_absoluteX="8dp"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent">

Chaque ligne du tableau doit ensuite être dans une balise TableRow :

<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent" >
<Button
android:text="+"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btnPlus" ></Button>
...
</TableRow>

Enfin le bouton = devra simplement avoir une propriété en plus pour lui dire d'occuper 2 cases :
android:layout_span="2"

Et le tour est joué ! :)

Exemple de style fichier MesStyles.xml que l'on aurait pu créer dans le dossier values :

<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="DigitBtnStyle">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
</style>
</resources>

Ce style pourrait être utilisé pour alléger la définition d'un bouton comme suit :

<Button style="@style/DigitBtnStyle"
android:text="3"
android:id="@+id/btn3" />

Définition du programme Java

Au niveau du comportement on se rend compte que pour faire des opérations binaires (donc avec 2 opérandes ^^), il faudra mémoriser 2 opérandes et un opérateur. L'action de = sera celle qui fera le calcul. Comme il n'y a qu'un seul affichage, il faut aussi mémoriser si on est en train de saisir le premier ou le second opérande. Ainsi, on pourrait avoir comme attributs :

private TextView screen;
private int op1=0;
private int op2=0;
private Ops operator=null;
private boolean isOp1=true;

Dans ces attributs, j'ai fait un choix de représentation pour l'opérateur. On pourrait utiliser simplement un caractère ('+''-''*'  et '/'), mais je vous propose plutôt de faire une énumération, qui permet de caractériser par null, l'absence d'opérateur :

public enum Ops {
PLUS,
MOINS,
FOIS,
DIV;
}

Ainsi, lancer le calcul ne correspondrait qu'à faire l'opération demandée entre les 2 opérandes en mémoire, stocker le résultat en premier opérande et mettre à jour l'affichage :

public void compute() {
if(!isOp1){
switch(operator) {
case PLUS : op1 = op1 + op2; break;
case MOINS : op1 = op1 - op2; break;
case FOIS : op1 = op1 * op2; break;
case DIV : op1 = op1 / op2; break;
default : return; // do nothing if no operator
}
op2 = 0;
isOp1 = true;
updateDisplay();
}
}

Cette action serait à appeler lorsque le bouton btnEgal  serait pressé. Il faudrait donc appeler cette méthode dans un écouteur associé à btnEgal. En choisissant de le faire via une classe anonyme, on pourrait écrire dans onCreate  :

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
screen = (TextView) findViewById(R.id.screen);
Button btnEgal = (Button)findViewById(R.id.btnEgal);
btnEgal.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
compute();
}
});
}

Pour les opérateurs, on souhaiterait aussi faire une unique méthode gérant les 4 cas, mais on voudrait éviter de faire 4 fois ce gros bloc de lignes pour associer le bouton à la méthode. Je vous propose de varier et de le faire via le XML. À chacun des boutons d'opérateur, ajoutez la propriétéandroid:onClick="setOperator". Exemple :

<Button
android:text="*"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btnFois"
android:onClick="setOperator" />

Définissez aussi la méthodesetOperator. Cette méthode devra prendre une View en paramètre et retrouver le bouton qui a été pressé pour agir en conséquence. Pour ce faire, nous pouvons utiliser la méthode getId() présente dans toutes les vues et qui permet de récupérer l'identifiant de la vue qui a généré l'événement. En comparant cette valeur aux 4 id possibles, on peut écrire :

public void setOperator(View v) {
switch (v.getId()) {
case R.id.btnPlus : operator=Ops.PLUS; break;
case R.id.btnMoins : operator=Ops.MOINS; break;
case R.id.btnFois : operator=Ops.FOIS; break;
case R.id.btnDiv : operator=Ops.DIV; break;
default :
Toast.makeText(this, "Opérateur non reconnu",Toast.LENGTH_LONG);
return; // do nothing if no operator
}
isOp1=false;
updateDisplay();
}

Enfin, pour les boutons associés à des chiffres, on pourrait pratiquer de la même manière. Cependant, plutôt que de passer par une énumération de choix, je vous propose de le faire plutôt par conversion numérique de la valeur présente dans le texte du bouton. Ajouter un digit à un opérande revient ensuite à multiplier la valeur actuelle de l'opérande par 10 et à y ajouter la valeur du nouveau digit.

public void addNumber(View v){
try {
int val = Integer.parseInt(((Button)v).getText().toString());
if (isOp1) {
op1 = op1 * 10 + val;
updateDisplay();
} else {
op2 = op2 * 10 + val;
updateDisplay();
}
}catch (NumberFormatException | ClassCastException e) {
Toast.makeText(this, "Valeur erronée",Toast.LENGTH_LONG);
}
}

Pour varier, pour associer l'action du bouton aux 10 boutons de chiffre, je vous propose de passer par une feuille de style. En effet, comme pour les autres propriétés, il est possible de préciser la propriété onClick  dans la feuille de style. En définissant le style suivant :

<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="DigitBtnStyle">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:onClick">addNumber</item>
</style>
</resources>

On pourrait alors avoir une définition très légère des boutons. Exemple pour 3 :

<Button
style="@style/DigitBtnStyle"
android:text="3"
android:id="@+id/btn3" />

Il ne manque alors qu'à écrire la méthode de publication de la valeur de l'opérande en cours de saisie pour avoir fini :

private void updateDisplay() {
int v=op1;
if(!isOp1) {
v=op2;
}
screen.setText(String.format("%9d",v));
}

Une solution complète

Example of certificate of achievement
Example of certificate of achievement