1.实验内容

  • 创建一个新的Android项目,并设置基本的项目结构和属性。
  • 设计计算器的用户界面,包括布局和控件的添加。
  • 实现计算器的基本功能,如数字输入、运算符选择以及计算结果展示。

实际上学习的内容:

  • UI界面的布局、各种控件的使用(主要是TextView和Button)
  • 事件监听器的使用以及使不使用回调的差别

2.实验过程

创建一个新的Android项目

  1. 打开Android Studio,选择“创建新项目”。
    请添加图片描述

  2. 选择“Empty Activity”作为项目模板,点击“Finish”创建项目。
    请添加图片描述

  3. 填写项目名称、保存位置等信息,选择最小SDK版本。
    在这里插入图片描述

设计计算器的用户界面

  1. 打开res文件夹,创建layout文件夹,新建activity_main.xml文件,这是主Activity的布局文件(新版的Android studio中需要自己创建)
    在这里插入图片描述

  2. 使用 LinearLayout(线性排列)RelativeLayout(相对位置排列) 等布局管理器来组织控件。计算器的界面较简单,使用线性排列的布局足以。
    这里我使用了一个一级LinearLayout(控件垂直方向排列)嵌套了一个EditText(显示屏)一个二级LinearLayout,二级LinearLayout(控件垂直方向排列)下嵌套四个三级LinearLayout(控件水平方向排列)用于组织计算器的按键。
    在这里插入图片描述

  3. 添加必要的控件

  • 显示屏
<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"/>
  1. 设置控件的属性
    我的UI界面做得比较简单,只求各个按键排列整齐、舒展,没有对颜色、样式做过多设计,所有button的属性除了text和id外基本一样,EditText在未输入数据前会显示“hello world”字样。
    设置完属性之后的效果如图:
    在这里插入图片描述

实现计算器的基本功能

  1. 打开MainActivity.kt文件
    在这里插入图片描述

  2. 为每个按钮控件设置点击事件监听器
    这里我将所有的按钮控件都设置为同一个点击事件监听器,以便在点击时使用一个函数来处理:

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()
        }
    }
  1. 在监听器的回调方法中实现计算逻辑,根据用户输入的数字和运算符进行计算,并将结果显示在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)
            }
        }
    }
  1. 计算结果演示

计算器计算过程演示

3.学习中遇到的问题及解决

  • 问题1:计算器的计算逻辑如何设计
  • 问题1解决方案:参考网络上的代码,可以 先将用户输入的表达式存储到一个动态数组mixList中,再对数组进行遍历。 由于数组中最终存储的是完整的表达式,这样可以先处理表达式中所有优先级高的运算(乘除),再处理优先级较低的运算(加减)。不过在我的代码中,一来没有设置括号的按钮(即无法输入括号),二来也没有设计含括号的计算逻辑(太复杂了,印象中学数据结构时需要用到堆栈)
  • 问题2:控件绑定
  • 问题2解决方案:这个其实不算一个问题,因为在我写kotlin代码之前老师已经提醒过我们如果不对控件进行绑定,运行的时候所有控件就会堆在一起。然后我就尝试了一下不绑定会怎么样,发现并不像我想象中的会堆到一起,只是我点击button时计算器会没有反应。

4.学习感悟与思考

  • Android studio中的组件特别多,对于还不太熟悉它的我来说,设计UI界面时敲代码要比直接在可视化界面上操作要来得简单一些。经过设计计算器这一实践,我对UI界面的各种控件以及它们的属性有了更清晰的认识,也对kotlin代码的整体结构和语法有了进一步的掌握。
  • 值得注意的是,本次实验使用了事件监听器这一工具,setOnClickListener()方法对onClick()方法的回调让我大开眼界,这大大减小了代码的冗余,体现了kotlin语言简洁的特点和优点。

参考资料

Android开发:基于Kotlin编写一个简易计算器
在 Kotlin 中使用 setOnClickListener

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐