Android开发——实现一个计算器
目录
代码讲解
activity_calculator.xmld代码讲解
1. 根布局(LinearLayout)
2. 显示区域(TextView)
3. 按钮区域(GridLayout)
4. 清除和删除按钮
5. 数字和操作符按钮
6. 其他行的按钮
7. 最后一行
CalculatorActivity.java代码讲解
1. 类和变量声明
2. onCreate 方法
3. 数字按钮处理
4. 操作符按钮处理
5. 执行计算
6. 清除所有内容
7. 删除最后一个字符
8. 更新显示
9. 显示错误提示
编辑
完整代码
activity_calculator.xml
CalculatorActivity.java
运行效果
代码讲解
activity_calculator.xmld代码讲解
1. 根布局(LinearLayout)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:id="@+id/main"android:layout_width="match_parent"android:layout_height="match_parent"tools:context="myapp.CalculatorActivity"android:orientation="vertical"android:padding="8dp">
LinearLayout
:- 根布局是一个垂直方向的线性布局 (
android:orientation="vertical"
),所有子控件会从上到下依次排列。
- 根布局是一个垂直方向的线性布局 (
xmlns:android
和xmlns:tools
:- 命名空间声明,支持 Android 的属性和工具属性。
android:layout_width
和android:layout_height
:- 设置布局的宽度和高度为
match_parent
,表示充满整个父容器。
- 设置布局的宽度和高度为
tools:context
:- 指定该布局与哪个 Activity 关联(这里是
CalculatorActivity
)。
- 指定该布局与哪个 Activity 关联(这里是
android:padding="8dp"
:- 设置内边距为 8dp,使内容与屏幕边缘保持一定距离。
2. 显示区域(TextView)
<TextViewandroid:id="@+id/tv_display"android:layout_width="match_parent"android:layout_height="100dp"android:gravity="end|center_vertical"android:textSize="32sp"android:background="#EEEEEE"android:padding="16dp"android:text="0"/>
TextView
:- 显示计算器的输入和结果。
android:id="@+id/tv_display"
:- 给控件一个唯一标识符,方便在代码中引用。
android:layout_width="match_parent"
和android:layout_height="100dp"
:- 宽度充满父容器,高度固定为 100dp。
android:gravity="end|center_vertical"
:- 文本靠右对齐,并在垂直方向居中。
android:textSize="32sp"
:- 设置字体大小为 32sp。
android:background="#EEEEEE"
:- 设置背景颜色为浅灰色。
android:padding="16dp"
:- 设置内边距为 16dp,避免文本紧贴边界。
android:text="0"
:- 初始显示内容为“0”。
3. 按钮区域(GridLayout)
<GridLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:columnCount="4"android:rowCount="5">
GridLayout
:- 用于放置计算器的按钮,按网格形式排列。
android:columnCount="4"
:- 网格有 4 列。
android:rowCount="5"
:- 网格有 5 行。
4. 清除和删除按钮
<Buttonandroid:id="@+id/btn_clear"android:layout_columnSpan="2"android:text="C"android:textSize="24sp"android:backgroundTint="#FF5722"/><Buttonandroid:id="@+id/btn_delete"android:text="DEL"android:textSize="24sp"android:backgroundTint="#FF5722"/>
btn_clear
:- 清除按钮,占据两列(
android:layout_columnSpan="2"
),用于清空输入。 - 背景色为橙红色(
#FF5722
)。
- 清除按钮,占据两列(
btn_delete
:- 删除按钮,用于删除最后一个字符。
- 背景色同样为橙红色。
5. 数字和操作符按钮
<Button android:id="@+id/btn_7" android:text="7"/>
<Button android:id="@+id/btn_8" android:text="8"/>
<Button android:id="@+id/btn_9" android:text="9"/>
<Buttonandroid:id="@+id/btn_multiply"android:text="×"android:textSize="24sp"android:backgroundTint="#2196F3"/>
- 数字按钮:
- 数字按钮(如
btn_7
,btn_8
,btn_9
)用于输入数字。
- 数字按钮(如
- 操作符按钮:
- 操作符按钮(如
btn_multiply
)用于输入运算符(乘法符号×
)。 - 背景色为蓝色(
#2196F3
)。
- 操作符按钮(如
6. 其他行的按钮
<Button android:id="@+id/btn_4" android:text="4"/>
<Button android:id="@+id/btn_5" android:text="5"/>
<Button android:id="@+id/btn_6" android:text="6"/>
<Buttonandroid:id="@+id/btn_subtract"android:text="-"android:textSize="24sp"android:backgroundTint="#2196F3"/>
- 类似于上一行,每行包含三个数字按钮和一个操作符按钮(减号
-
)。
7. 最后一行
<Buttonandroid:id="@+id/btn_0"android:layout_columnSpan="2"android:text="0"/><Buttonandroid:id="@+id/btn_dot"android:text="."/><Buttonandroid:id="@+id/btn_equal"android:text="="android:textSize="24sp"android:backgroundTint="#4CAF50"/>
btn_0
:- 数字 0 按钮,占据两列(
android:layout_columnSpan="2"
)。
- 数字 0 按钮,占据两列(
btn_dot
:- 小数点按钮,用于输入小数。
btn_equal
:- 等号按钮,用于计算结果。
- 背景色为绿色(
#4CAF50
)。
CalculatorActivity.java代码讲解
1. 类和变量声明
public class CalculatorActivity extends AppCompatActivity {private TextView tvDisplay;private StringBuilder currentInput = new StringBuilder();private double operand1 = Double.NaN;private String currentOperator = "";
CalculatorActivity
:- 继承自
AppCompatActivity
,表示这是一个 Activity。
- 继承自
tvDisplay
:- 用于显示用户输入和计算结果的
TextView
。
- 用于显示用户输入和计算结果的
currentInput
:- 使用
StringBuilder
存储当前用户的输入内容。
- 使用
operand1
:- 存储第一个操作数,默认值为
Double.NaN
(表示未初始化)。
- 存储第一个操作数,默认值为
currentOperator
:- 存储当前的操作符(如 "+"、"-" 等),默认为空字符串。
2. onCreate
方法
@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);EdgeToEdge.enable(this);setContentView(R.layout.activity_calculator);ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);return insets;});tvDisplay = findViewById(R.id.tv_display);// 数字按钮统一处理setNumberButton(R.id.btn_0, "0");setNumberButton(R.id.btn_1, "1");setNumberButton(R.id.btn_2, "2");setNumberButton(R.id.btn_3, "3");setNumberButton(R.id.btn_4, "4");setNumberButton(R.id.btn_5, "5");setNumberButton(R.id.btn_6, "6");setNumberButton(R.id.btn_7, "7");setNumberButton(R.id.btn_8, "8");setNumberButton(R.id.btn_9, "9");setNumberButton(R.id.btn_dot, ".");// 操作符按钮setOperatorButton(R.id.btn_add, "+");setOperatorButton(R.id.btn_subtract, "-");setOperatorButton(R.id.btn_multiply, "×");setOperatorButton(R.id.btn_divide, "/");// 功能按钮findViewById(R.id.btn_equal).setOnClickListener(v -> performCalculation());findViewById(R.id.btn_clear).setOnClickListener(v -> clearAll());findViewById(R.id.btn_delete).setOnClickListener(v -> deleteLastChar());
}
EdgeToEdge.enable(this)
:- 启用全面屏模式,使内容可以延伸到屏幕边缘。
setContentView(R.layout.activity_calculator)
:- 设置布局文件为
activity_calculator.xml
。
- 设置布局文件为
ViewCompat.setOnApplyWindowInsetsListener
:- 处理系统栏(如状态栏和导航栏)的内边距,确保内容不会被遮挡。
findViewById(R.id.tv_display)
:- 将布局中的
TextView
控件与代码中的变量绑定。
- 将布局中的
- 数字按钮和操作符按钮:
- 使用
setNumberButton
和setOperatorButton
方法统一设置按钮的点击事件。
- 使用
- 功能按钮:
- 为等号、清除和删除按钮分别设置点击事件。
3. 数字按钮处理
private void setNumberButton(int buttonId, String value) {findViewById(buttonId).setOnClickListener(v -> {if (currentInput.length() < 15) {// 防止重复小数点if (value.equals(".") && currentInput.toString().contains(".")) return;currentInput.append(value);updateDisplay();}});
}
- 限制输入长度:
- 最大允许输入 15 个字符。
- 防止重复小数点:
- 如果当前输入中已经包含小数点,则不允许再输入小数点。
- 更新显示:
- 调用
updateDisplay()
方法更新TextView
的内容。
- 调用
4. 操作符按钮处理
private void setOperatorButton(int buttonId, String operator) {findViewById(buttonId).setOnClickListener(v -> {if (currentInput.length() > 0) {operand1 = Double.parseDouble(currentInput.toString());currentOperator = operator;currentInput.setLength(0);}});
}
- 保存第一个操作数:
- 当用户点击操作符按钮时,将当前输入的内容转换为数字并存储到
operand1
中。
- 当用户点击操作符按钮时,将当前输入的内容转换为数字并存储到
- 保存操作符:
- 将当前操作符保存到
currentOperator
中。
- 将当前操作符保存到
- 清空输入:
- 清空
currentInput
,以便用户输入第二个操作数。
- 清空
5. 执行计算
private void performCalculation() {if (!Double.isNaN(operand1) && currentInput.length() > 0) {double operand2 = Double.parseDouble(currentInput.toString());try {switch (currentOperator) {case "+":operand1 += operand2;break;case "-":operand1 -= operand2;break;case "×":operand1 *= operand2;break;case "/":if (operand2 == 0) throw new ArithmeticException();operand1 /= operand2;break;}currentInput.setLength(0);currentInput.append(operand1 % 1 == 0 ? (int) operand1 : operand1);updateDisplay();} catch (ArithmeticException e) {showError("不能除以零");}currentOperator = "";}
}
- 检查条件:
- 确保
operand1
已初始化且当前有输入内容。
- 确保
- 执行运算:
- 根据
currentOperator
的值执行相应的运算(加、减、乘、除)。
- 根据
- 处理除以零的情况:
- 如果除数为零,抛出异常并显示错误提示。
- 格式化结果:
- 如果结果是整数(如 10.0),则去掉小数部分。
- 更新显示:
- 调用
updateDisplay()
方法更新TextView
的内容。
- 调用
6. 清除所有内容
private void clearAll() {currentInput.setLength(0);operand1 = Double.NaN;currentOperator = "";tvDisplay.setText("0");
}
- 清空输入:
- 重置
currentInput
。
- 重置
- 重置操作数和操作符:
- 将
operand1
重置为Double.NaN
,currentOperator
重置为空字符串。
- 将
- 重置显示:
- 将
TextView
的内容重置为“0”。
- 将
7. 删除最后一个字符
private void deleteLastChar() {if (currentInput.length() > 0) {currentInput.deleteCharAt(currentInput.length() - 1);updateDisplay();}
}
- 删除字符:
- 删除
currentInput
的最后一个字符。
- 删除
- 更新显示:
- 调用
updateDisplay()
方法更新TextView
的内容。
- 调用
8. 更新显示
private void updateDisplay() {String displayText = currentInput.length() > 0 ? currentInput.toString() : "0";tvDisplay.setText(displayText);
}
- 显示内容:
- 如果
currentInput
不为空,则显示其内容;否则显示“0”。
- 如果
9. 显示错误提示
private void showError(String message) {Toast.makeText(this, message, Toast.LENGTH_SHORT).show();clearAll();
}
- 弹出提示:
- 使用
Toast
显示错误信息。
- 使用
- 清空内容:
- 调用
clearAll()
方法重置所有状态。
- 调用
完整代码
activity_calculator.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:id="@+id/main"android:layout_width="match_parent"android:layout_height="match_parent"tools:context="myapp.CalculatorActivity"android:orientation="vertical"android:padding="8dp"><!-- 显示区域 --><TextViewandroid:id="@+id/tv_display"android:layout_width="match_parent"android:layout_height="100dp"android:gravity="end|center_vertical"android:textSize="32sp"android:background="#EEEEEE"android:padding="16dp"android:text="0"/><!-- 按钮区域 --><GridLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:columnCount="4"android:rowCount="5"><!-- 第一行:清除按钮 --><Buttonandroid:id="@+id/btn_clear"android:layout_columnSpan="2"android:text="C"android:textSize="24sp"android:backgroundTint="#FF5722"/><Buttonandroid:id="@+id/btn_delete"android:text="DEL"android:textSize="24sp"android:backgroundTint="#FF5722"/><Buttonandroid:id="@+id/btn_divide"android:text="/"android:textSize="24sp"android:backgroundTint="#2196F3"/><!-- 数字按钮 --><Button android:id="@+id/btn_7" android:text="7"/><Button android:id="@+id/btn_8" android:text="8"/><Button android:id="@+id/btn_9" android:text="9"/><Buttonandroid:id="@+id/btn_multiply"android:text="×"android:textSize="24sp"android:backgroundTint="#2196F3"/><Button android:id="@+id/btn_4" android:text="4"/><Button android:id="@+id/btn_5" android:text="5"/><Button android:id="@+id/btn_6" android:text="6"/><Buttonandroid:id="@+id/btn_subtract"android:text="-"android:textSize="24sp"android:backgroundTint="#2196F3"/><Button android:id="@+id/btn_1" android:text="1"/><Button android:id="@+id/btn_2" android:text="2"/><Button android:id="@+id/btn_3" android:text="3"/><Buttonandroid:id="@+id/btn_add"android:text="+"android:textSize="24sp"android:backgroundTint="#2196F3"/><!-- 最后一行 --><Buttonandroid:id="@+id/btn_0"android:layout_columnSpan="2"android:text="0"/><Buttonandroid:id="@+id/btn_dot"android:text="."/><Buttonandroid:id="@+id/btn_equal"android:text="="android:textSize="24sp"android:backgroundTint="#4CAF50"/></GridLayout>
</LinearLayout>
CalculatorActivity.java
package myapp;import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;import com.example.usthandroidjava.R;public class CalculatorActivity extends AppCompatActivity {private TextView tvDisplay;private StringBuilder currentInput = new StringBuilder();private double operand1 = Double.NaN;private String currentOperator = "";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);EdgeToEdge.enable(this);setContentView(R.layout.activity_calculator);ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);return insets;});tvDisplay = findViewById(R.id.tv_display);// 数字按钮统一处理setNumberButton(R.id.btn_0, "0");setNumberButton(R.id.btn_1, "1");setNumberButton(R.id.btn_2, "2");setNumberButton(R.id.btn_3, "3");setNumberButton(R.id.btn_4, "4");setNumberButton(R.id.btn_5, "5");setNumberButton(R.id.btn_6, "6");setNumberButton(R.id.btn_7, "7");setNumberButton(R.id.btn_8, "8");setNumberButton(R.id.btn_9, "9");setNumberButton(R.id.btn_dot, ".");// 操作符按钮setOperatorButton(R.id.btn_add, "+");setOperatorButton(R.id.btn_subtract, "-");setOperatorButton(R.id.btn_multiply, "×");setOperatorButton(R.id.btn_divide, "/");// 功能按钮findViewById(R.id.btn_equal).setOnClickListener(v -> performCalculation());findViewById(R.id.btn_clear).setOnClickListener(v -> clearAll());findViewById(R.id.btn_delete).setOnClickListener(v -> deleteLastChar());}private void setNumberButton(int buttonId, String value) {findViewById(buttonId).setOnClickListener(v -> {if (currentInput.length() < 15) {// 防止重复小数点if (value.equals(".") && currentInput.toString().contains(".")) return;currentInput.append(value);updateDisplay();}});}private void setOperatorButton(int buttonId, String operator) {findViewById(buttonId).setOnClickListener(v -> {if (currentInput.length() > 0) {operand1 = Double.parseDouble(currentInput.toString());currentOperator = operator;currentInput.setLength(0);}});}private void performCalculation() {if (!Double.isNaN(operand1) && currentInput.length() > 0) {double operand2 = Double.parseDouble(currentInput.toString());try {switch (currentOperator) {case "+":operand1 += operand2;break;case "-":operand1 -= operand2;break;case "×":operand1 *= operand2;break;case "/":if (operand2 == 0) throw new ArithmeticException();operand1 /= operand2;break;}currentInput.setLength(0);currentInput.append(operand1 % 1 == 0 ? (int) operand1 : operand1);updateDisplay();} catch (ArithmeticException e) {showError("不能除以零");}currentOperator = "";}}private void clearAll() {currentInput.setLength(0);operand1 = Double.NaN;currentOperator = "";tvDisplay.setText("0");}private void deleteLastChar() {if (currentInput.length() > 0) {currentInput.deleteCharAt(currentInput.length() - 1);updateDisplay();}}private void updateDisplay() {String displayText = currentInput.length() > 0 ? currentInput.toString() : "0";tvDisplay.setText(displayText);}private void showError(String message) {Toast.makeText(this, message, Toast.LENGTH_SHORT).show();clearAll();}
}