美文网首页
Android开发(32)——计算器

Android开发(32)——计算器

作者: 让时间走12138 | 来源:发表于2021-11-18 16:55 被阅读0次

    本节内容

    1.项目功能介绍

    2.设置背景和全屏显示

    3.添加textView

    4.添加数字和操作按钮

    5.给控件添加事件

    6.显示点击的数字

    7.混合运算逻辑思路

    8.保存输入的数字和运算符

    9.将输入的内容显示到界面

    10.实现撤销功能

    11.实现乘除运算

    12.实现混合运算

    一、项目功能介绍

    1.实现类似手机上的计算器的功能,可以进行简单地加减乘除,还可以撤销不想进行的操作。
    效果展示

    二、设置背景和全屏显示

    1.在strings.xml中修改一下app的名字
     <string name="app_name">计算器</string>
    
    2.去网上随便下载一个计算器的图标图片,将它放在drawable文件下。然后在manifest文件里面设置一个app的图标。
            android:icon="@drawable/icon"
            android:roundIcon="@drawable/icon"
    
    3.最后运行起来的图标就如下图所示:
    app图标
    4.整个计算器分为三个部分,最上面是显示用户操作的流程,中间显示运算的结果,最下面一层是用户进行操作的界面。使用guideline来将这三部分进行分割。
    5.添加两个guideline,点出百分比,然后将它们移动到40%和30%的位置。
    6.为了让界面好看一点,我们可以给最下面的部分设置一个圆角。在drawable里面新建一个资源文件,名字就叫top_round_shape.xml,然后在里面设置圆角和颜色等属性。
    <shape xmlns:android="http://schemas.android.com/apk/res/android">
        <solid android:color="#1F2129"/>
        <corners android:topRightRadius="60dp"
            android:topLeftRadius="60dp"/>
    </shape>
    
    7.在layout文件里面,添加一个view,让它完全匹配最下方的版块(就是宽高都设为0dp,然后左右下三边和手机对齐,上面和guideline对齐)。
    8.给背景添加一个颜色。
    android:background="#1A1C21"
    
    9.在themes.xml里面设置全屏显示,并且不显示标签栏。
    <style name="Theme.Calculate" parent="Theme.MaterialComponents.DayNight.NoActionBar">
    <item name="android:windowFullscreen">true</item>
    
    10.运行出来就是这么个效果,颜色变化不明显,但是可以看出不同。而且顶部的标签栏也没了。
    效果

    三、添加textView

    1.拖动一个textView,让它和上面那部分对齐,也就是完全匹配(左右留出20dp的间距,看起来好看点)。然后设置一下字体的颜色以及大小。并且让它显示在右下角(设置gravity为bottom|right)。最后一个是设置文字间的间距,让它们分开一点。
           <TextView
            android:id="@+id/process_textView"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_marginStart="20dp"
            android:layout_marginEnd="20dp"
            android:gravity="bottom|right"
            android:letterSpacing="0.1"
            android:text="12 * 20"
            android:textColor="@color/white"
            android:textSize="25sp"
            app:layout_constraintBottom_toTopOf="@+id/guideline3"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
    2.拖动一个textView到中间,然后顶满。设置文字的大小和颜色,显示的位置。给textView设置id。
    <TextView
            android:id="@+id/result_textView"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_marginStart="20dp"
            android:layout_marginEnd="20dp"
            android:gravity="right|center"
            android:letterSpacing="0.1"
            android:text="240"
            android:textColor="@color/white"
            android:textSize="30sp"
            app:layout_constraintBottom_toTopOf="@+id/guideline2"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="@+id/guideline3" />
    

    四、添加数字和操作按钮

    1.每一个数字都放在一个小容器里面,我们新建一个资源文件设置它的形状和大小。
    <shape xmlns:android="http://schemas.android.com/apk/res/android">
        <solid android:color="#778899"/>
        <corners android:radius="30dp"/>
        <size android:width="60dp" android:height="60dp"/>
    </shape>
    
    2.添加一个textView到最下面的容器,然后将我们上面设置的属性赋给它。并设置字体颜色和大小。
            android:background="@drawable/round_shape"
            android:gravity="center"
            android:text="AC"
            android:textColor="@color/black"
            android:textSize="20sp"
    
    3.然后复制三个一个的控件,在水平上均匀拉伸,然后顶部对齐。纵向上也复制几个控件,然后纵向也拉伸。
    4.撤销键是一张图片,所以我们在drawable里面新建一个vector Asset,然后搜索back,选中图片,并选择颜色为白色。然后在layout里面拖动一个imageView进来,选中我们刚刚挑中的图片。拉伸类型为centerinside,这样会美观一点。
            app:srcCompat="@drawable/back"
            android:scaleType="centerInside"
    
    5.因为底下的控件有三种不同的类型,所以我又添加了一个资源文件。颜色分别为灰色和橙色。
    <shape xmlns:android="http://schemas.android.com/apk/res/android">
        <solid android:color="#696969"/>
        <corners android:radius="30dp"/>
        <size android:width="60dp" android:height="60dp"/>
    </shape>
    
    <shape xmlns:android="http://schemas.android.com/apk/res/android">
        <solid android:color="#FF8C00"/>
        <corners android:radius="30dp"/>
        <size android:width="60dp" android:height="60dp"/>
    </shape>
    
    6.后面就是自己布局了,这一步要细心。反正就是找参考点对齐就行了。布局完成之后的效果如下所示:
    布局效果

    五、给控件添加事件

    1.我们要处理五种事件。(1)数字键被点击。(2)清空键被点击。(3)撤销键被点击。(4)运算符被点击。(5)=号被点击。(没有实现正负和取余的功能)
    2.在MainActivity里面添加这些点击事件。
     //数字键
        fun numberButtClick(view:View){
    
        }
        //运算符
        fun operatorButtClick(view: View){
    
        }
        //请空键
        fun clearButtClick(view: View){
    
        }
        //撤销键
        fun backButtClick(view: View){
    
        }
        //=键
        fun equalButtClick(view: View){
    
        }
    
    3.然后回到layout界面,给每个view都设置一个响应事件。比如数字就是以下这个,不同的控件选择自己对应的响应方法。
    android:onClick="numberButtClick"
    android:onClick="operatorButtClick"
    android:onClick="clearButtClick"
    android:onClick="backButtClick"
    android:onClick="numberButtClick"
    

    六、显示点击的数字

    1.首先将默认显示修改为空,然后结果显示为0。
    2.显示的是数字,所以我们要将获取到的view强制转换为textview。
    fun numberButtClick(view:View){
            //将view强制转化为textView
            val tv = view as TextView
            //将选中的标题显示到文本中
            process_textView.text = tv.text.toString()
        }
    
    3.这样可以将文本显示到最上方的View中,但是只能显示一个数字。所以我们需要一个变量来拼接我们输入的内容。
    var currentInputNumSB = StringBuilder()
    
    4.如果第一个数字为0,那么就清空一下。然后再将后面的数字拼接进来。
           //将选中的标题拼接到StringBuilder中
            if(currentInputNumSB.toString()=="0"){
                currentInputNumSB.clear()
            }
            currentInputNumSB.append(tv.text)
            process_textView.text = currentInputNumSB.toString()
    
    5.拼接效果如下:
    拼接结果

    七、混合运算逻辑思路

    1.我们输入的时候,既有数字,又有运算符。所以我们需要两个数组,一个来存放数字,一个来存放运算符。
    2.这样做可以方便我们遍历数组进行运算,而且也很容易实现撤销功能。
    3.逻辑差不多如下图所示:最后将运算结果作为第1个运算数。
    逻辑图
    4.进行运算之前都会将当前运算符的优先级与下一个运算符的优先级进行比较,如果前面的优先级大于或等于后面的,那么就执行前面的运算,否则就执行后面的运算。

    八、保存输入的数字和运算符

    1.先实现一下清空键的功能。
     //请空键
        fun clearButtClick(view: View){
            currentInputNumSB.clear()
            process_textView.text = " "
        }
    
    2.定义两个数组来存放数字和运算符
        private val numsList = mutableListOf<Int>()
        private val operatorsList = mutableListOf<String>()
    
    3.定义一个变量来记录当前的状态是替换还是添加。就是当我们输入一个数时,我们要判断它是直接加入数组,还是替换掉数组里的最后一个数。(比如我们要输入12,输入1的时候,直接将1加入了数组,输入2的时候,就要将12替换掉数组里面的1。)
    private var isNumStart = true
    
    4.将数字添加到数组中。
    fun numberButtClick(view:View){
            //将view强制转化为textView
            val tv = view as TextView
            currentInputNumSB.append(tv.text)
            if (isNumStart){
                //当前输入的数是一个新的数字,添加到数组中
                numsList.add(tv.text.toString().toInt())
                //更改状态,已经不是一个新数字的开始
                isNumStart = false
            }else{
                //用当前的数字去替换列表中最后一个元素
                numsList[numsList.size-1] = currentInputNumSB.toString().toInt()
            }
        }
    
    5.当输入运算符时,将输入的运算符保存到数组中,并且清空当前输入,改变一下数字输入的状态。
    fun operatorButtClick(view: View){
            val tv = view as TextView
            //保存当前运算符
           operatorsList.add(tv.text.toString())
            //改变状态
            isNumStart = true
            currentInputNumSB.clear()
        }
    

    九、将输入的内容显示到界面

    1.无论是输入数字还是运算符,我们都要将输入内容显示出来。所以我们将显示内容单独拎出来,另写一个方法。
    2.循环遍历数字数组,将数字拼接到变量中,同时还要判断运算符数组是否有内容,如果有则进行拼接。
    //拼接当前运算的表达式,显示在界面上
       private fun showUI(){
            val str = StringBuilder()
            for((i,num)in numsList.withIndex()){
                //将当前数字拼接上去
                str.append(num)
                //判断运算符数组中对应位置是否有内容
                if (operatorsList.size>i){
                    //将i对应的运算符拼接到字符串中
                    str.append(" $operatorsList[i] ")
                }
            }
            process_textView.text = str.toString()
        }
    
    3.然后在输入数字键和运算符的方法里面调用该方法。最后运行结果如下图所示:
    显示结果

    十、实现撤销功能

    1.判断是撤销数字还是撤销运算符,然后删除数组中最后一个值,并修改变量isNumStart
    fun backButtClick(view: View){
            //判断应该撤销运算符还是数字
            if(numsList.size>operatorsList.size){
                //撤销数字
                numsList.removeLast()
                isNumStart = true
                currentInputNumSB.clear()
            }else{
                //撤销运算符
                operatorsList.removeLast()
                isNumStart = false
                currentInputNumSB.append(numsList.last())
            }
         showUI()
        }
    
    2.在进行测试的时候,还发现了请空键的bug。当我们点击清空之后,屏幕上的内容是没有了。但是当我们又点击数字的时候,前面的内容又会重新显示出来,所以我们要修改一下这个bug。我们要将两个数组里面的内容也清空一下。
    fun clearButtClick(view: View){
            currentInputNumSB.clear()
            process_textView.text = " "
            numsList.clear()
            operatorsList.clear()
        }
    

    十一、实现乘除运算

    1.写一个方法实现逻辑运算。
    2.实现逻辑运算前,先写一个方法实现真正的运算。
    private fun realCalculate(param1:Float,operator:String,param2:Float):Float{
            var result : Float = 0.0f
            when(operator){
                "+" ->{
                    result = param1 + param2
                }
                "-" ->{
                    result = param1 - param2
                }
                "×" ->{
                    result = param1 * param2
                }
                "÷" ->{
                    result = param1 / param2
                }
            }
            return result
        }
    
    3.当数字数组长度大于0时,我们才需要运算。然后记录第一个数和第二个数,根据运算符来判断它们进行什么运算。如果是乘除那么直接找到第二个数进行运算。如果是加减,就要先判断后面那个运算符是加减还是乘除,如果是加减就直接运算,如果是乘除就先计算后面的数。
     //实现逻辑运算功能
        fun calculate(){
            if(numsList.size>0) {
                //记录运算符数组遍历时的下标
                var i = 0;
                //记录第一个运算数 = 数字数组第一个数
                var param1 = numsList[0].toFloat()
                var param2 = 0f
                if(operatorsList.size>0){
                    while (true){
                        //获取i对应的运算符
                        val operator = operatorsList[i];
                        //是不是乘除
                        if(operator=="×"|| operator=="÷"){
                            //乘除直接运算
                            //找到第二个运算数
                                if(i+1<numsList.size){
                                    param2 = numsList[i+1].toFloat();
                                    param1 = realCalculate(param1,operator,param2)
                                }
                        }else{
                            //如果是加减 得判断下一个运算符是不是乘除
    
                        }
                        i++
                        if(i==operatorsList.size){
                            //遍历结束
                            break
                        }
                    }
                }
    
                //显示对应结果
                result_textView.text = "$param1"
            }
        }
    
    4.实现按钮的点击事件还是不能像上面一样直接在layout里面赋值,我们先给每个View添加一个id。然后让MainActivity继承View.OnClickListener,然后调用我们刚刚写好的方法。
    //清空按钮被点击
            AC.setOnClickListener {
                clearButtClick(it)
            }
    
    //撤销按钮
            back.setOnClickListener { 
                backButtClick(it)
            }
    
    //运算符按钮
            jia.setOnClickListener {
                operatorButtClick(it)
            }
    
    5.对于数字,我们调用有括号的监听方法,然后在onClick方法里面具体实现。以七为例,其他复制粘贴即可。
     override fun onClick(v: View?) {
            numberButtClick(v!!)
        }
     seven.setOnClickListener(this)
    
    6.然后把我们之前在layout里面添加的onClick方法清空。
    7.结果保留两位小数。
     result_textView.text = String.format("%.2f",param1)
    
    8.按清空键的时候,要把结果显示也清空一下。
    result_textView.text = "0 "
    
    9.清空之后想要继续输入的时候,报错直接退出了。这是因为清空的时候,没有修改inNumStart的状态,将其改为true。
    isNumStart = true
    
    10.最后运行结果如下图所示:
    实现乘除之后

    十二、实现混合运算

    1.当运算符不是乘除的时候,先判断是不是最后一个运算符或者后面的运算符不是乘除,如果成立,那么就直接运算。如果后面还有运算符,并且是乘除,那么就要遍历后面的乘除运算符,逐步计算,直到后面没有为止。
    else{
                                //后面有,而且是乘除
                                var j = i+1
                                var mparam1 = numsList[j].toFloat()
                                var mparam2 = 0.0f
                                while(true){
                                    //获取j对应的运算符
                                    if (operatorsList[j]=="×"||operatorsList[j]=="÷"){
                                        mparam2 = numsList[j+1].toFloat()
                                        mparam1 = realCalculate(mparam1,operatorsList[j],mparam2)
                                    }else{
                                        //之前那个运算符后面所有连续的乘除都运算结束
                                        break
                                    }
                                    j++
                                   if(j==operatorsList.size){
                                        break
                                    }
                                }
                                param2 = mparam1
                                param1=realCalculate(param1,operator,param2)
                                i = j-1
                            }
    
    2.最后的运行结果如下图所示:
    混合运算结果
    3.当数据为空时,我们点击撤销按钮,发现报错退出了。所以我们要修改一下这个bug。只有当数组长度大于0时,点击撤销键才能实现撤销功能。
     //撤销数字
                if (numsList.size>0){
                    numsList.removeLast()
                    isNumStart = true
                    currentInputNumSB.clear()
                }
    
    //撤销运算符
                if (operatorsList.size>0){
                    operatorsList.removeLast()
                    isNumStart = false
                    if (numsList.size>0){
                        currentInputNumSB.append(numsList.last())   
                    }
                }
    
    4.当我们点击撤销按钮之后,如果第二个数还在,才能计算,否则就计算不了。
     //直接运算
                                if(i<numsList.size-1){
                                    param2 = numsList[i+1].toFloat()
                                    param1 = realCalculate(param1,operator,param2)
                                }
    
    5.当数字数组中的数据都撤销空了之后,结果显示也要置为0.
     result_textView.text = "0"
    
    6.在判断后面的运算符是否为乘除时,也要防止数组越界。
    if (operatorsList[j]=="×"||operatorsList[j]=="÷"){
                                        if(j<operatorsList.size-1){
                                            mparam2 = numsList[j+1].toFloat()
                                            mparam1 = realCalculate(mparam1,operatorsList[j],mparam2)
                                        }
                                    }
    
    7.进行撤销运算的结果如下图所示:
    混合运算
    撤销结果之后
    8.完整代码我上传到github上了。https://github.com/Pluto3035/Calculate.git

    相关文章

      网友评论

          本文标题:Android开发(32)——计算器

          本文链接:https://www.haomeiwen.com/subject/sgeraltx.html