恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

Android Studio计算器开发实战:从GridLayout布局到表达式求值

  • 首页
  • 资讯中心
  • /
  • Android Studio计算器开发实战:从GridLayout布局到表达式求值

相关资讯

桌面级本地Text2SQL工具:自然语言查数据库开源方案 2026/10/11 21:53:29
小智微直播平台V3.4.9全开源解密版:私有化部署与二次开发实战指南 2026/10/11 21:53:29
人大金仓Windows安装避坑指南:服务启动、字符集与权限三大关键问题 2026/10/11 21:53:29

最新资讯

@actions/core 版本演进全解析:从 1.0 到 3.0 的核心 API 变迁与实战指南
Kubernetes Python Client 源码解读:V2HorizontalPodAutoscalerStatus 状态模型与 autoscaling/v2 HPA 状态解析
PPT Master SVG 图标库完全指南:12,027 个内置图标的选取、同步与嵌入实践
semantic-router sr-bench 结果解读指南:读懂报告指标、Dashboard 与未完成任务恢复
openJiuwen NativeHarness 设计解析:继承 DeepAgent 复用 task_loop 内核的并发安全交互层
log4js-node File Appender 完全指南:文件日志写入、按大小滚动与压缩备份

今日推荐

Debian新手入门:从部署到日常操作的完整指南
MongoDB复制集扩缩容实战:从rs.add到选主事故复盘
条形码目标检测数据集实战:从YOLOv8训练到部署

本周热门

UE动画修改实战:从资产编辑到重定向与蒙太奇驱动
统计随机数生成器攻击下的KLJN安全密钥交换协议Matlab仿真
政务API安全治理:资产测绘、低代码编排与行标对标实践

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

Android Studio计算器开发实战:从GridLayout布局到表达式求值

发布时间:2026/10/11 21:58:29
Android Studio计算器开发实战:从GridLayout布局到表达式求值 简介PDF格式的技术说明文档面向Android开发入门者系统讲解使用Android Studio实现简单计算器功能的完整过程。内容基于具体案例展开从新建项目、设计界面到编写逻辑逐步推进利用GridLayout网格布局搭建6行4列的计算器面板配置显示文本、清除按钮以及数字、小数点、加减乘除和等号按钮在MainActivity中通过findViewById绑定各组件使用getText()和setText()读取与显示数据结合switch语句判断操作符并完成四则运算。文档配有截图和代码片段还特别提醒了布局文件抬头不可随意修改等易错点对理解Android事件监听与界面交互很有帮助。整个案例流程清晰无需额外依赖库适合零基础学习者。资源包共1个文件格式为PDF大小约109KB便携易用。该PDF已被3708人浏览学习适合刚开始学习Android开发、需要完成课堂作业或课程设计的人员参考。1. 别把计算器当入门玩具Android Studio 里真正卡住你的是按键状态逻辑很多人拿到“Android Studio 实现简单计算器功能”这个题目时第一反应是拖几个按钮、写个加减乘除半小时交差。真做起来就会发现问题全挤在按键逻辑里按完再按5显示区应该长什么样连按两次运算符到底听谁的按了等号之后再按数字旧结果会不会和新数字拼在一起这些细节才是这个标题真正要练的东西。这篇文章从布局写到表达式求值再写到输入状态机和几处高频踩坑最终交付一个能连续运算、带小数点、具备基本防呆、旋转屏幕不丢状态的 Android 计算器 Demo。适合刚学完 Activity 和布局、想找小项目练手的人也适合准备面试前快速复习 Android 基础的人。2. 界面与布局用 GridLayout 排一个 5 行 4 列的计算器键盘并做好小屏适配2.1 布局选型用 GridLayout 而不是嵌套 LinearLayout计算器界面本质是一张 5 行 4 列的按钮网格。用 LinearLayout 嵌套也能做但每一行都要写一个水平 LinearLayout再给每个按钮分配weight1外层再竖排列代码冗长行间距和权重稍一调整就会乱。GridLayout 可以直接声明columnCount4和rowCount5子按钮通过layout_columnWeight与layout_rowWeight自动平分格子。这是做“键盘类”界面最常规的方案门槛低也方便以后改成单位换算、科学计算器之类的面板。注意 GridLayout 的 weight 与 LinearLayout 的 weight 语义一致要求子 view 的宽高为0dp否则“均分”会退化成“内容固定尺寸后塞剩余空间”不同按键之间大小不一致。我把这些公共属性收敛到一个 style 里避免 16 个按钮写 16 份重复代码。2.2 一份可直接跑的 activity_calculator.xml先在res/values/styles.xml增加一个按钮样式style nameCalcButton item nameandroid:layout_width0dp/item item nameandroid:layout_height0dp/item item nameandroid:layout_columnWeight1/item item nameandroid:layout_rowWeight1/item item nameandroid:layout_gravityfill/item item nameandroid:textSize22sp/item item nameandroid:minHeight48dp/item item nameandroid:insetTop0dp/item item nameandroid:insetBottom0dp/item /stylelayout_columnWeight与layout_rowWeight让每个按钮在各自单元格里横向、纵向均分0dp宽高是权重生效的前提。insetTop/insetBottom是为了去掉系统 Button 默认的上下内边距否则同一格子里按钮实际看起来会偏小。这里没有放按钮背景直接沿用主题默认样式换个深色主题它也能跟着变。然后是主布局res/layout/activity_calculator.xml。整体采用竖向 LinearLayout上边是表达式回显和结果回显下边是 GridLayout 键盘?xml version1.0 encodingutf-8? LinearLayout xmlns:androidhttp://schemas.android.com/apk/res/android xmlns:apphttp://schemas.android.com/apk/res-auto android:layout_widthmatch_parent android:layout_heightmatch_parent android:orientationvertical android:padding8dp TextView android:idid/tv_expression android:layout_widthmatch_parent android:layout_height0dp android:layout_weight1 android:gravitybottom|end android:padding8dp android:singleLinetrue android:ellipsizestart android:textSize20sp / androidx.appcompat.widget.AppCompatTextView android:idid/tv_result android:layout_widthmatch_parent android:layout_height0dp android:layout_weight2 android:gravitycenter_vertical|end android:padding8dp android:singleLinetrue android:text0 android:textSize34sp app:autoSizeTextTypeuniform app:autoSizeMinTextSize16sp app:autoSizeMaxTextSize34sp / GridLayout android:idid/keypad android:layout_widthmatch_parent android:layout_height0dp android:layout_weight6 android:columnCount4 android:rowCount5 Button stylestyle/CalcButton android:idid/btn_clear android:textC / Button stylestyle/CalcButton android:idid/btn_back android:text⌫ / Button stylestyle/CalcButton android:idid/btn_percent android:text% / Button stylestyle/CalcButton android:idid/btn_divide android:text÷ / Button stylestyle/CalcButton android:idid/btn_7 android:text7 / Button stylestyle/CalcButton android:idid/btn_8 android:text8 / Button stylestyle/CalcButton android:idid/btn_9 android:text9 / Button stylestyle/CalcButton android:idid/btn_multiply android:text× / Button stylestyle/CalcButton android:idid/btn_4 android:text4 / Button stylestyle/CalcButton android:idid/btn_5 android:text5 / Button stylestyle/CalcButton android:idid/btn_6 android:text6 / Button stylestyle/CalcButton android:idid/btn_minus android:text- / Button stylestyle/CalcButton android:idid/btn_1 android:text1 / Button stylestyle/CalcButton android:idid/btn_2 android:text2 / Button stylestyle/CalcButton android:idid/btn_3 android:text3 / Button stylestyle/CalcButton android:idid/btn_plus android:text / Button stylestyle/CalcButton android:idid/btn_sign android:text± / Button stylestyle/CalcButton android:idid/btn_0 android:text0 / Button stylestyle/CalcButton android:idid/btn_dot android:text. / Button stylestyle/CalcButton android:idid/btn_equals android:text / /GridLayout /LinearLayout根布局用三层weight1:2:6分配表达式占 1 份结果占 2 份键盘占 6 份。结果区字号偏大遇到很长的数字时用autoSizeTextTypeuniform自动收缩避免溢出屏幕。tv_expression用ellipsizestart保证最新输入的字符永远留在右侧可见范围这是真实计算器回显的常见处理。2.3 用 ViewBinding 绑定按钮绕开 findViewById 样板代码在模块的build.gradle的android块里打开 ViewBindingbuildFeatures { viewBinding true }Activity 里这样初始化class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityCalculatorBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding ActivityCalculatorBinding.inflate(layoutInflater) setContentView(binding.root) setupListeners() } }ActivityCalculatorBinding是由activity_calculator.xml自动生成的类。XML 里每个带 id 的控件都会变成 binding 的一个属性类型在编译期就确定点错方法直接编译不过。相比 findViewById这类项目里最大的收益不是少写几行而是改 XML 的 id 后不会再出现“代码引用了不存在的控件”这种运行时崩溃。横竖屏适配需要注意竖屏时 16 个按钮平分高度没问题横屏时键盘区被压扁按钮文字会显得局促。常见做法是在res/values-land下建一份dimens.xml把 CalcButton 的textSize覆盖为 18sp、minHeight覆盖为 32dp。不推荐用android:screenOrientationportrait锁死方向那等于把适配问题留给用户旋转屏幕保存状态的正确解法在第 5 章会展开。提示如果你发现 GridLayout 里按钮没有按预期均分先检查是否所有按钮都通过 style 或直接在标签上写了layout_width0dp和layout_height0dp。这是 GridLayout 权重最常见的“玄学失效”原因。3. 计算核心用中缀转后缀表达式求值代替“两个数 一个运算符”的 if 堆叠3.1 两种实现路线怎么选计算器逻辑最朴素的写法是维护三个状态第一个操作数、运算符、第二个操作数。按一下先把当前数字存进 firstNum按3就往 secondNum 里追加按时做一次运算。这套逻辑写起来快但很快会撞墙想支持12×3时无法正确处理优先级想支持负数、括号、连续运算时每加一个能力就要多一组 if 分支代码很快就变成一坨只能加不能减的状态机。更稳妥的方案是把所有输入拼成一个表达式字符串按时统一交给表达式引擎求值。引擎采用经典的中缀转后缀Shunting-yard再用后缀表达式计算结果。这个方案只多几十行代码但乘法优先级、连续运算、小数、括号以后想加的话都变成通用能力不用再为单个按键维护大量状态。对一个“简单计算器”来说这个投入非常划算。3.2 运算符优先级与 token 化规则先明确这个引擎只处理四则运算 - × ÷。小数点和数字一起构成一个 token运算符单独构成一个 token。用一张表说明优先级运算符优先级说明-1后计算×÷2先计算为什么中缀转后缀能解决优先级因为转后缀的过程中只有“栈顶运算符优先级不低于当前运算符”时才把栈顶弹出到输出序列。于是12×3变成后缀1 2 3 × 计算时先算2×3再算16天然满足了数学规则UI 层不需要感知优先级。3.3 完整的 ExpressionEvaluator 实现object ExpressionEvaluator { private val operators setOf(, -, ×, ÷) private val precedence mapOf( to 1, - to 1, × to 2, ÷ to 2 ) fun evaluate(expression: String): Double { val tokens tokenize(expression) val postfix shunt(tokens) return evaluatePostfix(postfix) } private fun tokenize(expression: String): ListString { val tokens mutableListOfString() val number StringBuilder() for (ch in expression) { if (ch.isDigit() || ch .) { number.append(ch) } else { if (number.isNotEmpty()) { tokens.add(number.toString()) number.clear() } tokens.add(ch.toString()) } } if (number.isNotEmpty()) { tokens.add(number.toString()) } return tokens } private fun shunt(tokens: ListString): ListString { val output mutableListOfString() val opStack mutableListOfString() for (token in tokens) { if (token in operators) { while (opStack.isNotEmpty() opStack.last() in operators precedence[opStack.last()]!! precedence[token]!! ) { output.add(opStack.removeAt(opStack.lastIndex)) } opStack.add(token) } else { output.add(token) } } while (opStack.isNotEmpty()) { output.add(opStack.removeAt(opStack.lastIndex)) } return output } private fun evaluatePostfix(postfix: ListString): Double { val stack mutableListOfDouble() for (token in postfix) { when (token) { - { val b stack.removeAt(stack.lastIndex) val a stack.removeAt(stack.lastIndex) stack.add(a b) } - - { val b stack.removeAt(stack.lastIndex) val a stack.removeAt(stack.lastIndex) stack.add(a - b) } × - { val b stack.removeAt(stack.lastIndex) val a stack.removeAt(stack.lastIndex) stack.add(a * b) } ÷ - { val b stack.removeAt(stack.lastIndex) if (b 0.0) throw ArithmeticException(除数不能为 0) val a stack.removeAt(stack.lastIndex) stack.add(a / b) } else - stack.add(token.toDouble()) } } if (stack.size ! 1) throw IllegalArgumentException(表达式不完整) return stack.last() } }tokenize的核心思路是数字和小数点连续收集遇到运算符就切一刀。所以123.5×4会被切成[12, , 3.5, ×, 4]多位数和一位小数都不需要额外处理。shunt是转换主逻辑数字直接进输出队列运算符先看栈顶只要栈顶也是运算符且优先级不低于当前运算符就把栈顶弹出最后把当前运算符压栈。这样×和÷总会比和-先进入输出序列也就先被计算。evaluatePostfix每遇到一个运算符就从数字栈弹出右操作数和左操作数注意顺序必须是先弹b再弹a否则减法、除法结果会反。除零直接抛ArithmeticException交给 UI 层统一捕获并提示。3.4 等号按钮如何调用求值引擎private fun onEquals() { var expr binding.tvExpression.text.toString() if (expr.isEmpty()) return while (expr.isNotEmpty() expr.last() in listOf(, -, ×, ÷)) { expr expr.dropLast(1) } val result runCatching { ExpressionEvaluator.evaluate(expr) } if (result.isSuccess) { binding.tvResult.text formatResult(result.getOrThrow()) binding.tvExpression.text expr isResultShown true } else { binding.tvResult.text 错误 isResultShown false } }runCatching在这里充当统一异常出口除法异常、表达式不完整、数字格式错误全部收敛成“错误”两个字不会让 App 崩溃。等号前先裁掉尾部运算符是为了处理用户按了3就直接按的情况此时按3处理比报错更符合日常使用习惯。isResultShown这个标志会在下一章详细讲它负责告诉 UI“当前显示的是历史结果下一次输入数字应该开始新算式而不是继续拼接。”提示表达式引擎是纯 Kotlin 单例不依赖任何 Android 组件。建议把它单独放一个文件并且只在evaluate方法里暴露入口。后续加括号、幂运算都只改这个文件Activity 代码基本不动。4. 交互细节连续运算符、小数点、等号后的输入靠一个状态机管住4.1 所有数字和运算符按键共用同一个入口16 个按钮如果每个都写一个setOnClickListener代码会又臭又容易漏。常见做法是先用一对(按钮, 输入值)建立映射统一挂监听private fun setupListeners() { listOf( binding.btn0 to 0, binding.btn1 to 1, binding.btn2 to 2, binding.btn3 to 3, binding.btn4 to 4, binding.btn5 to 5, binding.btn6 to 6, binding.btn7 to 7, binding.btn8 to 8, binding.btn9 to 9, binding.btnDot to ., binding.btnPlus to , binding.btnMinus to -, binding.btnMultiply to ×, binding.btnDivide to ÷ ).forEach { (btn, input) - btn.setOnClickListener { appendInput(input) } } binding.btnClear.setOnClickListener { clearAll() } binding.btnBack.setOnClickListener { backspace() } binding.btnEquals.setOnClickListener { onEquals() } binding.btnPercent.setOnClickListener { percent() } binding.btnSign.setOnClickListener { toggleSign() } }这种写法的好处是把“哪个键对应哪个输入”和“输入后怎么处理”彻底分离。以后想改成 MaterialButton或者调整键盘布局只需要改映射表不用动appendInput的内部逻辑。4.2 输入合法性一张表说清所有边界行为计算器输入最容易出问题的不是“允许什么”而是“该忽略什么”。我用一张表格约束输入行为之后appendInput就是这张表的翻译当前状态用户按下期望行为实现要点刚按过等号显示结果数字清空表达式开始新算式isResultShown分支刚按过等号显示结果运算符用结果继续参与运算表达式 结果 运算符表达式为空运算符忽略仅允许-开头空串时直接 return表达式尾部是运算符运算符替换尾部运算符dropLast(1)后拼接当前数字已有小数点.忽略本次点击检查最后一段数字表达式为空或尾部是运算符.补0.不能只出现.拼0.appendInput的具体实现如下private fun appendInput(token: String) { val current binding.tvExpression.text.toString() val last current.lastOrNull()?.toString() ?: when { token in listOf(, -, ×, ÷) - { when { current.isEmpty() token - - { binding.tvExpression.text - } current.isEmpty() - return last in listOf(, -, ×, ÷) - { binding.tvExpression.text current.dropLast(1) token } isResultShown - { binding.tvExpression.text binding.tvResult.text.toString() token isResultShown false } else - { binding.tvExpression.text current token } } } token . - { if (isResultShown) { binding.tvExpression.text 0. isResultShown false return } val currentNumber lastNumber() if (currentNumber.contains(.)) return binding.tvExpression.text if (currentNumber.isEmpty()) ${current}0. else $current. } else - { // 数字 0-9 if (isResultShown) { binding.tvExpression.text token isResultShown false } else { binding.tvExpression.text current token } } } } private fun lastNumber(): String { val current binding.tvExpression.text.toString() var index current.length - 1 while (index 0) { if (current[index] || current[index] - || current[index] × || current[index] ÷ ) { break } index-- } return current.substring(index 1) }几个关键点说一下。运算符分支里current.isEmpty() token -是为了让用户能输入负号开头的算式比如-53其他运算符开头一律忽略。last in operators时用dropLast(1) token做“替换而不是追加”所以用户连按、-多次只会保留最后一次操作不会生成3-5这种非法表达式。小数点分支里用lastNumber()获取当前正在输入的数字段判断其中是否已包含.避免3.5.6这种情况。如果表达式尾部是运算符比如3那么lastNumber()返回空串此时补0.保证后续按数字能拼出30.5这个合法表达式。4.3 正负号和百分号的实现用“整体取反”代替“贪心定位”正负号和百分号是简单计算器里两个容易想复杂的功能。我采用的方案是对“当前完整表达式”求值再对结果做变换private fun toggleSign() { val value currentResultOrNull() ?: return binding.tvExpression.text formatResult(-value) } private fun percent() { val value currentResultOrNull() ?: return binding.tvExpression.text formatResult(value / 100.0) } private fun currentResultOrNull(): Double? { val expr binding.tvExpression.text.toString() if (expr.isEmpty()) return null return runCatching { ExpressionEvaluator.evaluate(expr) }.getOrNull() }这个方案的取舍非常明确用户如果输入35然后按±得到的是-(35)-8而不是把单个5变成-5。在真实计算器上前者更常见于“整体结果取反”后者则需要额外的状态记录或光标定位。对“简单计算器”这个定位整体取反逻辑最简单、行为可预期把一个容易翻车的小功能控制在 5 行代码以内。如果你之后想做成科学计算器那种“只对当前输入数取反”就必须引入“当前输入缓冲”这个独立状态那就不是这个项目该背的复杂度了。5. 避坑记录计算器项目最常见的 5 个现场现象、原因、解决办法5.1 数字按了没反应过一会儿却突然出现现象点了5表达式 TextView 没变又点了5一下子全出来了。原因按键监听里改了内存中的表达式字符串但刷新 UI 的代码只写在了部分分支里或者把“改数据”和“刷 UI”拆散在多个回调中中间某个分支漏刷了。这是计算器项目最常见的低级翻车。解决所有输入统一走appendInput所有展示统一通过binding.tvExpression.text ...完成。不要在监听器里既写setText又写text text 。把“改数据”和“刷 UI”收敛进同一个函数后这个问题基本不会再出现。5.2 0.1 0.2 算出了 0.30000000000000004现象输入0.10.2按等号结果区显示一长串浮点尾差。原因Double 是二进制浮点0.1和0.2无法精确表示相加产生尾差。这不是计算器 App 特有的问题是 IEEE 754 的固有行为。解决展示层做一次格式化保留最多 10 位小数并去掉末尾的 0private fun formatResult(value: Double): String { return if (value value.toLong().toDouble()) { value.toLong().toString() } else { BigDecimal(value.toString()) .setScale(10, RoundingMode.HALF_UP) .stripTrailingZeros() .toPlainString() } }setScale(10, RoundingMode.HALF_UP)把尾差收敛到 10 位小数stripTrailingZeros()去掉末尾无意义的 0toPlainString()保证结果不用科学计数法显示。如果做的是金融类计算器这个方案仍然不够需要全程用 BigDecimal 参与运算但作为普通计算器 DemoDouble 加格式化是性价比最高的做法。5.3 连按多个运算符表达式引擎直接崩现象输入35按等号App 闪退或结果错得离谱。原因表达式引擎只认合法的中缀表达式35里有连续运算符token 切出来是[3,,,,,5]后缀计算时数字栈会在某个处取不到操作数直接抛异常。解决两层防线缺一不可。第一层是输入侧appendInput里遇到“尾部已是运算符”时做替换而不是追加从源头杜绝连续运算符。第二层是求值侧onEquals里先用while循环裁掉尾部所有运算符再用runCatching包住引擎调用。这样即使将来增加新的输入通道表达式引擎也不会被非法输入砸崩。5.4 旋转屏幕后算到一半的表达式全没了现象竖屏输入12一转横屏表达式和结果都回到初始状态。原因Activity 旋转默认会销毁重建内存中tvExpression的字符串没有同步到新的 Activity 实例状态自然丢失。这是新手最容易踩的坑也是最需要早点养成的习惯。解决用onSaveInstanceState保存表达式和结果字符串在onCreate里恢复override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putString(STATE_EXPRESSION, binding.tvExpression.text.toString()) outState.putString(STATE_RESULT, binding.tvResult.text.toString()) }恢复逻辑放在binding初始化之后、setupListeners之前或之后都可以只要保证 binding 不为 nullif (savedInstanceState ! null) { binding.tvExpression.text savedInstanceState.getString(STATE_EXPRESSION, ) binding.tvResult.text savedInstanceState.getString(STATE_RESULT, 0) }不建议通过android:configChangesorientation来逃避重建那会引入大量“配置变了但组件没重绘”的隐性问题。保存状态才是正规军做法。5.5 按完等号再按数字旧结果被拼到新数字后面现象计算123之后按5表达式显示成35而不是从5开始一个新算式。原因isResultShown这个状态没有被处理。等号之后表达式中还留着旧算式直接追加数字拼出来就是125的错觉。解决在数字和.的输入分支里先判断isResultShown为 true 时用新输入替换整个表达式同时把标志位复位。这就是第 4 章appendInput里数字分支的逻辑if (isResultShown) { binding.tvExpression.text token isResultShown false } else { binding.tvExpression.text binding.tvExpression.text.toString() token }这里有一个容易被忽略的细节按完等号后如果直接按运算符应该继续基于结果参与运算所以运算符分支里不能清空表达式而要把tvResult的文本作为新表达式的一部分。数字分支和运算符分支对isResultShown的处理不一样写的时候要分开判断。6. 从“能算”到“好用”表达式回显、按键反馈和几个可扩展方向计算器做到能算加减乘除只是过了及格线。我一般会再补三个小改进让这个 Demo 从“能演示”变成“真的敢拿出手”。第一个是保证表达式回显一直在工作。tv_expression显示完整算式而不是只显示当前数字这一步的价值在调试时最大用户按错键时你能直接看到是输入拼接的问题还是引擎求值的问题不用靠猜。每次写完表达式引擎我都会用手工用例回归一遍12×37、10÷42.5、0.10.20.3、1÷0错误。这几个用例能覆盖优先级、小数、格式化、异常四条核心链路把它们记在注释里以后改引擎时顺手跑一遍能少掉很多头发。第二个是按键反馈。Android 按钮默认有点击音效但很多设备上的反馈很轻。给每个按键加一次触感反馈完成度会明显提升private fun clickWithFeedback(view: View) { view.performHapticFeedback( HapticFeedbackConstants.VIRTUAL_KEY, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING ) }在setupListeners里把每个setOnClickListener的第一行改成clickWithFeedback(it)代码侵入很小。注意FLAG_IGNORE_GLOBAL_SETTING会无视系统“触摸反馈”开关如果你想尊重系统设置去掉这个参数即可。长按连续输入、按键弹起动画这类增强对简单计算器来说收益不高我一般不做。第三个是预览扩展方向。这套架构往后走非常顺想支持括号只需要在tokenize里加入(和)分支在shunt里处理“遇到右括号弹栈到左括号”想加历史记录结果是一个字符串列表用 RecyclerView 就能展示想换深色主题把项目主题切成DayNight按钮颜色会自动跟随。这些都不需要推翻现有代码因为表达式引擎和 UI 层是解耦的。我的习惯是每次写完这类小项目先在真机上把横竖屏、除以零、连续运算符这几条路走一遍再考虑加新功能。这个习惯帮我挡过好几次“Demo 在白板上能跑、演示时翻车”的尴尬。希望帮到你。本文还有配套的精品资源点击获取

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号