Зная некоторые основы компоновки и такие элементы как TextView, EditText и Button, уже можно составить более менее полноценное приложение. В данном случае мы сделаем простенький калькулятор.
Для этого создадим новый проект и определим в файле activity_main.xml следующий интерфейс:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="8dp">
<!-- поле результата -->
<TextView
android:id="@+id/resultField"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintHorizontal_weight="1"
android:textSize="18sp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/operationField"/>
<!-- поле знака операции -->
<TextView
android:id="@+id/operationField"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintHorizontal_weight="1"
android:textSize="18sp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toRightOf="@+id/resultField"
/>
<!-- поле ввода чисел -->
<EditText
android:id="@+id/numberField"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:inputType="phone"
app:layout_constraintTop_toBottomOf="@+id/resultField"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"/>
<LinearLayout
android:id="@+id/firstButtonPanel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
app:layout_constraintTop_toBottomOf="@+id/numberField"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent">
<Button
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="7"
android:id="@+id/n7" />
<Button
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="8"
android:id="@+id/n8"/>
<Button
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="9"
android:id="@+id/n9"/>
<Button
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="/"
android:id="@+id/div"/>
</LinearLayout>
<LinearLayout
android:id="@+id/secondButtonPanel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
app:layout_constraintTop_toBottomOf="@+id/firstButtonPanel"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent">
<Button
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="4"
android:id="@+id/n4"/>
<Button
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="5"
android:id="@+id/n5"/>
<Button
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="6"
android:id="@+id/n6"/>
<Button
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="*"
android:id="@+id/mul"/>
</LinearLayout>
<LinearLayout
android:id="@+id/thirdButtonPanel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
app:layout_constraintTop_toBottomOf="@+id/secondButtonPanel"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent">
<Button
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="1"
android:id="@+id/n1"/>
<Button
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="2"
android:tag="num"
android:id="@+id/n2"/>
<Button
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="3"
android:id="@+id/n3"/>
<Button
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="-"
android:id="@+id/sub" />
</LinearLayout>
<LinearLayout
android:id="@+id/forthButtonPanel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
app:layout_constraintTop_toBottomOf="@+id/thirdButtonPanel"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent">
<Button
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="0"
android:id="@+id/n0"/>
<Button
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text=","
android:id="@+id/comma"/>
<Button
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="+"
android:id="@+id/add"/>
<Button
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="="
android:id="@+id/eq"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
В итоге весь интерфейс будет выглядеть следующим образом:
Корневой контейнер компоновки представляет элемент ConstraintLayout . Сверху в нем определены два текстовых поля TextView: одно для вывода результата вычислений и одно для вывода текущего знака операции.
Затем идет элемент EditText, предназначенный для ввода чисел.
И далее расположены четыре элемента LinearLayout с горизонтальными рядами кнопок. Чтобы все кнопки занимали равное пространство внутри контейнера, для них установлены атрибуты
android:layout_weight="1" и android:layout_width="0dp".
Для каждой кнопки установлен идентификатор, чтобы в коде можно было прикрепить к ней определенный обработчик нажатия.
Теперь изменим класс MainActivity:
package com.metanit.calculator;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
TextView resultField; // текстовое поле для вывода результата
EditText numberField; // поле для ввода числа
TextView operationField; // текстовое поле для вывода знака операции
Double operand = null; // операнд операции
String lastOperation = "="; // последняя операция
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// получаем все поля по id из activity_main.xml
resultField = findViewById(R.id.resultField);
numberField = findViewById(R.id.numberField);
operationField = findViewById(R.id.operationField);
findViewById(R.id.add).setOnClickListener((view)->onOperationClick("+"));
findViewById(R.id.sub).setOnClickListener((view)->onOperationClick("-"));
findViewById(R.id.mul).setOnClickListener((view)->onOperationClick("*"));
findViewById(R.id.div).setOnClickListener((view)->onOperationClick("/"));
findViewById(R.id.eq).setOnClickListener((view)->onOperationClick("="));
findViewById(R.id.n0).setOnClickListener((view)->onNumberClick("0"));
findViewById(R.id.n1).setOnClickListener((view)->onNumberClick("1"));
findViewById(R.id.n2).setOnClickListener((view)->onNumberClick("2"));
findViewById(R.id.n3).setOnClickListener((view)->onNumberClick("3"));
findViewById(R.id.n4).setOnClickListener((view)->onNumberClick("4"));
findViewById(R.id.n5).setOnClickListener((view)->onNumberClick("5"));
findViewById(R.id.n6).setOnClickListener((view)->onNumberClick("6"));
findViewById(R.id.n7).setOnClickListener((view)->onNumberClick("7"));
findViewById(R.id.n8).setOnClickListener((view)->onNumberClick("8"));
findViewById(R.id.n9).setOnClickListener((view)->onNumberClick("9"));
findViewById(R.id.comma).setOnClickListener((view)->onNumberClick(","));
}
// сохранение состояния
@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putString("OPERATION", lastOperation);
if(operand!=null)
outState.putDouble("OPERAND", operand);
super.onSaveInstanceState(outState);
}
// получение ранее сохраненного состояния
@Override
protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
lastOperation = savedInstanceState.getString("OPERATION");
operand= savedInstanceState.getDouble("OPERAND");
resultField.setText(operand.toString());
operationField.setText(lastOperation);
}
// обработка нажатия на числовую кнопку
public void onNumberClick(String number){
numberField.append(number);
if(lastOperation.equals("=") && operand!=null){
operand = null;
}
}
// обработка нажатия на кнопку операции
public void onOperationClick(String op){
String number = numberField.getText().toString();
// если введенно что-нибудь
if(number.length()>0){
number = number.replace(',', '.');
try{
performOperation(Double.valueOf(number), op);
}catch (NumberFormatException ex){
numberField.setText("");
}
}
lastOperation = op;
operationField.setText(lastOperation);
}
private void performOperation(Double number, String operation){
// если операнд ранее не был установлен (при вводе самой первой операции)
if(operand ==null){
operand = number;
}
else{
if(lastOperation.equals("=")){
lastOperation = operation;
}
switch(lastOperation){
case "=":
operand =number;
break;
case "/":
if(number==0){
operand =0.0;
}
else{
operand /=number;
}
break;
case "*":
operand *=number;
break;
case "+":
operand +=number;
break;
case "-":
operand -=number;
break;
}
}
resultField.setText(operand.toString().replace('.', ','));
numberField.setText("");
}
}
Разберем этот код. Вначале в методе onCreate() получаем все поля из activity_main.xml, текст которых будет изменяться:
resultField = findViewById(R.id.resultField); numberField = findViewById(R.id.numberField); operationField = findViewById(R.id.operationField);
Далее для каждой кнопки назначаем определенный обработчик нажатия - в зависимости от типа кнопки это либо метод onNumberClick, в который передается нажатое число,
либо onOperationClick, в который передается знак операции.
Результат операции будет попадать в переменную operand, которая представляет тип Double, а знак операции - в переменную lastOperation:
Double operand = null; String lastOperation = "=";
Так как при переходе от портретной ориентации к альбомной или наоборот мы можем потерять все введенные данные, то чтобы их не потерять, мы их сохраняем в методе
onSaveInstanceState() и обратно получаем в методе onRestoreInstanceState().
При нажатии на числовую кнопку будет вызываться метод onNumberClick, в котором добавляем введенную цифру или знак запятой к тексту в поле numberField:
numberField.append(number);
if(lastOperation.equals("=") && operand!=null){
operand = null;
}
При этом если последняя операция представляла собой получение результата (знак "равно"), то мы сбрасываем переменную operand.
В методе onOperationClick происходит обработка нажатия на кнопку со знаком операции:
String number = numberField.getText().toString();
if(number.length()>0){
number = number.replace(',', '.');
try{
performOperation(Double.valueOf(number), op);
}catch (NumberFormatException ex){
numberField.setText("");
}
}
lastOperation = op;
operationField.setText(lastOperation);
Здесь получаем ранее введенное число и введенную операцию и передаем их в метод performOperation(). Так как в метод передается не просто строка, а число
Double, то нам надо преобразовать строку в чсло. И поскольку теоретически могут быть введены нечисловые символы, то для отлова исключения, которое может возникнуть при преобразовании
используется конструкция try...catch.
Кроме того, так как разделителем целой и дробной части в Double в java является точка, то нам надо заменить запятую на точку, так как предполагается, что мы используем в качестве разделителя запятую.
А методе performOperation() выполняем собственно операцию. При вводе первой операции, когда операнд еще не установлен, мы просто устанавливаем операнд:
if(operand ==null){
operand = number;
}
При вводе второй и последующих операций применяем предыдущую операцию, знак которой хранится в переменной lastOperation, к операнду operand и второму числу, которое было введено в числовое поле. Полученный результат операции сохраняем в переменной operand.