简介:面向K12阶段初学者的安卓期末大作业资源,基于Android Studio开发一个简易计算器应用,完整覆盖界面设计、按钮事件监听、计算逻辑封装、运算符优先级处理以及测试调试与发布流程。压缩包共包含48个文件,其中11个XML布局与配置负责界面搭建,12个PNG素材提供按钮与图标资源,3个Java文件实现主活动与计算引擎,另附Gradle构建脚本和项目配置文件,整体仅180KB,结构清晰可直接导入开发环境运行。项目采用栈数据结构处理运算符优先级,支持数字输入、四则运算与结果实时刷新;布局文件使用XML编写,Java代码通过setOnClickListener实现事件响应,并配有模拟器调试和Logcat排错思路。目前已有4331人学习下载,适合作为期末作业参考或安卓入门实践项目。通过完整项目源码、布局文件及构建脚本,学习者既能直接运行体验,也能对照理解事件监听、计算逻辑与界面分离的安卓开发范式,并经历从创建、调试到打包APK的完整流程。
1. 安卓期末大作业:为什么简易计算器是最稳的选择
每到期末,用 Android Studio 交安卓期末大作业的时候,“简易计算器实现”都是搜索量最大的选题之一。它不依赖数据库、不依赖第三方 SDK,核心就两件事:用 XML 把界面画出来,用 Java 把四则运算串起来,正好覆盖课程在 UI 布局、事件监听、数据逻辑三个方向的评分点。这个方案真正有弹性的地方在于:加减乘除就能及格,把横竖屏状态恢复、边界输入校验、历史记录这些加上去就能冲高分。后续内容从新建工程讲到答辩自检,代码可以直接抄,关键参数会解释为什么这样设,容易翻车的边界条件也会单独列出来。
2. 在 Android Studio 搭项目与界面:新建工程的 4 个关键选项和 16 键布局完整代码
写代码之前,先把项目的骨架定下来。这里说的不是配色,而是模板、语言、SDK 版本和布局容器。这四个选项决定了后面几小时是顺利推进还是一直跟环境较劲,很多同学栽在第一步,不是因为不会写计算器,而是因为模板选错或者 Gradle 同步反复失败。
2.1 新建项目的四个关键选择:Empty Activity、Java、API 24 与 Gradle 配置
打开 Android Studio 新建项目时,最稳的模板是 Empty Activity,不是 Basic Activity。Basic Activity 会自带 FloatingActionButton、菜单、资源文件等一堆用不到的东西,期末项目里这些没用,反而容易触发资源重复或者主题不匹配的错误。Empty Activity 只有 MainActivity 和 activity_main.xml,路径最短,踩坑最少。
如果你的 Android Studio 界面还是英文,可以去 Settings 里的 Plugins 搜索 Chinese Language Pack 安装后重启,这是“Android Studio 怎么设置中文”这个问题最常见的解法,不装也不影响写代码。
第二个关键是语言下拉框。我建议选 Java,理由在第 3 章展开,这里只需要知道:绝大多数安卓课程用的就是 Java,照着老师的课件改代码最不容易出问题。第三,Minimum SDK 选 API 24。这个版本对应 Android 7.0,覆盖了现在的绝大多数真机和模拟器,同时不用像 API 21 那样处理大量老机型兼容问题。最后,项目名和包名不要出现中文、空格和数字开头,用 Calculator 和 com.example.calculator 就能顺利走到创建完成。
点 Finish 之后,Android Studio 会开始 Gradle 同步。第一次建项目下载依赖比较慢,网上一搜“Android Studio Gradle 配置详解”能找到一堆排查方向,最常见的是 gradle-wrapper.properties 里的 distributionUrl 下载超时,以及 build.gradle 里的插件版本和本地环境不匹配。我一般建议耐心等一次完整同步,不要反复取消重来,否则本地 Gradle 缓存容易处于半残状态,后面 build 更慢。
2.2 计算器界面选 LinearLayout 还是 GridLayout:期末场景下的选型对比
计算器界面的核心需求是:上方一个结果显示区,下方 16 个按钮排成 4 行 4 列,每个按钮等宽等高。实现这种矩阵布局,新手最常纠结的是 LinearLayout 嵌套和 GridLayout 二选一。
| 对比项 | LinearLayout 嵌套 | GridLayout |
|---|---|---|
| 学习成本 | 低,只用 orientation 和 weight | 中,要理解 columnCount、rowCount、columnWeight |
| 等分布局 | 每行按钮设 0dp 宽度和 weight=1,规则统一 | 需额外处理权重属性,写错位置按钮直接错位 |
| 调试难度 | 层级多但结构直观,哪里不对一眼看出 | 属性报错时经常是整块叠在一起 |
| 答辩讲解 | 每个属性都能解释清楚 | 代码短,但要讲清 span 和 weight 不太容易 |
我一般推荐 LinearLayout 嵌套。很多同学想用 GridLayout 展示“自己会用新布局”,结果在 layout_columnWeight 上翻车,按钮挤成一团,答辩现场越改越乱。至于“Android Studio 自定义组件”这种更高级的做法,比如封装一个 CalculatorButton,要处理自定义属性 attrs 和样式资源,复杂度对期末来说太高,风险大于收益。
2.3 手写 XML 布局:显示区、清除键与 16 个按钮的完整代码
在 activity_main.xml 里用代码视图粘贴下面内容,整体结构是一个垂直方向的 LinearLayout,里面放 1 个 TextView 和 4 个水平方向的 LinearLayout,每个水平行放 4 个 Button。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="12dp"> <!-- 结果显示区 --> <TextView android:id="@+id/tv_display" android:layout_width="match_parent" android:layout_height="80dp" android:layout_marginBottom="8dp" android:background="#EFEFEF" android:gravity="end|center_vertical" android:maxLines="1" android:padding="16dp" android:text="0" android:textSize="36sp" /> <!-- 第一行:7 8 9 ÷ --> <LinearLayout android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:orientation="horizontal"> <Button android:id="@+id/btn_7" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="7" /> <Button android:id="@+id/btn_8" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="8" /> <Button android:id="@+id/btn_9" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="9" /> <Button android:id="@+id/btn_divide" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="÷" /> </LinearLayout> <!-- 第二行:4 5 6 × --> <LinearLayout android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:orientation="horizontal"> <Button android:id="@+id/btn_4" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="4" /> <Button android:id="@+id/btn_5" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="5" /> <Button android:id="@+id/btn_6" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="6" /> <Button android:id="@+id/btn_multiply" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="×" /> </LinearLayout> <!-- 第三行:1 2 3 - --> <LinearLayout android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:orientation="horizontal"> <Button android:id="@+id/btn_1" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="1" /> <Button android:id="@+id/btn_2" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="2" /> <Button android:id="@+id/btn_3" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="3" /> <Button android:id="@+id/btn_minus" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="-" /> </LinearLayout> <!-- 第四行:C 0 = + --> <LinearLayout android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:orientation="horizontal"> <Button android:id="@+id/btn_clear" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="C" /> <Button android:id="@+id/btn_0" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="0" /> <Button android:id="@+id/btn_equal" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="=" /> <Button android:id="@+id/btn_plus" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="+" /> </LinearLayout> </LinearLayout>这段布局的核心是 layout_weight 和 0dp 的配合。横向的每个 Button 宽度设为 0dp,再设 weight=1,四个按钮就会均分父容器宽度;纵向每个按钮高度用 match_parent,所在行的 LinearLayout 高度设 0dp 和 weight=1,四行均分剩余高度。整个键盘区无论屏幕多大,都会自动铺满,不会出现某一行特别高、某一列特别宽的情况。
TextView 里的 gravity 用 end|center_vertical,让数字从右向左显示,模拟真实计算器的输入习惯;textSize 用 36sp 比较醒目,maxLines=1 防止结果太长自动换行把布局顶乱。按钮上的文本直接写在 XML 里,严格来说应该抽到 strings.xml,但期末项目里硬编码不影响功能,省一层跳转反而少一个问题。
这段 XML 在 Android Studio 的 Design 视图里可以实时预览。如果按钮显示不出来,检查每个 Button 的 id 是否重复,以及代码是不是少粘了一个半括号,这类错误在 Build 时会直接报出来,Logcat 里能看到具体行号。
3. 从按键到结果上屏:中缀转后缀求值器与 MainActivity 完整绑定代码
布局搭完,下一步就是让按钮真正“能算”。这一章要解决的问题是:按“7 + 8 × 2 =”时,程序如何知道先算乘法再算加法,最后显示 23 而不是 30。答案是用栈把中缀表达式转换成后缀表达式,再做单次遍历求值。
3.1 Java 还是 Kotlin:期末大作业的选型逻辑
先说语言选型。如果学校课程明确用 Kotlin,那就用 Kotlin;否则我建议 Java。原因很实际:期末大作业的重点是把功能跑通,Java 的语法和老师课件上的示例一致,考试或答辩时被问到“这段代码什么意思”,解释起来成本低。Kotlin 的 null 安全、扩展函数、协程虽然现代,但当你不熟悉它时,每写一行都可能在跟编译器计较。
还有一个更隐蔽的坑:网上搜“Java 高级计算器”,很多方案会用 ScriptEngine 之类的动态求值工具。这个类在标准 Java 里能用,但在 Android 里被移除了,所以这种代码直接拿到 Android Studio 里一定编译不过。这也是我坚持用手写栈求值的根本原因,它不依赖任何第三方库,逻辑完全可控。
3.2 四则运算求值器:用栈实现中缀转后缀
新建一个 Calculator.java,把下面代码完整放进去。这个类不依赖 Activity,可以单独用 JUnit 测试,也可以被 MainActivity 直接调用。
import java.text.DecimalFormat; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; import java.util.HashMap; import java.util.List; import java.util.Map; public class Calculator { public static String evaluate(String expr) { try { List<String> tokens = tokenize(expr); List<String> postfix = infixToPostfix(tokens); double result = calcRpn(postfix); if (Double.isNaN(result) || Double.isInfinite(result)) { return "除数不能为 0"; } return formatResult(result); } catch (Exception e) { return "表达式错误"; } } private static List<String> tokenize(String expr) { List<String> tokens = new ArrayList<>(); StringBuilder num = new StringBuilder(); for (int i = 0; i < expr.length(); i++) { char c = expr.charAt(i); if (Character.isDigit(c) || c == '.') { num.append(c); } else { if (num.length() > 0) { tokens.add(num.toString()); num.setLength(0); } tokens.add(String.valueOf(c)); } } if (num.length() > 0) { tokens.add(num.toString()); } return tokens; } private static List<String> infixToPostfix(List<String> tokens) { List<String> output = new ArrayList<>(); Deque<String> stack = new ArrayDeque<>(); Map<String, Integer> priority = new HashMap<>(); priority.put("+", 1); priority.put("-", 1); priority.put("*", 2); priority.put("/", 2); for (String token : tokens) { if (isNumber(token)) { output.add(token); } else { while (!stack.isEmpty() && priority.containsKey(stack.peek()) && priority.get(stack.peek()) >= priority.get(token)) { output.add(stack.pop()); } stack.push(token); } } while (!stack.isEmpty()) { output.add(stack.pop()); } return output; } private static double calcRpn(List<String> postfix) { Deque<Double> stack = new ArrayDeque<>(); for (String token : postfix) { if (isNumber(token)) { stack.push(Double.parseDouble(token)); } else { double b = stack.pop(); double a = stack.pop(); switch (token) { case "+": stack.push(a + b); break; case "-": stack.push(a - b); break; case "*": stack.push(a * b); break; case "/": stack.push(a / b); break; } } } return stack.pop(); } private static boolean isNumber(String s) { try { Double.parseDouble(s); return true; } catch (NumberFormatException e) { return false; } } private static String formatResult(double value) { if (value == Math.floor(value) && !Double.isInfinite(value)) { return String.valueOf((long) value); } DecimalFormat df = new DecimalFormat("0.######"); return df.format(value); } }这段代码的逻辑分成四步。tokenize 把 “12+3” 拆成字符串列表 “12”、“+”、“3”,小数点和数字会拼在一起,运算符单独成项。infixToPostfix 是核心,乘除优先级设为 2,加减设为 1,遇到新运算符时,只要栈顶运算符优先级大于等于自己,就先弹出去,从而实现先乘除后加减。calcRpn 拿到后缀表达式后,遇到数字就入栈,遇到运算符就弹出两个数计算结果再入栈,最后栈顶就是答案。
evaluate 方法里还做了两层防护:计算结果如果是 Infinity 或 NaN,说明发生了除零,返回中文提示;整个流程任何异常都会被捕获,返回“表达式错误”。formatResult 把整数值转成不带小数点的字符串,比如 4.0 显示成 4,同时用 DecimalFormat 限制小数位数,避免 0.1+0.2 这种浮点误差在屏幕上暴露出很长一串 0.30000000000000004。
3.3 MainActivity 完整绑定代码:数字、运算符、等号和清除
回到 MainActivity.java,先删除模板生成的代码,改成下面内容。布局文件里一共有 16 个按钮,如果用 findViewById 一个个绑定,代码会又长又容易漏。我这里用数组加循环,把相同行为的按钮归成一组,分别绑定数字和运算符。
import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; public class MainActivity extends AppCompatActivity { private TextView tvDisplay; private final StringBuilder expression = new StringBuilder(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tvDisplay = findViewById(R.id.tv_display); // 数字按钮统一绑定 int[] digitIds = { R.id.btn_0, R.id.btn_1, R.id.btn_2, R.id.btn_3, R.id.btn_4, R.id.btn_5, R.id.btn_6, R.id.btn_7, R.id.btn_8, R.id.btn_9 }; for (int id : digitIds) { findViewById(id).setOnClickListener(v -> appendInput(((Button) v).getText().toString())); } // 运算符按钮统一绑定 int[] operatorIds = { R.id.btn_plus, R.id.btn_minus, R.id.btn_multiply, R.id.btn_divide }; for (int id : operatorIds) { findViewById(id).setOnClickListener(v -> appendInput(((Button) v).getText().toString())); } findViewById(R.id.btn_equal).setOnClickListener(v -> calculate()); findViewById(R.id.btn_clear).setOnClickListener(v -> clear()); } private void appendInput(String input) { // 表达式不能以运算符开头 if (expression.length() == 0 && input.matches("[+\\-*/]")) { return; } // 禁止连续输入两个运算符 String last = expression.length() > 0 ? expression.substring(expression.length() - 1) : ""; if (last.matches("[+\\-*/]") && input.matches("[+\\-*/]")) { return; } expression.append(input); tvDisplay.setText(expression.toString()); } private void calculate() { if (expression.length() == 0) { return; } String result = Calculator.evaluate(expression.toString()); if ("表达式错误".equals(result) || "除数不能为 0".equals(result)) { expression.setLength(0); tvDisplay.setText(result); } else { expression.setLength(0); expression.append(result); tvDisplay.setText(result); } } private void clear() { expression.setLength(0); tvDisplay.setText("0"); } }这里有两个输入校验值得注意。第一个校验拦截了“以运算符开头”的输入,比如刚打开应用直接按“+”,不会产生 “+2” 这种非法表达式。第二个校验禁止连续运算符,比如输入 1 之后连按两次“+”,第二次会被忽略,这样就不会出现 “1++2” 导致求值时栈弹出空元素崩溃。
calculate 方法里用了一个技巧:如果结果是正常数字,就把结果重新写回 expression,这样用户可以在结果上继续运算。比如按下 “5 + 3 =” 得到 8,再按 “+” 和 “2” 再按 “=”,实际上算的是 “8+2”,符合真实计算器的操作习惯。如果结果是错误提示,就直接清空表达式,防止用户在“除数不能为 0”后面继续追加数字。
这段代码里的 v -> 写法是 Java 8 Lambda。Android Studio 默认支持,如果你们的课程环境还停在 Java 7,把 lambda 换成匿名内部类效果一样,只是代码会长一点,不影响逻辑。绑定按钮时有一个 Android Studio 快捷键可以派上用场:选中重复代码后按 Ctrl+Alt+M 抽取方法,可以把初始化绑定的代码收拢成一个 setupButtonListeners() 方法,让 onCreate 更短。
4. 计算器避坑指南:答辩前必须自查的 5 个典型问题
下面这几个坑单独拿出来讲,因为它们不是在正常点击路径上能碰到的,而是要在边界操作下才会暴露。我按出现频率从高到低排列,每个都按“现象、原因、解决”展开,如果你能把这五关都过掉,答辩时的稳定性已经超过大多数同学。
4.1 按钮被拉伸变形:layout_weight 没有配合 0dp
现象:同一行里四个按钮宽度不一样,文字被截断,或者换一台分辨率不同的模拟器后布局完全变形。
原因:Button 默认宽度是 wrap_content,文字短的按钮就窄,文字长的按钮就宽。有人为了让按钮看起来整齐,直接写死 layout_width="60dp",这在当前模拟器上没问题,切到高密度屏幕或大字体模式马上翻车。
解决:一行内所有 Button 的 layout_width 都写 0dp,layout_weight 写 1。LinearLayout 会按权重把剩余空间均分给子 View。“0dp + weight=1”是线性布局做等比划分的标准写法,不要改成 wrap_content,也不要写成固定 dp,否则权重分配会被系统忽略。高度方向的等分同理,每行 LinearLayout 设 height="0dp" 和 layout_weight="1",四行按钮自动均分屏幕高度,不会出现上面一行被压扁、下面一行空一大块的情况。
注意:如果一行里有按钮的宽度是 0dp,另一行里是 wrap_content,两侧对齐一定会失败。检查时直接看四个按钮是否全部是“0dp + weight=1”。
4.2 旋转屏幕后计算结果归零:Activity 重建导致数据丢失
现象:横屏算出一个结果,转回竖屏后屏幕变成 0,之前输入的内容全部消失。
原因:旋转屏幕时 Android 会销毁当前 Activity,再重新创建一个。MainActivity 里的 expression 只是内存对象,重建后自然被清空。这不算 bug,而是 Android 生命周期机制,不理解这个机制,花多少时间重写计算逻辑都没用。
解决:把需要保留的字符串存进 onSaveInstanceState 的 Bundle,在 onCreate 里判断 savedInstanceState 非空后恢复。真正需要保存的数据只有两个:当前表达式内容和屏幕上显示的文本。这套代码不长,第 5 章会直接给出。如果用的是 Kotlin,也可以考虑 rememberSaveable,但原理一样,核心是“字符串在重建后要能塞回来”。
4.3 连续点运算符直接闪退:输入校验缺失
现象:按 1、+、+、2、= 之后,App 闪退,Logcat 里出现 NumberFormatException 或者 EmptyStackException。
原因:两个加号都被拼进表达式变成“1++2”。tokenize 时第二个加号前面没有数字,求值阶段栈里没有足够的操作数,pop 时抛出异常。如果不加校验,靠求值器兜底是兜不住的。
解决:在 appendInput 的入口做两层校验。第一层,expression 为空时不允许输入运算符;第二层,最后一个字符已经是运算符时,新的运算符直接忽略。这样输入层面就把非法组合挡掉了,求值器永远拿到的都是数字和运算符交替出现的合法表达式。
4.4 5÷2 显示 2 而不是 2.5:整数除法陷阱
现象:5 ÷ 2 的结果显示 2,9 ÷ 4 显示 2,但预期应该是 2.5 和 2.25。
原因:求值过程用了 int 类型,Java 整数除法会直接丢弃小数部分。有的同学在 tokenize 之后用 Integer.parseInt 把数字转成整数,或者在栈里存 int,都会触发这个坑。
解决:求值器从头到尾使用 double 运算,只在显示阶段做格式化。判断结果是整数时转成 long 去掉小数点,不是整数就保留小数。注意顺序是先 double 运算再格式化,不能在输入时就转 int,否则小数信息在第一步就丢了。手写测试时,5÷2 必须显示 2.5,这是计算器及格的基本线。
4.5 0.1+0.2 显示一长串小数:浮点精度问题
现象:0.1 + 0.2 显示成 0.30000000000000004,看起来像程序算错了。
原因:double 用二进制表示十进制小数,0.1 和 0.2 都无法被二进制精确表示,相加之后误差被放大。这个问题在 Java、Kotlin、C、JavaScript 里都存在,不是代码听写错误。
解决:在 formatResult 里用 DecimalFormat("0.######") 限制最多 6 位小数,既有精度又不会暴露二进制尾巴。如果用 BigDecimal,注意要用 BigDecimal.valueOf 而不是 new BigDecimal(double),前者会按十进制字符串精确转换,后者会把二进制近似值整个带进来,得到一长串更离谱的数字。
这五个问题处理完之后,我还会做一轮乱按测试:切换屏幕、连续运算符、除零、表达式为空时按等号,把所有能想到的反常操作都过一遍。因为这些边界行为不会在正常开发时出现,但它们恰恰是答辩老师最爱动手试的地方。
5. 答辩前的最后一道工序:横竖屏状态保存与演示自检
到这一步,计算器已经能正常计算,但还差一个容易被老师当场抓住的细节:旋转屏幕后状态丢失。这一章补上状态保存,再给一套可以照着做的自检动作。
5.1 onSaveInstanceState 保存表达式和显示文本
在 MainActivity 里加一个方法,并在 onCreate 里加一小段恢复逻辑:
@Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putString("expression", expression.toString()); outState.putString("display", tvDisplay.getText().toString()); }@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // ...原有按钮绑定代码... if (savedInstanceState != null) { expression.append(savedInstanceState.getString("expression", "")); tvDisplay.setText(savedInstanceState.getString("display", "0")); } }这段代码只保存两个字符串,不涉及任何复杂对象。旋转屏幕时,系统先把当前表达式和显示内容放进 Bundle,Activity 重建之后再读回来填进控件。points: 1)onSaveInstanceState 只在异常销毁时被调用,正常按返回键不会触发;2)恢复逻辑要放在 setContentView 和 tvDisplay 的 findViewById 之后,否则控件还没创建就 setText 会空指针。如果是内存不足导致 Activity 被系统回收,返回时也会走同一套恢复路径,所以这招不是只防旋转,而是通用的状态恢复方案。
5.2 三个破坏性自检动作
我现在的习惯是,交付前把手机转两圈,然后按下面顺序测试:第一,算出一个结果后旋转屏幕,确认结果还在;第二,连续输入 1 ++ 2 =,确认不会闪退;第三,按下 5 ÷ 0 =,确认屏幕上出现“除数不能为 0”而不是乱码。这三个动作都是破坏性操作,正常使用不会碰到,但答辩老师为了验证代码质量,经常专门挑这种边缘路径按。
如果你还有余力,可以再加一个历史记录功能:用一个 ArrayList 保存每次等号之前的表达式和结果,下方加一个 ListView 展示。这个功能不需要数据库,内存列表就能撑住演示场景,除非老师要求重启 App 后记录还在,才需要用 SharedPreferences 做持久化。
选择简易计算器作为安卓期末大作业,最大的好处是东西不大,但五脏俱全。布局考到了权重和适配,逻辑考到了栈和表达式解析,生命周期考到了状态恢复,这三块正好覆盖课程的主要考点。把边界条件处理好,答辩时被追问“为什么这里不会崩溃”,你能讲出校验和异常处理的理由,比报出一个页面更让老师信服。希望帮到你。
本文还有配套的精品资源,点击获取