【Android入门】计算器

1. 前言

这是我的第一个安卓app,从界面到算法都是自己写的,期间经历了各种各样的bug,也学到的各种各样的知识,加深了对Java的使用,也了解了如何写一个Android应用。因为我Java也才入门,安卓更不用说(第一行代码第三章都没看完),所以代码写的肯定非常粗糙,望大家谅解。后期我会对其进行改进,也会上传到此博客

2. 准备

1. 编译器:Android Studio

虽说AndroidStudio有很多不如人意的地方(主要还是谷歌是一个404小公司),特别是gradle。但是其作为现在谷歌力推的编译器,还是很值得信赖的。基于Idea,所以界面、插件等方面都不用说,而且从Idea过来简直就是秒适应(毕竟他就相当于一个加强版Idea)。所以我个人推荐AndroidStudio。

2. 所需知识:

  1. 有一个简单的Java基础。
    我也就学到异常和泛型这块,IO、多线程都没学
  2. 对Android开发有一个简单的了解
    我是看第一行代码看到第三章基础空间以及布局就开始写了
  3. 对计算器算法的大致了解
    中缀表达式转后缀表达式,后缀表达式的运算

3. 学会使用百度谷歌等搜索引擎

很多东西书上没讲,你不知道怎么实现,就直接百度或者google,找到别人的博客,然后好好看一遍,就算你看不懂,但是一定要看一遍,对其有一个大致的了解

4. 会使用AndroidStudio

至少懂得如何使用快捷键,懂得编译器界面上各个部分干嘛的

3. 实现效果,截图

1. 实现效果

  1. 能实现表达式的运算(废话,好歹是个计算器)
  2. 当你表达式存在问题时,不让你输入,或者不让你按下等号,并且某些情况会提示你
  3. 能对负数进行计算
  4. 有些简单的自动补全功能
  5. 能抛出异常

2. 截图

这里写图片描述


代码部分

1. 界面

1. 使用百分比布局

我选择的是百分比界面,但是由于百分比布局是新增的布局,所以你得手动添加其相应的依赖

  • 打开 app/build.gradle 文件,在dependencies闭包中添加这样一行代码
dependencies {
    implementation 'com.android.support:percent:28.0.0-alpha3'
}
  • 添加完毕后,编辑面板顶部会出现一行提示,点击Sync now稍等片刻即可
    这里写图片描述

2. button周边的边框

因为Android对button不能通过设置属性来达到显示边框的功能,所以我们只能创建一个图片文件,在图片中设置背景色以及边框的宽度和颜色,然后把button的background属性设置为对应图片

  • 首先在app/src/main/res/drawable文件夹中新建一个Drawable resource file
  • 在弹出的窗口中设置文件名,文件名随意,但是要简单易懂,而且编译器不报错,例如我设置的shape_blue_bg
  • 然后切换到text视图,把代码修改为
一定要把下面代码中的注释删掉
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid
        android:color="#f0ffff"/> //设置背景颜色,也就是你按键想要显示的颜色
    <stroke
        android:width="0.01dp" //设置边框的宽度
        android:color="#ccc0c0c0"/> //设置边框的颜色
</shape>
  • 然后再在你的布局文件(例如app/src/main/res/layout/activity_main.xml)中,对需要显示边框的button中添加设置背景这行代码
一定要把下面代码中的注释删掉
<Button
        android:id="@+id/button0"
        android:background="@drawable/click_white" //设置button的背景
        />

3. 实现button点击变色功能

这里写图片描述

  • 首先在app/src/main/res/drawable文件夹中新建一个Drawable resource file,随意起名,例如我的click_blue
  • 把代码修改为
一定要把下面代码中的注释删掉
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/shape_click_blue_bg" //设置背景,就是上面的有边框的文件,你也可以就设置成颜色,或者其他图片
        android:state_enabled="true"
        android:state_pressed="true"/> //当button被点击时
    <item android:drawable="@drawable/shape_white_bg"//设置背景,就是上面的有边框的文件,你也可以就设置成颜色,或者其他图片
        android:state_enabled="true"
        android:state_pressed="false"/> //当button未被点击时
</selector>
  • 你可以看到他需要两个drawable文件,一个是点击时的显示效果,一个是未点击时的显示效果,而在设置边框时还只创建了一个文件,也就是他不点击时的文件,接下来,我们还得创建一个跟他一样的文件,只是颜色不同而已
  • 创建一个边框背景文件,随意命名
  • 把代码修改为如下
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#ffffff"/>
    <stroke
        android:width="0.01dp"
        android:color="#ccc0c0c0"/>
</shape>
  • 然后再在你的布局文件(例如app/src/main/res/layout/activity_main.xml)中,对需要显示边框的button中添加设置背景这行代码
一定要把下面代码中的注释删掉
<Button
        android:id="@+id/button0"
        android:background="@drawable/click_blue" //设置button的背景,也就是你刚刚创建的那个起选择按压那个背景不按压那个背景的文件
        />

4. 隐藏app默认自带标题栏

  • 在代码中,在onCreate()中,在super.onCreate(savedInstanceState)之后setContentView(R.layout.activity_main)之前添加
// 隐藏标题栏
ActionBar actionBar = getSupportActionBar();
if (actionBar != null){
    actionBar.hide();
}

5. 隐藏系统通知栏

  • 在上面一样的地方,添加
// 隐藏通知栏
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);

6. 修改app名称

  • app/src/java/res/values/strings.xml文件中将第二行(一般默认是第二行,不是第二行的话就在其他地方找,反正编辑器一定会自动创建这行代码的)
<resources>
    <string name="app_name">小柯基的calculator</string> //“小柯基的calculator”就是我的app名称,你可以换成任何自己想要的名字
</resources>

7. 修改app图标

因为篇幅文体我就不再这说了,分享博客地址
https://blog.csdn.net/zhangkaidsy/article/details/74852470

8. 将默认字体修改为非衬线字体

由于默认字体我觉得有点粗了,而网上的使用极细字体的方法又太细了,所以我就想到了修改字体的方法,还是在layout设置布局的xml文件中,找到你要修改字体的组件,添加一行android:typeface=”sans”

<Button
        android:id="@+id/button0"
        android:text="@string/num0"
        android:typeface="sans" //这行代码就是修改
        app:layout_heightPercent="15%"
        app:layout_widthPercent="25%" />

详情见https://blog.csdn.net/l_lhc/article/details/51769245

9. 字体能适应显示框而改变大小

大家在很多app中都看到过一个功能,就是你输入的文字,能随着你输入的文字的数量而快占满显示框的时候,自动缩小字体以适应显示框
**图片**
* 同样是在显示布局的文件中,在显示组件中添加一行代码

 <TextView
        android:id="@+id/text_view"
        app:autoSizeTextType="uniform" //开启字体自适应功能
        app:layout_heightPercent="25%"
        app:layout_widthPercent="100%" />
  • 但是这种方法存在一个问题,就是它自动适应输入框大小,就是说,你的输入框有多大,他显示的字体就有多大,所以我们得对他做个限制。限制最大字体和最小字体以及每次变化的字体的大小
 <TextView
        android:id="@+id/text_view"
        app:autoSizeMaxTextSize="80sp" //最大字体
        app:autoSizeMinTextSize="20sp" //最小字体
        app:autoSizeStepGranularity="4sp" //每次变化的字体大小
        app:autoSizeTextType="uniform" //开启字体自适应功能
        app:layout_heightPercent="25%"
        app:layout_widthPercent="100%" />

10. 关于组件之间配色问题

自己百度(手动doge)

11. 代码

layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.percent.PercentRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/buttonC"
        android:layout_alignParentBottom="true"
        android:layout_alignParentStart="true"
        android:background="@drawable/click_yellow"
        android:gravity="center"
        android:text="@string/C"
        android:textColor="#6f6f6f"
        android:textSize="30sp"
        android:typeface="sans"
        app:layout_heightPercent="15%"
        app:layout_widthPercent="25%" />

    <Button
        android:id="@+id/button0"
        android:layout_width="81dp"
        android:layout_height="81dp"
        android:layout_alignParentBottom="true"
        android:layout_toEndOf="@+id/buttonC"
        android:background="@drawable/click_white"
        android:gravity="center"
        android:text="@string/num0"
        android:typeface="sans"
        android:textSize="30sp"
        android:textColor="#6f6f6f"
        app:layout_heightPercent="15%"
        app:layout_widthPercent="25%" />

    <Button
        android:id="@+id/buttonDot"
        android:layout_alignParentBottom="true"
        android:layout_toEndOf="@id/button0"
        android:background="@drawable/click_yellow"
        android:gravity="center"
        android:text="@string/Dot"
        android:textSize="30sp"
        android:textColor="#6f6f6f"
        android:typeface="sans"
        app:layout_heightPercent="15%"
        app:layout_widthPercent="25%" />

    <Button
        android:id="@+id/buttonEqual"
        android:layout_alignParentBottom="true"
        android:layout_toEndOf="@id/buttonDot"
        android:background="#58a6e3 "
        android:gravity="center"
        android:text="@string/equal"
        android:textColor="#fff"
        android:textSize="40sp"
        android:typeface="sans"
        app:layout_heightPercent="15%"
        app:layout_widthPercent="25%" />

    <Button
        android:id="@+id/button1"
        android:layout_above="@id/buttonC"
        android:background="@drawable/click_white"
        android:gravity="center"
        android:text="@string/num1"
        android:typeface="sans"
        android:textColor="#6f6f6f"
        android:textSize="30sp"
        app:layout_heightPercent="15%"
        app:layout_widthPercent="25%" />

    <Button
        android:id="@+id/button2"
        android:layout_above="@id/button0"
        android:layout_toEndOf="@id/button1"
        android:background="@drawable/click_white"
        android:gravity="center"
        android:text="@string/num2"
        android:textSize="30sp"
        android:textColor="#6f6f6f"
        android:typeface="sans"
        app:layout_heightPercent="15%"
        app:layout_widthPercent="25%" />

    <Button
        android:id="@+id/button3"
        android:layout_above="@id/buttonDot"
        android:layout_toEndOf="@id/button2"
        android:background="@drawable/click_white"
        android:gravity="center"
        android:text="@string/num3"
        android:textSize="30sp"
        android:typeface="sans"
        android:textColor="#6f6f6f"
        app:layout_heightPercent="15%"
        app:layout_widthPercent="25%" />

    <Button
        android:id="@+id/buttonAdd"
        android:layout_above="@id/buttonEqual"
        android:layout_toEndOf="@id/button3"
        android:background="@drawable/click_blue_light"
        android:gravity="center"
        android:text="@string/add"
        android:textColor="#019eff"
        android:textSize="40sp"
        android:typeface="sans"
        app:layout_heightPercent="15%"
        app:layout_widthPercent="25%" />

    <Button
        android:id="@+id/button4"
        android:layout_above="@id/button1"
        android:background="@drawable/click_white"
        android:gravity="center"
        android:text="@string/num4"
        android:textSize="30sp"
        android:typeface="sans"
        android:textColor="#6f6f6f"
        app:layout_heightPercent="15%"
        app:layout_widthPercent="25%" />

    <Button
        android:id="@+id/button5"
        android:layout_above="@id/button2"
        android:layout_toEndOf="@id/button4"
        android:background="@drawable/click_white"
        android:gravity="center"
        android:text="@string/num5"
        android:textSize="30sp"
        android:typeface="sans"
        android:textColor="#6f6f6f"
        app:layout_heightPercent="15%"
        app:layout_widthPercent="25%" />

    <Button
        android:id="@+id/button6"
        android:layout_above="@id/button3"
        android:layout_toEndOf="@id/button5"
        android:background="@drawable/click_white"
        android:gravity="center"
        android:text="@string/num6"
        android:textSize="30sp"
        android:typeface="sans"
        android:textColor="#6f6f6f"
        app:layout_heightPercent="15%"
        app:layout_widthPercent="25%" />

    <Button
        android:id="@+id/buttonDec"
        android:layout_above="@id/buttonAdd"
        android:layout_toEndOf="@id/button6"
        android:background="@drawable/click_blue_light"
        android:gravity="center"
        android:text="@string/dec"
        android:textColor="#019eff"
        android:textSize="40sp"
        android:typeface="sans"
        app:layout_heightPercent="15%"
        app:layout_widthPercent="25%" />

    <Button
        android:id="@+id/button7"
        android:layout_width="wrap_content"
        android:layout_height="81dp"
        android:layout_above="@id/button4"
        android:background="@drawable/click_white"
        android:gravity="center"
        android:text="@string/num7"
        android:textSize="30sp"
        android:typeface="sans"
        android:textColor="#6f6f6f"
        app:layout_heightPercent="15%"
        app:layout_widthPercent="25%" />

    <Button
        android:id="@+id/button8"
        android:layout_width="81dp"
        android:layout_height="81dp"
        android:layout_above="@id/button5"
        android:layout_marginStart="0dp"
        android:layout_toEndOf="@id/button7"
        android:background="@drawable/click_white"
        android:gravity="center"
        android:text="@string/num8"
        android:textSize="30sp"
        android:typeface="sans"
        android:textColor="#6f6f6f"
        app:layout_heightPercent="15%"
        app:layout_widthPercent="25%" />

    <Button
        android:id="@+id/button9"
        android:layout_above="@+id/button6"
        android:layout_toEndOf="@id/button8"
        android:background="@drawable/click_white"
        android:gravity="center"
        android:text="@string/num9"
        android:textSize="30sp"
        android:typeface="sans"
        android:textColor="#6f6f6f"
        app:layout_heightPercent="15%"
        app:layout_widthPercent="25%" />

    <Button
        android:id="@+id/buttonMulti"
        android:layout_above="@id/buttonDec"
        android:layout_toEndOf="@id/button9"
        android:background="@drawable/click_blue_light"
        android:gravity="center"
        android:text="@string/multi"
        android:textColor="#019eff"
        android:textSize="40sp"
        android:typeface="sans"
        app:layout_heightPercent="15%"
        app:layout_widthPercent="25%" />

    <Button
        android:id="@+id/buttonAC"
        android:layout_above="@+id/button7"
        android:layout_alignParentStart="true"
        android:background="@drawable/click_blue_light"
        android:gravity="center"
        android:text="@string/AC"
        android:textSize="30sp"
        android:textColor="#e47c5e"
        app:layout_heightPercent="15%"
        app:layout_widthPercent="25%" />

    <Button
        android:id="@+id/buttonBracketLeft"
        android:layout_above="@id/button8"
        android:layout_toEndOf="@id/buttonAC"
        android:background="@drawable/click_blue_light"
        android:gravity="center"
        android:text="@string/BracketLeft"
        android:textSize="30sp"
        android:typeface="sans"
        android:textColor="#019eff"
        app:layout_heightPercent="15%"
        app:layout_widthPercent="25%" />

    <Button
        android:id="@+id/buttonBracketRight"
        android:layout_above="@id/button9"
        android:layout_toEndOf="@id/buttonBracketLeft"
        android:background="@drawable/click_blue_light"
        android:gravity="center"
        android:text="@string/BracketRight"
        android:textSize="30sp"
        android:textColor="#019eff"
        android:typeface="sans"
        app:layout_heightPercent="15%"
        app:layout_widthPercent="25%" />

    <Button
        android:id="@+id/buttonDiv"
        android:layout_above="@id/buttonMulti"
        android:layout_toEndOf="@id/buttonBracketRight"
        android:background="@drawable/click_blue_light"
        android:gravity="center"
        android:text="@string/div"
        android:textSize="30sp"
        android:textColor="#019eff"
        android:typeface="sans"
        app:layout_heightPercent="15%"
        app:layout_widthPercent="25%" />



    <TextView
        android:id="@+id/text_view"
        android:background="#FAFFF0"
        android:layout_alignParentTop="true"
        android:gravity="end|center_vertical"
        app:autoSizeMaxTextSize="80sp"
        app:autoSizeMinTextSize="20sp"
        app:autoSizeStepGranularity="4sp"
        app:autoSizeTextType="uniform"
        app:layout_heightPercent="25%"
        app:layout_widthPercent="100%" />

</android.support.percent.PercentRelativeLayout>
values/strings.xml
<resources>
    <string name="app_name">小柯基的calculator</string>
    <string name="AC">AC</string>
    <string name="AddDec">+/-</string>
    <string name="equal">=</string>
    <string name="add">+</string>
    <string name="dec">-</string>
    <string name="multi">*</string>
    <string name="div">/</string>
    <string name="Dot">.</string>
    <string name="num0">0</string>
    <string name="num1">1</string>
    <string name="num2">2</string>
    <string name="num3">3</string>
    <string name="num4">4</string>
    <string name="num5">5</string>
    <string name="num6">6</string>
    <string name="num7">7</string>
    <string name="num8">8</string>
    <string name="num9">9</string>
    <string name="BracketLeft">(</string>
    <string name="BracketRight">)</string>
    <string name="Mod">%</string>
    <string name="Delete">删除</string>
    <string name="C">C</string>
</resources>
values/styles.xml
<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Base.Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

    <style name="baseTextStyle">
        <item name="android:layout_width">wrap_content</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:fontFamily">sans-serif-light</item>
    </style>

</resources>
drawable/click_blue.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/shape_click_blue_bg" android:state_enabled="true"
        android:state_pressed="true"/>
    <item android:drawable="@drawable/shape_white_bg" android:state_enabled="true"
        android:state_pressed="false"/>
</selector>
drawable/click_blue_light.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/shape_white_bg" android:state_enabled="true"
        android:state_pressed="true"/>
    <item android:drawable="@drawable/shape_click_blue_bg" android:state_enabled="true"
        android:state_pressed="false"/>\

</selector>
drawable/click_white.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/shape_click_blue_bg" android:state_enabled="true"
        android:state_pressed="true"/>
    <item android:drawable="@drawable/shape_white_bg" android:state_enabled="true"
        android:state_pressed="false"/>\

</selector>
drawable/click_yellow.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/shape_white_bg" android:state_enabled="true"
        android:state_pressed="true"/>
    <item android:drawable="@drawable/shape_yellow_bg" android:state_enabled="true"
        android:state_pressed="false"/>\

</selector>
drawable/shape_click_blue_bg.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid
        android:color="#f0ffff"/>
    <stroke
        android:width="0.01dp"
        android:color="#ccc0c0c0"/>
</shape>
drawable/shape_white_bg.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#ffffff"/>
    <stroke
        android:width="0.01dp"
        android:color="#ccc0c0c0"/>
</shape>
drawable/shape_yellow_bg.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid
        android:color="#c8fffaf0"/>
    <stroke
        android:width="0.01dp"
        android:color="#ccc0c0c0"/>
</shape>

活动的代码

1. 异常处理

  • 当输出的结果有问题时,抛出异常,然后显示Error
  • 当左括号数目等于或者小于右括号时,不然输入右括号,而且按等号不会运算,提示括号不匹配
  • 当前面是符号,然后输入减号默认添加’(‘当负号处理
  • 实现输入是’数字(数字’时,当乘法处理
    ···还有很多功能就不一一列举了

2. 点击时手机震动

  • 首先在AndroidManifest.xml中添加震动权限
<uses-permission android:name="android.permission.VIBRATE" />
  • 新建class文件,命名为Vibrate
    代码如下
package com.example.calculator;

import android.content.Context;
import android.os.Vibrator;

/**
 * Created by littlecorgi_a1203991686 on 2018/08/02 15:56.
 */
public class Vibrate {
    private static Vibrator vibrator;
    /**
     * 简单震动
     * @param context     调用震动的Context
     * @param millisecond 震动的时间,毫秒
     */
    @SuppressWarnings("static-access")
    public static void vSimple(Context context, int millisecond) {
        vibrator = (Vibrator) context.getSystemService(context.VIBRATOR_SERVICE);
        vibrator.vibrate(millisecond);
    }
    /**
     * 停止震动
     */
    public static void stop() {
        if (vibrator != null) {
            vibrator.cancel();
        }
    }
}

3. MainActivity代码

package com.example.calculator;

import android.annotation.SuppressLint;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    private TextView textview;
    private StringBuilder sb = new StringBuilder();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // 隐藏标题栏
        ActionBar actionBar = getSupportActionBar();
        if (actionBar != null){
            actionBar.hide();
        }

        // 隐藏通知栏
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);

        setContentView(R.layout.activity_main);

        // 定义控件
        Button button0 = findViewById(R.id.button0);
        Button button1 = findViewById(R.id.button1);
        Button button2 = findViewById(R.id.button2);
        Button button3 = findViewById(R.id.button3);
        Button button4 = findViewById(R.id.button4);
        Button button5 = findViewById(R.id.button5);
        Button button6 = findViewById(R.id.button6);
        Button button7 = findViewById(R.id.button7);
        Button button8 = findViewById(R.id.button8);
        Button button9 = findViewById(R.id.button9);
        Button buttonC = findViewById(R.id.buttonC);
        Button buttonDot =  findViewById(R.id.buttonDot);
        Button buttonEqual =  findViewById(R.id.buttonEqual);
        Button buttonAdd =  findViewById(R.id.buttonAdd);
        Button buttonDec =  findViewById(R.id.buttonDec);
        Button buttonMulti =  findViewById(R.id.buttonMulti);
        Button buttonAC =  findViewById(R.id.buttonAC);
        Button buttonBracketLeft =  findViewById(R.id.buttonBracketLeft);
        Button buttonBracketRight =  findViewById(R.id.buttonBracketRight);
        Button buttonDiv =  findViewById(R.id.buttonDiv);
        textview = findViewById(R.id.text_view);

        button0.setOnClickListener(this);
        button1.setOnClickListener(this);
        button2.setOnClickListener(this);
        button3.setOnClickListener(this);
        button4.setOnClickListener(this);
        button5.setOnClickListener(this);
        button6.setOnClickListener(this);
        button7.setOnClickListener(this);
        button8.setOnClickListener(this);
        button9.setOnClickListener(this);
        buttonAC.setOnClickListener(this);
        buttonAdd.setOnClickListener(this);
        buttonBracketLeft.setOnClickListener(this);
        buttonBracketRight.setOnClickListener(this);
        buttonC.setOnClickListener(this);
        buttonDec.setOnClickListener(this);
        buttonDiv.setOnClickListener(this);
        buttonDot.setOnClickListener(this);
        buttonEqual.setOnClickListener(this);
        buttonMulti.setOnClickListener(this);
    }

    private int count_negative = 0; //负数标记
    private final int vibrate_time = 5; //震动时长
    private boolean equals = false;
    private int count_bracket_left = 0;
    private int count_bracket_right = 0;



    @SuppressLint("SetTextI18n")
    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.button0:
                if (equals) {
                    sb = sb.delete(0, sb.length());
                    equals = false;
                }
                sb = sb.append("0");
                Vibrate.vSimple(view.getContext(), vibrate_time);
                textview.setText(sb.toString());
                break;
            case R.id.button1:
                if (equals) { //当equals为true,输入数字,清空字符串,在把标志变为false
                    sb = sb.delete(0, sb.length());
                    equals = false;
                }
                if (sb.length() > 0 && sb.charAt(sb.length() - 1) == ')') {
                    sb = sb.append("*1");
                } else {
                    sb = sb.append("1");
                }
                Vibrate.vSimple(view.getContext(), vibrate_time);
                textview.setText(sb.toString());
                break;
            case R.id.button2:
                if (equals) {
                    sb = sb.delete(0, sb.length());
                    equals = false;
                }
                if (sb.length() > 0 && sb.charAt(sb.length() - 1) == ')') {
                    sb = sb.append("*2");
                } else {
                    sb = sb.append("2");
                }
                Vibrate.vSimple(view.getContext(), vibrate_time);
                textview.setText(sb.toString());
                break;
            case R.id.button3:
                if (equals) {
                    sb = sb.delete(0, sb.length());
                    equals = false;
                }
                if (sb.length() > 0 && sb.charAt(sb.length() - 1) == ')') {
                    sb = sb.append("*3");
                } else {
                    sb = sb.append("3");
                }
                Vibrate.vSimple(view.getContext(), vibrate_time);
                textview.setText(sb.toString());
                break;
            case R.id.button4:
                if (equals) {
                    sb = sb.delete(0, sb.length());
                    equals = false;
                }
                if (sb.length() > 0 && sb.charAt(sb.length() - 1) == ')') {
                    sb = sb.append("*4");
                } else {
                    sb = sb.append("4");
                }
                Vibrate.vSimple(view.getContext(), vibrate_time);
                textview.setText(sb.toString());
                break;
            case R.id.button5:
                if (equals) {
                    sb = sb.delete(0, sb.length());
                    equals = false;
                }
                if (sb.length() > 0 && sb.charAt(sb.length() - 1) == ')') {
                    sb = sb.append("*5");
                } else {
                    sb = sb.append("5");
                }
                Vibrate.vSimple(view.getContext(), vibrate_time);
                textview.setText(sb.toString());
                break;
            case R.id.button6:
                if (equals) {
                    sb = sb.delete(0, sb.length());
                    equals = false;
                }
                if (sb.length() > 0 && sb.charAt(sb.length() - 1) == ')') {
                    sb = sb.append("*6");
                } else {
                    sb = sb.append("6");
                }
                Vibrate.vSimple(view.getContext(), vibrate_time);
                textview.setText(sb.toString());
                break;
            case R.id.button7:
                if (equals) {
                    sb = sb.delete(0, sb.length());
                    equals = false;
                }
                if (sb.length() > 0 && sb.charAt(sb.length() - 1) == ')') {
                    sb = sb.append("*7");
                } else {
                    sb = sb.append("7");
                }
                Vibrate.vSimple(view.getContext(), vibrate_time);
                textview.setText(sb.toString());
                break;
            case R.id.button8:
                if (equals) {
                    sb = sb.delete(0, sb.length());
                    equals = false;
                }
                if (sb.length() > 0 && sb.charAt(sb.length() - 1) == ')') {
                    sb = sb.append("*8");
                } else {
                    sb = sb.append("8");
                }
                Vibrate.vSimple(view.getContext(), vibrate_time);
                textview.setText(sb.toString());
                break;
            case R.id.button9:
                if (equals) {
                    sb = sb.delete(0, sb.length());
                    equals = false;
                }
                if (sb.length() > 0 && sb.charAt(sb.length() - 1) == ')') {
                    sb = sb.append("*9");
                } else {
                    sb = sb.append("9");
                }
                Vibrate.vSimple(view.getContext(), vibrate_time);
                textview.setText(sb.toString());
                break;
            case R.id.buttonC: //删除
                if (equals) {
                    equals = false;
                }
                if (sb.length() != 0) {
                    sb = sb.deleteCharAt(sb.length() - 1);
                }
                Vibrate.vSimple(view.getContext(), vibrate_time);
                textview.setText(sb.toString());
                break;
            case R.id.buttonAC: //清空
                if (equals) {
                    equals = false;
                }
                sb = sb.delete(0, sb.length());
                Vibrate.vSimple(view.getContext(), vibrate_time);
                textview.setText(sb.toString());
                break;
            case R.id.buttonBracketLeft: //左括号
                if (equals) {
                    equals = false;
                }
                if (sb.length() > 0 && (sb.charAt(sb.length() - 1) >= '0' && sb.charAt(sb.length() - 1) <= '9')) { //当前面是数字是,自动添加为'*('
                    sb = sb.append("*(");
                }
                if (sb.length() == 0) { //如果此时字符串是空的,也就是说想在式子最前面添加括号,就添加括号
                    sb = sb.append("(");
                }
                if (sb.length() > 0 && (sb.charAt(sb.length() - 1) == '*' || sb.charAt(sb.length() - 1) == '/' || sb.charAt(sb.length() - 1) == '+' || sb.charAt(sb.length() - 1) == '-')) { //如果当括号前面是符号时添加括号
                    sb = sb.append("(");
                }
                Vibrate.vSimple(view.getContext(), vibrate_time);
                textview.setText(sb.toString());
                break;
            case R.id.buttonBracketRight: //=右括号
                if (equals) {
                    equals = false;
                }
                int count_num = 0; //数字的数目
                count_bracket_left = count_bracket_right = 0;
                    if (sb.length() != 0) {
                        for (int i = sb.length() - 1; i >= 0; i--) { //对字符串进行遍历,如果存在左括号且括号中有数字,标记转为真,
                            if (count_bracket_left == 0 && (sb.charAt(i) >= '0' && sb.charAt(i) <= '9')) {
                                count_num++;
                            }
                            if (sb.charAt(i) == '(') {
                                count_bracket_left++;
                            }
                            if (sb.charAt(i) == ')') {
                                count_bracket_right++;
                            }
                        }
                        Log.d("count_bracket", String.valueOf(count_bracket_left+" "+count_bracket_right));
                        if ((count_bracket_left > count_bracket_right) && count_num > 0) { //当标记均为真,也就是存在左括号时且在左括号前面有数字,才让添加括号
                            sb = sb.append(")");
                        }
                    }
                Vibrate.vSimple(view.getContext(), vibrate_time);
                textview.setText(sb.toString());
                break;
            case R.id.buttonDiv: //除号
                if (equals) {
                    equals = false;
                }
                if (sb.length() != 0) {
                    if ((sb.charAt(sb.length() - 1) >= '0' && sb.charAt(sb.length() - 1) <= '9') || sb.charAt(sb.length() - 1) == '.') {
                        if ((sb.charAt(sb.length() - 1) >= '0' && sb.charAt(sb.length() - 1) <= '9')) { //如果前一位是数字,就直接添加
                            // if (count_negative > 0){ //如果前面是负数,就加上括号
                            //     sb = sb.append(")/");
                            //     count_negative = 0;
                            // } else {
                            sb = sb.append("/");
                            // }
                        }
                        if (sb.charAt(sb.length() - 1) == '.') { //如果前一位是'.',就先为前一位数字补0
                            sb = sb.append("0/");
                        }
                    }
                    if ((sb.charAt(sb.length() - 1) == ')')) { //如果前一位是')'也让加上/
                        sb = sb.append("/");
                    }
                }
                Vibrate.vSimple(view.getContext(), vibrate_time);
                textview.setText(sb.toString());
                break;
            case R.id.buttonMulti: //乘号
                if (equals) {
                    equals = false;
                }
                if (sb.length() != 0) {
                    if ((sb.charAt(sb.length() - 1) >= '0' && sb.charAt(sb.length() - 1) <= '9') || sb.charAt(sb.length() - 1) == '.') {
                        if ((sb.charAt(sb.length() - 1) >= '0' && sb.charAt(sb.length() - 1) <= '9')) {//如果前一位是数字,就直接添加
                            sb = sb.append("*");
                        }
                        if (sb.charAt(sb.length() - 1) == '.') {//如果前一位是'.',就先为前一位数字补0
                            sb = sb.append("0*");
                        }
                    }
                    if ((sb.charAt(sb.length() - 1) == ')')) {
                        sb = sb.append("*");
                    }
                }
                Vibrate.vSimple(view.getContext(), vibrate_time);
                textview.setText(sb.toString());
                break;
            case R.id.buttonDec: //减号
                if (equals) {
                    equals = false;
                }
                if (sb.length() != 0) {
                    if ((sb.charAt(sb.length() - 1) >= '0' && sb.charAt(sb.length() - 1) <= '9') || sb.charAt(sb.length() - 1) == '.' ||
                            sb.charAt(sb.length() - 1) == '(') {
                        if (sb.charAt(sb.length() - 1) >= '0' && sb.charAt(sb.length() - 1) <= '9') {//如果前一位是数字,就直接添加
                            // if (count_negative > 0) { //如果前面是负数,就加上括号
                            //     sb = sb.append(")-");
                            //     count_negative = 0;
                            // } else {
                            sb = sb.append("-");
                            // }
                        }
                        if (sb.charAt(sb.length() - 1) == '.') {//如果前一位是'.',就先为前一位数字补0
                            sb = sb.append("0-");
                        }
                        if (sb.charAt(sb.length() - 1) == '(') {
                            sb = sb.append("-");
                            count_negative++;
                        }

                    } else if ((sb.charAt(sb.length() - 1) == ')')) {
                        sb = sb.append("-");
                    } else {
                        sb = sb.append("(-");
                    }

                } else { //负号
                    sb = sb.append("(-");
                    count_negative++;
                }
                Vibrate.vSimple(view.getContext(), vibrate_time);
                textview.setText(sb.toString());
                break;
            case R.id.buttonAdd: //加号
                if (equals) {
                    equals = false;
                }
                if (sb.length() != 0) {
                    if ((sb.charAt(sb.length() - 1) >= '0' && sb.charAt(sb.length() - 1) <= '9') || sb.charAt(sb.length() - 1) == '.') {
                        if ((sb.charAt(sb.length() - 1) >= '0' && sb.charAt(sb.length() - 1) <= '9')) {//如果前一位是数字,就直接添加
                            // if (count_negative > 0) { //如果前面是负数,就加上括号
                            //     sb = sb.append(")+");
                            //     count_negative = 0;
                            // } else {
                            sb = sb.append("+");
                            // }
                        }
                        if (sb.charAt(sb.length() - 1) == '.') {//如果前一位是'.',就先为前一位数字补0
                            sb = sb.append("0+");
                        }
                    }
                    if ((sb.charAt(sb.length() - 1) == ')')) {
                        sb = sb.append("+");
                    }
                }
                Vibrate.vSimple(view.getContext(), vibrate_time);
                textview.setText(sb.toString());
                break;
            case R.id.buttonDot: //小数点
                if (equals) {
                    equals = false;
                }
                if (sb.length() != 0) {
                    int count_dot = 0;
                    for (int i = sb.length() - 1; i >= 0; i--) {
                        if (sb.charAt(i) == '.') {
                            count_dot++;
                        }
                        if (!(sb.charAt(i) >= '0' && sb.charAt(i) <= '9')) {
                            break;
                        }
                    }
                    if (count_dot == 0) {
                        if (sb.charAt(sb.length() - 1) == '*' || sb.charAt(sb.length() - 1) == '/' || sb.charAt(sb.length() - 1) == '+' || sb.charAt(sb.length() - 1) == '-') {
                            // 如果最后一位是符号时,直接输小数点会自动补'0',形成'0.'
                            sb = sb.append("0.");
                        } else {
                            sb = sb.append(".");
                        }
                    }
                }
                Vibrate.vSimple(view.getContext(), vibrate_time);
                textview.setText(sb.toString());
                break;
            case R.id.buttonEqual: //等号
                if (equals) {
                    equals = false;
                }
                count_bracket_right = count_bracket_left = 0;
                if (sb.length() != 0) {
                    for (int i = 0; i < sb.length(); i++) {
                        if (sb.charAt(i) == '(')
                            count_bracket_left++;
                        if (sb.charAt(i) == ')')
                            count_bracket_right++;
                    }
                    if (count_bracket_left != count_bracket_right) {
                        Toast.makeText(MainActivity.this, "请注意括号匹配", Toast.LENGTH_SHORT).show();
                    }
                    if (count_bracket_left == count_bracket_right &&
                            ((sb.charAt(sb.length() - 1) >= '0' && sb.charAt(sb.length() - 1) <= '9') || sb.charAt(sb.length() - 1) == ')')) {
                        equals = true;
                        count_negative = 0;
                        try {
                            textview.setText(InfixToSufix.Cal(InfixToSufix.Suffix(sb)));
                            sb = sb.delete(0, sb.length());
                            sb.append(textview.getText().toString());
                        } catch (Exception e){
                            textview.setText("Error");
                            sb = sb.delete(0, sb.length());
                        }
                    }
                }
                Vibrate.vSimple(view.getContext(), vibrate_time);
                break;
            default:
                break;
        }
    }
}

算法

1. 中缀表达式转后缀表达式

http://www.nowamagic.net/librarys/veda/detail/2307

2. 后缀表达式的计算

http://www.nowamagic.net/librarys/veda/detail/2306

3. 具体代码

在MainActivity同一个包下新建class文件,命名为InfixToSuffix
代码如下

package com.example.calculator;

import android.util.Log;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;

/**
 * Created by littlecorgi_a1203991686 on 2018/08/02 15:56.
 */
public class InfixToSuffix {
    /**
     * 中缀转后缀
     * @param str 从键盘读入的String类型的对象,也就是要求的表达式
     * @return 返回一个ArrayList类型的对象,也就是一个集合,也即是后缀表达式,让cal方法接收,
     */
    public static ArrayList Suffix(StringBuilder str){
        /*
         * Stack() 构造方法
         * void add(int index, Object element) 在此向量的指定位置插入指定的元素
         * boolean empty() 测试堆栈是否为空
         * Object pop( ) 移除堆栈顶部的对象,并作为此函数的值返回该对象
         * Object push(Object element) 把项压入堆栈顶部
         */

        for (int i = 1; i < str.length(); i++){ //识别到'(-'自动补0
            if (str.charAt(i) == '-' && str.charAt(i - 1) == '('){
                str.insert(i, '0');
            }
        }

        StringBuilder temp = new StringBuilder(); // 当做中间字符串,临时存放数字,方便往list中添加

        List<String> list = new ArrayList<>(); // 用于存储分割后的数字和符号

        ArrayList<String> result = new ArrayList<>(); //作为结果集合,后缀表达式保存在其中

        for (int i = 0; i < str.length(); i++){
            // if ( i != str.length() - 1 ){ //对不是最后一位数字的
            if ((str.charAt(i) >= '0' && str.charAt(i) <= '9') || str.charAt(i) == '.'){ // 判断是不是数字
                if (str.charAt(i) == '.' && temp.length() == 0){ //如果此位为'.',且这个时候中间字符串为空,自动补0
                    temp.append(0); // 添加0,在添加'.'
                    temp.append(str.charAt(i));
                } else {
                    temp.append(str.charAt(i)); // 是数字,就往中间字符串添加
                }
                if (i == str.length() - 1){ //对最后一位进行单独判断,如果是字符串最后一位,直接进行添加到list操作
                    list.add(temp.toString());
                }
            } else  { // 不是数字
                if (temp.length() != 0)
                    list.add(temp.toString()); // 判断中间字符串长度是不是0,不是0就往list中添加
                list.add(String.valueOf(str.charAt(i))); // 将符号往list中添加
                temp.delete(0, temp.length()); // 清空中间字符串
            }
            // } else { //单独把最后一个字符拿出来进行判断
            //     if ((str.charAt(i) >= '0' && str.charAt(i) <= '9') || str.charAt(i) == '.'){ // 下面代码注释参考上面
            //         temp.append(str.charAt(i));
            //
            //     } else  {
            //         if (temp.length() != 0)
            //             list.add(temp.toString()); // 判断中间字符串长度是不是0,不是0就往list中添加
            //         list.add(String.valueOf(str.charAt(i)));
            //         temp.delete(0, temp.length());
            //     }
            // }
        }

        // 遍历输出
        for (String aList : list) {
            System.out.print(aList + " ");
        }
        System.out.println();

        // 定义栈
        Stack<String> stack = new Stack<>();

        // 定义符号的优先级
        Map<Character, Integer> map = new HashMap<>();
        map.put('(', 0);
        map.put(')', 0);
        map.put('*', 1);
        map.put('/', 1);
        map.put('+', 2);
        map.put('-', 2);

        for ( String s : list ) {
            // System.out.println("s=" + s);
            if ( s.equals("*") || s.equals("/") || s.equals("+") || s.equals("-") || s.equals("(") || s.equals(")") ){ //不为数字
                if (stack.size() == 0){ //如果当前栈里面没有元素,不管是啥,直接进栈
                    stack.push(s);
                } else { //如果栈中有元素
                    if (s.equals(")")) { //如果当前是),那就的往前找(与其对应输出
                        if (!stack.empty()){ //容错,不然会有EmptyStackException,下面的类似的表达式同理
                            while (!stack.peek().equals("(")){ //循环,从栈顶开始循环,一直到栈顶为(,退出循环
                                // System.out.print(stack.pop() + " "); //输出并且把栈顶元素移除
                                result.add(stack.pop()); //保存到结果集合中,并且把栈顶元素移除
                                if (stack.empty()){//容错,不然会有EmptyStackException,见上面
                                    break;
                                }
                            }
                            if (!stack.empty()){
                                if (stack.peek().equals("("))//如果此时栈顶元素为(,就将他移除,原因是后缀表达式不用(,而在之前我们将他压入了栈中,具体见下面第5行
                                    stack.pop();
                            }
                        }
                    } else{
                        if (s.equals("(")){ //如果是'('将其入栈
                            stack.push(s);
                        } else {
                            if (stack.peek().charAt(0) != '('){
                                if ( map.get(s.charAt(0)) < map.get(stack.peek().charAt(0)) ){ //栈顶符号的优先级高于元素优先级,也就是数字小,进栈
                                    stack.push(s);
                                } else { //栈顶符号优先级低于元素优先级,输出并循环
                                    while ((map.get(s.charAt(0)) >= map.get(stack.peek().charAt(0))) && !stack.empty()){
                                        // System.out.print(stack.pop() + " ");
                                        result.add(stack.pop());
                                        if (stack.empty()){
                                            break;
                                        }
                                        if (stack.peek().equals("(")){
                                            break;
                                        }
                                    }
                                    stack.push(s);
                                }
                            } else {
                                stack.push(s);
                            }
                        }
                    }
                }
            } else { //是数字
                result.add(s);
            }
        }

        while (!stack.empty()){ //最后遍历栈,把结果保存到result中,直到栈被清空
            result.add(stack.pop());
        }
        return result;
    }

    /**
     * 用于计算后缀表达式的结果
     * @param arrayList 从Suffix传出来的后缀表达式
     * @return 返回String类型对象,也就是最后的结果
     */

    public static String Cal(ArrayList arrayList){
        int length = arrayList.size();
        String[] arr = new String[length]; //转为字符串数组,方便模拟栈

        for (int i = 0; i < arrayList.size(); i++){
            arr[i] = (String)arrayList.get(i);
        }
        Log.d("asdfg", arrayList.toString());

        List<String> list = new ArrayList<>();
        for (String anArr : arr) { //此处就是上面说的运算过程,因为list.remove的缘故,所以取出最后一个数个最后两个数  都是size-2
            int size = list.size();
            switch (anArr) {
                case "+":
                    BigDecimal a = new BigDecimal(list.remove(size - 2)).add(new BigDecimal(list.remove(size - 2)));
                    // list.add(String.valueOf(a.stripTrailingZeros().toPlainString()));
                    list.add(a.stripTrailingZeros().toString()); //去掉结果最后的0
                    break;
                case "-":
                    BigDecimal b = new BigDecimal(list.remove(size - 2)).subtract(new BigDecimal(list.remove(size - 2)));
                    list.add(b.stripTrailingZeros().toString());
                    break;
                case "*":
                    BigDecimal c = new BigDecimal(list.remove(size - 2)).multiply(new BigDecimal(list.remove(size - 2)));
                    list.add(c.stripTrailingZeros().toString());
                    break;
                case "/":
                    BigDecimal d = new BigDecimal(list.remove(size - 2)).divide(new BigDecimal(list.remove(size - 2)), 10, BigDecimal.ROUND_HALF_UP);
                    list.add(d.stripTrailingZeros().toString());
                    break;
                default:
                    list.add(anArr);
                    break;//如果是数字  直接放进list中
            }
        }

        if (list.size() == 1){
            if (list.get(0).length() < 30){ //当结果长度不长时,就直接输出
                BigDecimal bd = new BigDecimal(list.get(0));
                return bd.toPlainString(); //BigDecimal默认直接输出
            } else { //当结果过长时,就用科学计数法输出
                double d = Double.valueOf(list.get(0));
                return String.valueOf(d); //Double会使用科学计数法输出
            }
        } else {
            return "运算失败";
        }
    }
}

猜你喜欢

转载自blog.csdn.net/a1203991686/article/details/81394655