Как создать калькулятор на Java для Android с полным кодом
Калькулятор на Java для Android можно собрать из XML-разметки и одной Activity. В этой инструкции вы создадите экран с кнопками, подключите view binding и добавите четыре арифметических действия.
Пример показывает, как сделать калькулятор с дробями и обработкой ошибок. Код защищает приложение от сбоев при пустом вводе, повторных операторах и делении на ноль. В конце вы запустите приложение на эмуляторе и проверите вычисления.
- Создание проекта на Java
- Включение view binding
- Элементы экрана калькулятора
- Разметка калькулятора в activity_main.xml
- Подключение binding в MainActivity
- Поля и форматирование результата
- Обработка цифр и точки
- Операции, равно и сброс
- Полный MainActivity.java
- Запуск и проверка приложения
- Что умеет готовый калькулятор
Создание проекта на Java
Чтобы понять, как создать калькулятор, начните с проекта на Java с XML-разметкой. Экран будет храниться в файле activity_main.xml, а логика кнопок – в MainActivity.java.
- Откройте Android Studio и запустите создание нового проекта.
- Выберите шаблон, который создает экран на основе Views и XML-разметки. Если мастер предлагает выбрать язык, укажите «Java».
- Назовите Activity
MainActivity. Имя файла разметки оставьтеactivity_main.xml. - Завершите создание проекта и дождитесь окончания синхронизации Gradle.
После создания откройте дерево проекта. Найдите MainActivity.java в пакете приложения и файл activity_main.xml в каталоге ресурсов layout. Сохраните значение namespace из Gradle-файла: оно понадобится для импорта класса binding.
Если мастер создал Compose-разметку, пересоздайте проект с шаблоном Views для работы с XML.
Включение view binding
View binding создает класс ActivityMainBinding для файла activity_main.xml. Через него Activity получает ссылки на кнопки и поля без вызовов findViewById().
В файле build.gradle.kts модуля приложения добавьте настройку в существующий блок android:
android {
buildFeatures {
viewBinding = true
}
}Для файла build.gradle используйте синтаксис Groovy:
android {
buildFeatures {
viewBinding true
}
}Сохраните файл и выполните синхронизацию Gradle. Остальные параметры блока android, включая namespace, удалять не нужно.
Для view binding не нужна обертка <layout>, как поясняет документация Android.
Элементы экрана калькулятора
Экрану нужны поле текущей операции, поле числа и кнопки ввода. GridLayout раскладывает кнопки по четырем колонкам, поэтому разметка остается компактной.
| Элемент | Зачем нужен в калькуляторе |
|---|---|
| TextView | Показывает выбранный оператор, результат или сообщение об ошибке. |
| EditText | Показывает число, которое пользователь набрал кнопками. |
| Button | Передает цифру, точку, оператор или команду сброса. |
| GridLayout | Выстраивает кнопки в четыре колонки. |
Разметка калькулятора в activity_main.xml
Замените содержимое activity_main.xml полной разметкой. Идентификаторы кнопок совпадают с полями класса ActivityMainBinding.
<?xml version="1.0" encoding="utf-8"?>
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:columnCount="4"
android:padding="16dp"
android:rowCount="7">
<TextView
android:id="@+id/infoTextView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_columnSpan="4"
android:layout_columnWeight="1"
android:gravity="end"
android:textSize="24sp" />
<EditText
android:id="@+id/editText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_columnSpan="4"
android:layout_columnWeight="1"
android:cursorVisible="false"
android:focusable="false"
android:gravity="end"
android:inputType="numberDecimal|numberSigned"
android:textSize="30sp" />
<Button android:id="@+id/buttonSeven" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_columnWeight="1" android:text="7" />
<Button android:id="@+id/buttonEight" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_columnWeight="1" android:text="8" />
<Button android:id="@+id/buttonNine" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_columnWeight="1" android:text="9" />
<Button android:id="@+id/buttonDivide" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_columnWeight="1" android:text="/" />
<Button android:id="@+id/buttonFour" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_columnWeight="1" android:text="4" />
<Button android:id="@+id/buttonFive" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_columnWeight="1" android:text="5" />
<Button android:id="@+id/buttonSix" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_columnWeight="1" android:text="6" />
<Button android:id="@+id/buttonMultiply" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_columnWeight="1" android:text="*" />
<Button android:id="@+id/buttonOne" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_columnWeight="1" android:text="1" />
<Button android:id="@+id/buttonTwo" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_columnWeight="1" android:text="2" />
<Button android:id="@+id/buttonThree" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_columnWeight="1" android:text="3" />
<Button android:id="@+id/buttonSubtract" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_columnWeight="1" android:text="-" />
<Button android:id="@+id/buttonZero" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_columnWeight="1" android:text="0" />
<Button android:id="@+id/buttonDot" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_columnWeight="1" android:text="." />
<Button android:id="@+id/buttonClear" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_columnWeight="1" android:text="C" />
<Button android:id="@+id/buttonAdd" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_columnWeight="1" android:text="+" />
<Button android:id="@+id/buttonEqual" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_columnSpan="4" android:layout_columnWeight="1" android:text="=" />
</GridLayout>Поле ввода не получает фокус, поэтому число вводится кнопками приложения.
После сохранения файла соберите проект. Если поле binding.buttonOne не появляется, проверьте идентификатор в XML и дождитесь завершения сборки.
Подключение binding в MainActivity
Создайте поле binding и вызовите ActivityMainBinding.inflate() в onCreate(). Слушатели кнопок ставьте сразу после setContentView(), когда представления уже созданы.
private ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
setupNumberButtons();
setupOperatorButtons();
setupClearButton();
}Для импорта binding добавьте к namespace модуля окончание .databinding.ActivityMainBinding.
Поля и форматирование результата
Переменная valueOne хранит первое число, valueTwo – второе, а currentAction – выбранный оператор. Значение Double.NaN показывает, что первое число еще не выбрано.
private double valueOne = Double.NaN;
private double valueTwo;
private char currentAction;
private boolean resultShown;
private final DecimalFormat decimalFormat = new DecimalFormat(
"0.##########",
DecimalFormatSymbols.getInstance(Locale.US));Объявите и инициализируйте decimalFormat как поле класса, доступное обработчикам кнопок.
Locale.US закрепляет точку как разделитель дробной части. Шаблон выводит до 10 знаков после точки, а тип double подходит для учебного примера, но не гарантирует точность денежных расчетов.
Обработка цифр и точки
Метод appendInput() добавляет текст нажатой кнопки и блокирует вторую точку.
private void appendInput(String value) {
if (resultShown) {
binding.editText.setText("");
binding.infoTextView.setText("");
resultShown = false;
}
String current = binding.editText.getText().toString();
if (value.equals(".")) {
if (current.contains(".")) {
return;
}
if (current.isEmpty()) {
current = "0";
}
}
binding.editText.setText(current + value);
}
private void setupNumberButtons() {
View.OnClickListener numberListener = view -> {
Button button = (Button) view;
appendInput(button.getText().toString());
};
binding.buttonZero.setOnClickListener(numberListener);
binding.buttonOne.setOnClickListener(numberListener);
binding.buttonTwo.setOnClickListener(numberListener);
binding.buttonThree.setOnClickListener(numberListener);
binding.buttonFour.setOnClickListener(numberListener);
binding.buttonFive.setOnClickListener(numberListener);
binding.buttonSix.setOnClickListener(numberListener);
binding.buttonSeven.setOnClickListener(numberListener);
binding.buttonEight.setOnClickListener(numberListener);
binding.buttonNine.setOnClickListener(numberListener);
binding.buttonDot.setOnClickListener(numberListener);
}Точка в пустом поле превращается в 0.. После нажатия «=» первая новая цифра очищает старый результат и начинает следующее число.
Операции, равно и сброс
Чтобы разобраться, как написать калькулятор без сбоев, проследите проверку ввода в computeCalculation(). Метод не передает пустую строку в Double.parseDouble(), поэтому повторное нажатие оператора не вызывает ошибку.
Метод setOperator() вычисляет набранную часть выражения и сохраняет следующий оператор.
private boolean computeCalculation() {
String input = binding.editText.getText().toString();
if (input.isEmpty()) {
return !Double.isNaN(valueOne);
}
try {
valueTwo = Double.parseDouble(input);
} catch (NumberFormatException error) {
binding.infoTextView.setText("Введите число");
return false;
}
if (Double.isNaN(valueTwo) || Double.isInfinite(valueTwo)) {
binding.infoTextView.setText("Слишком большое число. Нажмите C");
return false;
}
if (Double.isNaN(valueOne)) {
valueOne = valueTwo;
return true;
}
if (currentAction == DIVISION && valueTwo == 0.0) {
binding.infoTextView.setText("На ноль делить нельзя");
return false;
}
double result = valueOne;
if (currentAction == ADDITION) {
result = valueOne + valueTwo;
} else if (currentAction == SUBTRACTION) {
result = valueOne - valueTwo;
} else if (currentAction == MULTIPLICATION) {
result = valueOne * valueTwo;
} else if (currentAction == DIVISION) {
result = valueOne / valueTwo;
}
if (Double.isNaN(result) || Double.isInfinite(result)) {
binding.infoTextView.setText("Слишком большой результат. Нажмите C");
return false;
}
valueOne = result;
binding.editText.setText("");
return true;
}
private void setOperator(char operator) {
if (!computeCalculation()) {
return;
}
currentAction = operator;
resultShown = false;
binding.infoTextView.setText(decimalFormat.format(valueOne) + operator);
binding.editText.setText("");
}
private void showResult() {
if (!computeCalculation()) {
return;
}
String result = decimalFormat.format(valueOne);
binding.infoTextView.setText(result);
binding.editText.setText(result);
valueOne = Double.NaN;
valueTwo = 0.0;
currentAction = '\0';
resultShown = true;
}Проверка деления на ноль выполняется до операции и предотвращает вывод Infinity.
Последовательные вычисления идут слева направо. Выражение 2 + 3 * 4 даст 20, потому что пример не реализует приоритет умножения.
Полный MainActivity.java
Замените содержимое Activity полным классом. Пакет в первой строке замените на пакет своего проекта, а импорт binding приведите к namespace модуля.
package com.example.simplecalculator;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;
import com.example.simplecalculator.databinding.ActivityMainBinding;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Locale;
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
private double valueOne = Double.NaN;
private double valueTwo;
private char currentAction;
private boolean resultShown;
private final DecimalFormat decimalFormat = new DecimalFormat(
"0.##########",
DecimalFormatSymbols.getInstance(Locale.US));
private static final char ADDITION = '+';
private static final char SUBTRACTION = '-';
private static final char MULTIPLICATION = '*';
private static final char DIVISION = '/';
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
setupNumberButtons();
setupOperatorButtons();
setupClearButton();
}
private void appendInput(String value) {
if (resultShown) {
binding.editText.setText("");
binding.infoTextView.setText("");
resultShown = false;
}
String current = binding.editText.getText().toString();
if (value.equals(".")) {
if (current.contains(".")) {
return;
}
if (current.isEmpty()) {
current = "0";
}
}
binding.editText.setText(current + value);
}
private void setupNumberButtons() {
View.OnClickListener numberListener = view -> {
Button button = (Button) view;
appendInput(button.getText().toString());
};
binding.buttonZero.setOnClickListener(numberListener);
binding.buttonOne.setOnClickListener(numberListener);
binding.buttonTwo.setOnClickListener(numberListener);
binding.buttonThree.setOnClickListener(numberListener);
binding.buttonFour.setOnClickListener(numberListener);
binding.buttonFive.setOnClickListener(numberListener);
binding.buttonSix.setOnClickListener(numberListener);
binding.buttonSeven.setOnClickListener(numberListener);
binding.buttonEight.setOnClickListener(numberListener);
binding.buttonNine.setOnClickListener(numberListener);
binding.buttonDot.setOnClickListener(numberListener);
}
private boolean computeCalculation() {
String input = binding.editText.getText().toString();
if (input.isEmpty()) {
return !Double.isNaN(valueOne);
}
try {
valueTwo = Double.parseDouble(input);
} catch (NumberFormatException error) {
binding.infoTextView.setText("Введите число");
return false;
}
if (Double.isNaN(valueTwo) || Double.isInfinite(valueTwo)) {
binding.infoTextView.setText("Слишком большое число. Нажмите C");
return false;
}
if (Double.isNaN(valueOne)) {
valueOne = valueTwo;
return true;
}
if (currentAction == DIVISION && valueTwo == 0.0) {
binding.infoTextView.setText("На ноль делить нельзя");
return false;
}
double result = valueOne;
if (currentAction == ADDITION) {
result = valueOne + valueTwo;
} else if (currentAction == SUBTRACTION) {
result = valueOne - valueTwo;
} else if (currentAction == MULTIPLICATION) {
result = valueOne * valueTwo;
} else if (currentAction == DIVISION) {
result = valueOne / valueTwo;
}
if (Double.isNaN(result) || Double.isInfinite(result)) {
binding.infoTextView.setText("Слишком большой результат. Нажмите C");
return false;
}
valueOne = result;
binding.editText.setText("");
return true;
}
private void setOperator(char operator) {
if (!computeCalculation()) {
return;
}
currentAction = operator;
resultShown = false;
binding.infoTextView.setText(decimalFormat.format(valueOne) + operator);
binding.editText.setText("");
}
private void showResult() {
if (!computeCalculation()) {
return;
}
String result = decimalFormat.format(valueOne);
binding.infoTextView.setText(result);
binding.editText.setText(result);
valueOne = Double.NaN;
valueTwo = 0.0;
currentAction = '\0';
resultShown = true;
}
private void setupOperatorButtons() {
binding.buttonAdd.setOnClickListener(view -> setOperator(ADDITION));
binding.buttonSubtract.setOnClickListener(view -> setOperator(SUBTRACTION));
binding.buttonMultiply.setOnClickListener(view -> setOperator(MULTIPLICATION));
binding.buttonDivide.setOnClickListener(view -> setOperator(DIVISION));
binding.buttonEqual.setOnClickListener(view -> showResult());
}
private void setupClearButton() {
binding.buttonClear.setOnClickListener(view -> {
valueOne = Double.NaN;
valueTwo = 0.0;
currentAction = '\0';
resultShown = false;
binding.infoTextView.setText("");
binding.editText.setText("");
});
}
}После сборки класс binding должен импортироваться без ошибки. Если Android Studio не находит его, проверьте имя XML-файла, идентификаторы элементов и значение namespace.
Запуск и проверка приложения
Запустите приложение на эмуляторе, чтобы проверить вычисления без физического телефона.
- Откройте менеджер виртуальных устройств Android Studio и создайте виртуальное устройство.
- Выберите профиль телефона и системный образ Android, совместимый с параметром
minSdkприложения. - Запустите созданное виртуальное устройство и дождитесь домашнего экрана.
- Выберите эмулятор в списке устройств Android Studio и нажмите кнопку запуска приложения.
- Дождитесь сборки. На экране эмулятора откроется калькулятор с полем результата и кнопками.
Пошаговое описание создания и запуска виртуальных устройств есть в документации Android Studio.
Для проверки каждого сценария сначала нажимайте «C».
| Нажатия | Ожидаемый результат |
|---|---|
7 + 5 = | 12 |
9 - 4 = | 5 |
6 * 3 = | 18 |
8 / 2 = | 4 |
8 / 0 = | Сообщение «На ноль делить нельзя». |
1 . . 5 | 1.5: вторая точка игнорируется. |
. 5 + . 5 = | 1: начальная точка получает ноль. |
7 + + 5 = | 12, без сбоя. |
+ = на пустом экране | Поля остаются пустыми. |
1 / 2 = + 1 = | Сначала 0.5, затем 1.5. |
2 + 3 = 7 | 7: новая цифра начинает следующее число. |
7 + C | Поля очищены, сохраненная операция сброшена. |
Если приложение не открывается, исправьте ошибки сборки и проверьте XML. Если кнопки не реагируют, убедитесь, что три метода setup вызываются в onCreate() после setContentView().
Что умеет готовый калькулятор
Приложение выполняет сложение, вычитание, умножение и деление. Общий обработчик принимает цифры и точку, а один метод с параметром обслуживает четыре оператора.
Кнопка «C» очищает поля и сбрасывает состояние Activity.
Проверка ввода защищает от NumberFormatException. Отдельная проверка деления на ноль не дает вывести Infinity.
Калькулятор хранит состояние в полях Activity, поэтому пересоздание экрана сбрасывает вычисление. Для следующей версии добавьте сохранение состояния и вынесите арифметику в отдельный класс.
Популярное за неделю
- Как создать сайт самому в 2026: 4 способа + пошаговая инструкция 34 мин чтения
- Лучшие конструкторы для создания сайтов — плюсы, минусы и сравнения 22 мин чтения
- Как преобразовать строку в целое число в Java 2 мин чтения
- Метод tostring java: как получить читаемый вывод объектов 9 мин чтения
- Do while Java и цикл while: как работают и чем отличаются 4 мин чтения
- Как настроить mysql connector java для подключения к MySQL 12 мин чтения
Комментарии