多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

Android Studio零基础计算器开发:Java+LinearLayout实战

Android Studio零基础计算器开发:Java+LinearLayout实战 简介这是一份面向Android开发初学者的入门级计算器App实战项目专为刚完成Hello World练习或零基础学习者设计聚焦界面布局与按钮点击事件的核心实践。资源完整呈现了加减乘除、小数点输入及清零等基础功能逻辑是掌握Activity生命周期、XML布局编写与Java事件响应机制的理想教学范例。压缩包共409个文件含130个flat资源文件用于UI渲染、122个json配置含Gradle依赖与构建参数、19个xml布局与清单文件、12个png图标资源、4个核心java源码文件及多个gradle构建脚本整体大小13.37MB结构清晰便于逐模块理解。已有3756人学习下载配套源码可直接导入Android Studio运行调试附带完整工程目录与可执行apk帮助新手快速建立安卓开发全流程认知夯实从编码到部署的实操能力。1. Android Studio 实现计算器零基础也能跑通的完整链路从新建项目到真机点击运算你刚在模拟器上跑出 “Hello World”但下一秒就想做点能按、能算、能清屏的东西——别翻文档了这个计算器项目就是为你设计的。它不依赖任何第三方库不用写网络请求、不用配权限、不碰数据库纯靠LinearLayoutButtonTextViewJava四件套把加减乘除、小数点、连续输入、错误容错全塞进一个 Activity 里。我带过 3 届安卓课设学生92% 的人卡在「按钮点了没反应」或「数字拼接错位」而这套代码把setOnClickListener的绑定时机、String拼接逻辑、运算符状态机、双操作数缓存机制全摊开写清楚。它不是玩具 demo而是你第一次真正理解「事件驱动 UI」的起点点击 → 更新显示 → 记录状态 → 触发计算 → 刷新结果。适合完全没写过 XML 布局、没 debug 过findViewById空指针、甚至没改过app:layout_constraintTop_toBottomOf的纯新手也适合想快速验证自己是否掌握Activity 生命周期和View 事件分发基础的转岗开发者。现在打开 Android Studio我们直接从New Project开始。2. 项目结构与核心组件拆解为什么用 LinearLayout 而不是 ConstraintLayout为什么 Java 不用 Kotlin2.1 选型依据轻量、可控、无隐藏陷阱这个计算器刻意回避了 ConstraintLayout 的复杂约束链和 Kotlin 的空安全语法糖原因很实际布局层LinearLayout垂直嵌套能 100% 可视化预测控件位置拖拽 XML 时不会出现「明明写了 top_toBottomOf 却叠在一起」的玄学问题android:layout_weight1控制按钮等宽比 ConstraintLayout 的chainspread_inside更直白逻辑层Java 的if-else if-else链天然匹配计算器的状态流转等待数字 / 输入第一个数 / 输入运算符 / 输入第二个数 / 执行计算Kotlin 的when虽简洁但新手容易忽略break缺失导致的 fall-through 错误调试友好所有变量currentInput,firstOperand,operator,isOperatorPressed都声明为private String或private double不封装成LiveData或StateFlowLog.d 打印时不会被生命周期干扰Gradle 兼容性minSdkVersion 21Android 5.0避开ViewBinding自动生成类名冲突比如activity_main_binding.java在旧版 AS 中常报红确保你用 Android Studio 4.1 任意版本都能编译通过。提示这不是反对新技术而是明确学习路径——先让findViewById(R.id.btn_0).setOnClickListener(...)这行代码稳稳执行再谈binding.btn0.setOnClickListener(...)的便利性。2.2 文件清单与职责划分项目共 5 个关键文件全部位于app/src/main/下文件路径类型核心职责关键行号范围res/layout/activity_main.xmlXML 布局定义屏幕结构顶部 TextView 显示区 4 行 Button 数字/运算符区第 1–87 行java/com/example/calculator/MainActivity.javaJava 主 Activity处理按钮点击、状态管理、运算逻辑、结果显示第 1–156 行res/values/strings.xml字符串资源存储 0, C, , ÷ 等按钮文本支持多语言占位第 5–22 行res/values/styles.xml样式定义统一设置按钮字体大小、背景色、圆角避免每个 Button 写重复属性第 12–35 行app/build.gradle构建配置指定compileSdk 34,targetSdk 34,implementation androidx.appcompat:appcompat:1.6.1第 5–20 行注意没有Fragment、没有ViewModel、没有Room所有状态都在MainActivity的成员变量中维护——这是刻意为之的「单 Activity 单页面」设计降低认知负荷。2.3 activity_main.xml 布局逻辑详解布局采用三层LinearLayout嵌套最外层vertical容纳显示区和按钮区中间层horizontal显示区TextView占满宽度android:layout_height0dpandroid:layout_weight1保证其高度可伸缩底部按钮区4 个LinearLayout每行 4 个按钮每行用android:layout_weight1均分高度每个Button设android:layout_weight1实现等宽。关键细节TextView设置android:textAlignmentviewEndandroid:gravityend|center_vertical确保长数字右对齐且垂直居中所有Button使用stylestyle/CalcButton统一控制textSize24sp、backgroundTint#FF6B35、cornerRadius12dp运算符按钮,-,×,÷额外设置android:layout_marginStart8dp避免与数字按钮粘连。!-- res/layout/activity_main.xml 片段第一行数字按钮 -- LinearLayout android:layout_widthmatch_parent android:layout_height0dp android:layout_weight1 android:orientationhorizontal Button stylestyle/CalcButton android:textstring/btn_7 android:onClickonDigitClick / Button stylestyle/CalcButton android:textstring/btn_8 android:onClickonDigitClick / Button stylestyle/CalcButton android:textstring/btn_9 android:onClickonDigitClick / Button stylestyle/OperatorButton android:textstring/btn_divide android:onClickonOperatorClick / /LinearLayout这段 XML 的android:onClick属性直接绑定 Java 方法名onDigitClick,onOperatorClick省去findViewByIdsetOnClickListener的样板代码——这是 Android 早期推荐写法对新手极其友好且 IDE 能自动检查方法是否存在。3. 核心逻辑实现从按钮点击到结果输出的四步状态机3.1 状态变量定义与初始化MainActivity.java开头声明 5 个核心状态变量它们共同构成计算器的状态机private TextView display; // 显示区域引用 private String currentInput 0; // 当前输入的数字字符串如 123.45 private double firstOperand 0.0; // 第一个操作数如 12.5 private String operator ; // 当前运算符, -, ×, ÷ private boolean isOperatorPressed false; // 标记是否已按下运算符影响后续数字输入逻辑初始化在onCreate()中完成display findViewById(R.id.display);获取 TextViewdisplay.setText(currentInput);首次显示 0其余变量保持默认值0.0,,false无需显式赋值。注意currentInput初始化为0而非避免空字符串导致Double.parseDouble()报NumberFormatExceptionfirstOperand初始化为0.0是为了兼容首次运算如0 5。3.2 数字按钮点击onDigitClick()方法解析所有数字按钮0–9、小数点., 清零C共享同一个android:onClickonDigitClick通过View.getId()区分具体按钮public void onDigitClick(View view) { Button btn (Button) view; String digit btn.getText().toString(); if (digit.equals(C)) { currentInput 0; firstOperand 0.0; operator ; isOperatorPressed false; } else if (digit.equals(.)) { if (!currentInput.contains(.)) { currentInput .; } } else { if (currentInput.equals(0)) { currentInput digit; // 替换初始 0 } else { currentInput digit; // 追加数字 } } display.setText(currentInput); }参数说明与边界处理digit.equals(C)清零逻辑重置所有状态防止C后继续输入产生脏数据!currentInput.contains(.)小数点防重复避免生成12..3currentInput.equals(0)解决 0 开头问题点击 5 后显示 5 而非 05display.setText()放在最后确保每次点击都刷新界面不依赖invalidate()。3.3 运算符按钮点击onOperatorClick()方法解析,-,×,÷按钮绑定onOperatorClick核心是「延迟计算」只记录运算符不立即执行运算除非已有firstOperand和operatorpublic void onOperatorClick(View view) { Button btn (Button) view; String op btn.getText().toString(); if (!operator.isEmpty() !isOperatorPressed) { // 已有运算符且未按新运算符执行上一次运算 calculate(); } firstOperand Double.parseDouble(currentInput); operator op; isOperatorPressed true; currentInput 0; // 重置输入等待第二个数 }关键逻辑说明!operator.isEmpty() !isOperatorPressed当用户连续点击运算符如5 - 3第二次-会触发calculate()计算505再将firstOperand设为5operator设为-Double.parseDouble(currentInput)将当前输入字符串转为数字存入firstOperand为后续计算准备currentInput 0清空显示区让用户输入第二个数避免5后直接输3变成53而非503。3.4 等号按钮点击onEqualsClick()与calculate()方法按钮调用onEqualsClick()内部调用calculate()执行真实运算public void onEqualsClick(View view) { if (!operator.isEmpty()) { calculate(); operator ; // 清空运算符支持连续计算如 538-26 isOperatorPressed false; } } private void calculate() { double secondOperand Double.parseDouble(currentInput); double result 0.0; switch (operator) { case : result firstOperand secondOperand; break; case -: result firstOperand - secondOperand; break; case ×: result firstOperand * secondOperand; break; case ÷: if (secondOperand ! 0) { result firstOperand / secondOperand; } else { display.setText(Error); return; } break; } // 结果处理整数显示不带小数点浮点数保留最多 8 位小数 if (result (long) result) { currentInput String.valueOf((long) result); } else { currentInput String.format(%.8f, result).replaceAll(0*$, ).replaceAll(\\.$, ); } display.setText(currentInput); firstOperand result; // 为连续计算保存结果 }精度与格式化要点if (secondOperand ! 0)除零保护直接显示Error并return不更新currentInputString.format(%.8f, result)强制转为 8 位小数字符串如1.23000000.replaceAll(0*$, )去掉末尾所有01.23000000→1.23.replaceAll(\\.$, )去掉小数点5.0→5firstOperand result将结果存为下一次运算的第一个操作数实现538×216连续计算。4. 避坑指南新手必踩的 5 个坑及血泪解决方案4.1 现象按钮点击无反应Logcat 无报错XML 中android:onClick已设置原因Java 方法签名错误——android:onClick绑定的方法必须严格满足public void methodName(View view)少View view参数或类型不对如Button btn都会静默失败。解决检查MainActivity.java中onDigitClick方法声明确认是public void onDigitClick(View view)而非public void onDigitClick()或public void onDigitClick(Button btn)IDE 会提示「Method does not match signature」务必修复。4.2 现象输入0.5 0.3显示0.7999999999999999而非0.8原因Javadouble浮点数二进制表示精度丢失0.1 0.2 ! 0.3是经典问题。解决不在calculate()中直接用double运算改用BigDecimal需导入import java.math.BigDecimal;BigDecimal bd1 new BigDecimal(String.valueOf(firstOperand)); BigDecimal bd2 new BigDecimal(String.valueOf(secondOperand)); BigDecimal result bd1.add(bd2); // 或 subtract/multiply/divide currentInput result.stripTrailingZeros().toPlainString();注意BigDecimal构造函数必须传String传double会继承精度问题如new BigDecimal(0.1)仍是0.1000000000000000055511151231257827021181583404541015625。4.3 现象连续点击如53结果变成NaN或Infinity原因calculate()中secondOperand未重置第二次时currentInput仍为上次结果但firstOperand已更新导致result result op result逻辑混乱。解决在onEqualsClick()末尾添加重置逻辑public void onEqualsClick(View view) { if (!operator.isEmpty()) { calculate(); operator ; isOperatorPressed false; // 新增重置 currentInput 为结果避免二次 时 secondOperand 错乱 currentInput display.getText().toString(); } }4.4 现象横屏旋转后计算器状态丢失显示变回 0运算符清空原因Android 默认销毁并重建 Activity成员变量currentInput,firstOperand等被重置。解决在onSaveInstanceState()中保存状态在onCreate()中恢复Override protected void onSaveInstanceState(NonNull Bundle outState) { super.onSaveInstanceState(outState); outState.putString(currentInput, currentInput); outState.putDouble(firstOperand, firstOperand); outState.putString(operator, operator); outState.putBoolean(isOperatorPressed, isOperatorPressed); } Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); display findViewById(R.id.display); if (savedInstanceState ! null) { currentInput savedInstanceState.getString(currentInput, 0); firstOperand savedInstanceState.getDouble(firstOperand, 0.0); operator savedInstanceState.getString(operator, ); isOperatorPressed savedInstanceState.getBoolean(isOperatorPressed, false); } else { currentInput 0; } display.setText(currentInput); }提示onCreate()中savedInstanceState为null表示首次启动否则为旋转/内存回收后的恢复。4.5 现象真机运行时报java.lang.ClassNotFoundException: com.example.calculator.MainActivity原因AndroidManifest.xml中activity的android:name写错包名如写成.MainActivity缺少com.example.calculator.或拼写错误。解决打开app/src/main/AndroidManifest.xml确认activity标签内activity android:name.MainActivity !-- 正确.MainActivity 表示 com.example.calculator.MainActivity -- android:exportedtrue intent-filter action android:nameandroid.intent.action.MAIN / category android:nameandroid.intent.category.LAUNCHER / /intent-filter /activity若包名是com.example.myapp则android:name必须为com.example.myapp.MainActivity或.MainActivity.是包名简写。5. 进阶技巧从基础计算器到可扩展架构的三步跃迁5.1 第一步支持负数输入± 按钮原项目未实现正负号切换但只需 3 行代码即可扩展在activity_main.xml中添加±按钮放在0和之间Button stylestyle/CalcButton android:textstring/btn_plus_minus android:onClickonToggleSign /在strings.xml中定义string namebtn_plus_minus±/string在MainActivity.java中添加onToggleSign()方法public void onToggleSign(View view) { if (!currentInput.equals(0) !currentInput.equals(Error)) { double value Double.parseDouble(currentInput); value -value; currentInput String.valueOf(value); // 处理小数点格式化同 calculate() 中逻辑 if (value (long) value) { currentInput String.valueOf((long) value); } else { currentInput String.format(%.8f, value).replaceAll(0*$, ).replaceAll(\\.$, ); } display.setText(currentInput); } }关键点currentInput.equals(0)防止0变成-0!currentInput.equals(Error)避免对错误状态操作。5.2 第二步分离计算逻辑到独立类CalculatorEngine将calculate()方法抽离为独立 Java 类提升可测试性// java/com/example/calculator/CalculatorEngine.java public class CalculatorEngine { public static double calculate(double a, double b, String operator) { switch (operator) { case : return a b; case -: return a - b; case ×: return a * b; case ÷: return b ! 0 ? a / b : Double.NaN; default: return Double.NaN; } } public static String formatResult(double result) { if (Double.isNaN(result)) return Error; if (result (long) result) { return String.valueOf((long) result); } else { return String.format(%.8f, result).replaceAll(0*$, ).replaceAll(\\.$, ); } } }MainActivity.java中调用double result CalculatorEngine.calculate(firstOperand, secondOperand, operator); currentInput CalculatorEngine.formatResult(result);优势CalculatorEngine可单独单元测试JUnit无需启动 Activity修改运算规则如增加%取模只改此文件。5.3 第三步适配深色模式与 Material You 动态颜色Android 12 支持Material You只需两步在res/values/themes.xml中启用Theme.Material3.DayNightstyle nameBase.Theme.Calculator parentTheme.Material3.DayNight item namecolorPrimarycolor/md_theme_light_primary/item item namecolorOnPrimarycolor/md_theme_light_onPrimary/item /style将Button的backgroundTint改为?attr/colorPrimary使用主题色!-- res/values/styles.xml -- style nameCalcButton parentWidget.Material3.Button.UnelevatedButton item nameandroid:textSize24sp/item item namebackgroundTint?attr/colorPrimary/item item nameandroid:textColor?attr/colorOnPrimary/item /style效果系统深色模式开启时按钮自动变为深蓝底白字浅色模式为蓝底黑字无需写values-night专门资源。从那以后我每次带新人做安卓课设都强制他们先删掉ConstraintLayout、关掉ViewBinding、用 Java 写完这个计算器再谈 MVVM 或 Jetpack Compose。因为真正的「入门」不是跑通代码而是亲手把findViewById的空指针、Double.parseDouble的异常、android:onClick的签名错误一个个揪出来——这些坑踩得越早后面越少翻车。希望帮到你。本文还有配套的精品资源点击获取
返回列表