【20212416 2023-2024-2 《移动平台开发与实践》第2次作业】
这里我使用了一个一级LinearLayout(控件垂直方向排列)嵌套了一个EditText(显示屏)一个二级LinearLayout,二级LinearLayout(控件垂直方向排列)下嵌套四个三级LinearLayout(控件水平方向排列)用于组织计算器的按键。方法的回调(Callback)是一种常用的编程模式,用于在某些情况下把一段代码作为参数传递给另一个方法,在特定的时机再被回调执行,以达到更
·
1.实验内容
- 创建一个新的Android项目,并设置基本的项目结构和属性。
- 设计计算器的用户界面,包括布局和控件的添加。
- 实现计算器的基本功能,如数字输入、运算符选择以及计算结果展示。
实际上学习的内容:
- UI界面的布局、各种控件的使用(主要是TextView和Button)
- 事件监听器的使用以及使不使用回调的差别
2.实验过程
创建一个新的Android项目
-
打开Android Studio,选择“创建新项目”。

-
选择“Empty Activity”作为项目模板,点击“Finish”创建项目。

-
填写项目名称、保存位置等信息,选择最小SDK版本。

设计计算器的用户界面
-
打开res文件夹,创建layout文件夹,新建activity_main.xml文件,这是主Activity的布局文件(新版的Android studio中需要自己创建)

-
使用 LinearLayout(线性排列) 或 RelativeLayout(相对位置排列) 等布局管理器来组织控件。计算器的界面较简单,使用线性排列的布局足以。
这里我使用了一个一级LinearLayout(控件垂直方向排列)嵌套了一个EditText(显示屏)一个二级LinearLayout,二级LinearLayout(控件垂直方向排列)下嵌套四个三级LinearLayout(控件水平方向排列)用于组织计算器的按键。
-
添加必要的控件
- 显示屏
<TextView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:text="Hello World!"
android:textAllCaps="false"
android:textSize="30sp"
android:gravity="right|bottom"
android:padding="15dp"
android:id="@+id/mtv_result"
/>
- 按键
<Button
android:textSize="28sp"
android:layout_margin="5dp"
android:padding="5dp"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="+"
android:id="@+id/btn_add"/>
- 设置控件的属性
我的UI界面做得比较简单,只求各个按键排列整齐、舒展,没有对颜色、样式做过多设计,所有button的属性除了text和id外基本一样,EditText在未输入数据前会显示“hello world”字样。
设置完属性之后的效果如图:
实现计算器的基本功能
-
打开MainActivity.kt文件

-
为每个按钮控件设置点击事件监听器
这里我将所有的按钮控件都设置为同一个点击事件监听器,以便在点击时使用一个函数来处理:
fun setClickEvent() {//设置点击事件监听器
mBtnCalculate.setOnClickListener(this)
mBtnAdd.setOnClickListener(this)
mBtnMultiply.setOnClickListener(this)
mBtnSubtract.setOnClickListener(this)
mBtnDivide.setOnClickListener(this)
mBtnOne.setOnClickListener(this)
mBtnTwo.setOnClickListener(this)
mBtnThree.setOnClickListener(this)
mBtnFour.setOnClickListener(this)
mBtnFive.setOnClickListener(this)
mBtnSix.setOnClickListener(this)
mBtnSeven.setOnClickListener(this)
mBtnEight.setOnClickListener(this)
mBtnNine.setOnClickListener(this)
mBtnZero.setOnClickListener(this)
mBtnCln.setOnClickListener(this)
}
override fun onClick(p0: View?) {//点击事件的具体代码
when (p0?.id) {
R.id.btn_zero -> addNum(0)
R.id.btn_one -> addNum(1)
R.id.btn_two -> addNum(2)
R.id.btn_three -> addNum(3)
R.id.btn_four -> addNum(4)
R.id.btn_five -> addNum(5)
R.id.btn_six -> addNum(6)
R.id.btn_seven -> addNum(7)
R.id.btn_eight -> addNum(8)
R.id.btn_nine -> addNum(9)
R.id.btn_calculate -> equal()
R.id.btn_add -> addOperate('+')
R.id.btn_subtract -> addOperate('-')
R.id.btn_multiply -> addOperate('*')
R.id.btn_divide -> addOperate('/')
R.id.btn_clean -> cln()
}
}
- 在监听器的回调方法中实现计算逻辑,根据用户输入的数字和运算符进行计算,并将结果显示在EditText控件中
- 方法的回调(Callback)是一种常用的编程模式,用于在某些情况下把一段代码作为参数传递给另一个方法,在特定的时机再被回调执行,以达到更灵活的控制和扩展。通过方法的回调,我们可以 把具体逻辑和控制权从一个类中移动到调用它的类中, 使得代码更加灵活和可复用,同时也减少了类之间的耦合。
/**
* 点击数字时调用此函数
* */
fun addNum(num: Long) {
if (operateCount == numCount) {
// 当前为1位的数字
numCount++
mixList.add(num.toString())
} else {
// 当前为多位的数字
val numPlus: Long = mixList[mixList.size - 1].toLong() * 10 + num
mixList[mixList.size - 1] = numPlus.toString()
}
// 实时展示界面变化
show(mixList)
}
/**
* 点击运算符时调用此函数
* */
fun addOperate(operate: Char) {
if (numCount == 0) return
if (operateCount < numCount) {
operateCount++
mixList.add(operate.toString())
} else if (operateCount == numCount) {
mixList[mixList.size - 1] = operate.toString()
}
show(mixList)
}
/**
* 点击“=”后调用此函数
* */
fun equal() {
// 当前只输入数字,不需要计算,直接返回
if (operateCount == 0) return
// 当前最后一位为运算符,不合法,对最后一位运算符进行删除
if (operateCount == numCount) {
mixList.removeAt(mixList.size - 1)
}
// 计算表达式的值
calculate()
// 进行界面展示
show(mixList)
}
/**
* 具体的计算函数
* */
fun calculate() {
// 优先进行乘除运算
for (i in mixList.indices) {
// 兜底判断,防止后续的删除操作导致List长度减小,导致下标溢出
if (i > mixList.size - 1) break
if (mixList[i] == "*") {
// 计算“乘”
val tmp: Long = mixList[i - 1].toLong() * mixList[i + 1].toLong()
// 更新值并删除相关操作数
mixList[i - 1] = tmp.toString()
mixList.removeAt(i)
mixList.removeAt(i)
} else if (mixList[i] == "/") {
// 对非法输入“除0”进行判断
if (mixList[i + 1].toString() == "0") break
val tmp: Long = mixList[i - 1].toLong() / mixList[i + 1].toLong()
mixList[i - 1] = tmp.toString()
mixList.removeAt(i)
mixList.removeAt(i)
}
}
// 进行加减运算
for (i in mixList.indices) {
// 兜底判断,防止后续的删除操作导致List长度减小,导致下标溢出
if (i > mixList.size - 1) break
if (mixList[i] == "+") {
val tmp: Long = mixList[i - 1].toLong() + mixList[i + 1].toLong()
mixList[i - 1] = tmp.toString()
mixList.removeAt(i)
mixList.removeAt(i)
} else if (mixList[i] == "-") {
val tmp: Long = mixList[i - 1].toLong() - mixList[i + 1].toLong()
mixList[i - 1] = tmp.toString()
mixList.removeAt(i)
mixList.removeAt(i)
}
}
}
- 计算结果演示
计算器计算过程演示
3.学习中遇到的问题及解决
- 问题1:计算器的计算逻辑如何设计
- 问题1解决方案:参考网络上的代码,可以 先将用户输入的表达式存储到一个动态数组mixList中,再对数组进行遍历。 由于数组中最终存储的是完整的表达式,这样可以先处理表达式中所有优先级高的运算(乘除),再处理优先级较低的运算(加减)。不过在我的代码中,一来没有设置括号的按钮(即无法输入括号),二来也没有设计含括号的计算逻辑(太复杂了,印象中学数据结构时需要用到堆栈)
- 问题2:控件绑定
- 问题2解决方案:这个其实不算一个问题,因为在我写kotlin代码之前老师已经提醒过我们如果不对控件进行绑定,运行的时候所有控件就会堆在一起。然后我就尝试了一下不绑定会怎么样,发现并不像我想象中的会堆到一起,只是我点击button时计算器会没有反应。
4.学习感悟与思考
- Android studio中的组件特别多,对于还不太熟悉它的我来说,设计UI界面时敲代码要比直接在可视化界面上操作要来得简单一些。经过设计计算器这一实践,我对UI界面的各种控件以及它们的属性有了更清晰的认识,也对kotlin代码的整体结构和语法有了进一步的掌握。
- 值得注意的是,本次实验使用了事件监听器这一工具,setOnClickListener()方法对onClick()方法的回调让我大开眼界,这大大减小了代码的冗余,体现了kotlin语言简洁的特点和优点。
参考资料
更多推荐


所有评论(0)