编写安卓手机计算器
提示:本章代码都是经过红米note10pro的测试,解决了大部分bug,一些特殊情况的处理方式也是仿照本机的系统计算器处理方式,如有bug或什么疑惑的地方请大家留言!
提示:完整代码在实现步骤中,可以直接复制到Android Studio中运行,如无法运行请在评论区留言
文章目录
- 编写安卓手机计算器
- 前言
- 一、用到哪些知识点?
- 二、实现步骤
- 1.写UI
- 2.代码设计思路
- 总结
前言
提示:前情提要,可直接忽略
因博主刚入门安卓四天,而java也是速学赶进度,一个多月就将Java基础过完了,所以没有什么实践的机会,刚好学安卓UI的时候,知道了一些简单的布局(先前都是学基础,没有接触过UI),所以就想着门面都能做出来的,为什么不做个完整的计算器,然后就动手了。
提示:下面是重点
一、用到哪些知识点?
1.UI界面的设计:线性布局、EditText控件、Button控件、TextView控件的简单使用(难的使用我自己也不会)。且全局控件都是默认样式,因为我懒。
2.后台计算的实现:一定的java基础,字符串简单的处理(从文本中分解出运算内容),包装类的静态方法的使用(字符串转数值),选择语句的使用,简单的方法调用,简单的异常捕获和简单的控件使用语句等。
3.设计细节(我是先把程序写的差不多后发现出现了很多问题,然后就抽了点时间将问题写了出来,写的很随意)
二、实现步骤
1.写UI
由于我自身UI设计刚刚入门,所以设置的布局大多都是固定的大小,有些手机屏幕可能会不适应,大家可以自己调整)
代码如下:
//安卓UI界面代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/edit_text"
android:layout_width="match_parent"
android:layout_height="250dp"
android:maxLines="3"
android:textSize="40dp"
android:inputType="textMultiLine"
android:text="0" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/edit_text">
<Button
android:id="@+id/calc_clear"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="40sp"
android:text="C"/>
<Button
android:id="@+id/calc_del"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="40sp"
android:textAllCaps="false"
android:text="de"/>
<Button
android:id="@+id/calc_bfh"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="40sp"
android:text="%"/>
<Button
android:id="@+id/calc_chu"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="40sp"
android:text="÷"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/edit_text">
<Button
android:id="@+id/calc_seven"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="40sp"
android:text="7"/>
<Button
android:id="@+id/calc_eight"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="40sp"
android:text="8"/>
<Button
android:id="@+id/calc_nine"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="40sp"
android:text="9"/>
<Button
android:id="@+id/calc_cheng"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="40sp"
android:text="×"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/edit_text">
<Button
android:id="@+id/calc_four"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="40sp"
android:text="4"/>
<Button
android:id="@+id/calc_five"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="40sp"
android:text="5"/>
<Button
android:id="@+id/calc_six"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="40sp"
android:text="6"/>
<Button
android:id="@+id/calc_jian"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="40sp"
android:text="-"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/edit_text">
<Button
android:id="@+id/calc_one"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="40sp"
android:text="1"/>
<Button
android:id="@+id/calc_two"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="40sp"
android:text="2"/>
<Button
android:id="@+id/calc_three"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="40sp"
android:text="3"/>
<Button
android:id="@+id/calc_add"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="40sp"
android:text="+"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/edit_text">
<Button
android:id="@+id/calc_zero"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:textSize="40sp"
android:text="0"
/>
<Button
android:id="@+id/calc_point"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="40sp"
android:text="."/>
<Button
android:id="@+id/calc_dengyu"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="40sp"
android:text="="/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="30sp"
android:textColor="@color/hulan"
android:text="据说输入特殊数字后按下等于\n号会发生奇怪的事情( ̄▽ ̄)"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="30sp"
android:textColor="@color/black"
android:text="还有记得帮我找找哪里Bug"/>
</LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/bug"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:text="bug反馈"/>
</RelativeLayout>
</LinearLayout>
整个布局思路就是一个垂直的线性布局中包含一个EditText控件和一些子水平线性布局组成的一个键盘和输入框。最后加的是一些无关紧要的字,如果不需要可以在代码尾部将那几个TextView控件和最后一个Button控件删除即可。
2.代码设计思路
1.通过用户按下不同的键,在后台通过选择语句判断输入的值,然后存放在一个可变字符串中。
2.每次用户按下提供的按键后,后台获取修改的值,然后通过EditText的方法setText直接显示在文本输入框中,此时用户输入的数据都是String类型的,存放在text变量中。
3.当用户按下等号,就开始进入计算环节,在先前都是讲输入数据保存,并没有进行计算,(这样设计比较简单,但是效率不高)。
(1)开始计算,首先要将用户输入的String类型的数据分解成一个个独立的元素,每个元素是一个单独的运算符或者一个数值。我们将这些分解出的独立的元素存放在字符串数组中,这时我们可以遍历之前text变量,当遇到的字符的ASCII位于数值的范围([‘0’,‘9’]),或者是一个小数点时(输入的可能为小数),就将其添加在当前字符串元素的末尾,当遇到运算符后,就存放在下一个数值元素中,然后再新建一个数组元素继续开始存放值。这就是数据提取的过程。
(整个叙述可能有点混乱,需要具体了解原理可以私聊或者留言评论,并且我的思路只有这个是最简单的但不是最高效的,但是我懒。比较高效的方式是通过指针在用户输入的时候就将其存放在每个不同的数组中,但是用容器的话,对于指针的控制java貌似没有C方便)
(2)将数据提取后,先要转换为数值,我们可以通过 Double.parseDouble进行转换,在转换之前记得判断是否为一个数值,并且在每次操作前判断数据是否为空(避免app闪退),然后每次转换出数据后赋值给temp (1),就是进行运算了,首次运算如果用户输入的第一个是运算符,就将默认值0与之进行运算。如果用户第一次输入的是数值,就直接赋值给temp(0)(通过记录运算符的变量是否为默认值判断是否是首次计算),当下一个运算符出现后,就让temp[0]和temp[1]通过这个运算符进行计算,最后的结果存放在temp[0]中,以便下一次运算。
(3)当最后将所有的计算都完成后,未来显示在输入框中的数据显示样式不会显示多余的小数,我们需要将最后的结果进行浮点数判断,判断的方式有多种,方法1是将其转换为字符串,然后查找小数点的位置,如果没有小数点,就说明是整数,否则就从小数点往后遍历,如果发现小数点后面都为0,那么也将其结果以整数的方式呈现。方法2是直接强转为整型,然后直接判断与原来的值是否相等,如果相等就说明小数可以舍弃咯。方法3为了弥补前面不能处理科学记数法的问题,我就直接根据java的性质(整数转换位浮点数1 -> 1.0),判断小数点后是否为(.0)如果是,则说明是一个整数,不是,则说明是一个科学计数法的数或者小数,都用double表示。
4.并且在代码中大大小小都对一些地方做了各种细节的处理,不知道这种处理方式的好坏,不过缝缝补补总算是一个差不多的计算器了。
5.在代码中还有一些有趣的输入和输出,在代码的最后一个方法中,也是我自己的自娱自乐吧。
//java逻辑代码
package com.example.calc;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.text.InputType;
import android.view.Gravity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import com.example.calculator.R;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
//一些变量的存放
Button mButton_clear;
Button mButton_del;
Button mButton_bfh;
Button mButton_chu;
Button mButton_7;
Button mButton_8;
Button mButton_9;
Button mButton_cheng;
Button mButton_4;
Button mButton_5;
Button mButton_6;
Button mButton_jian;
Button mButton_1;
Button mButton_2;
Button mButton_3;
Button mButton_add;
Button mButton_0;
Button mButton_point;
Button mButton_dengyu;
EditText mEditText;
Button bug;
StringBuilder text;
StringBuilder[] num;
double number;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findButton();
buttonClick();
mEditText.setInputType(InputType.TYPE_NULL);//禁止唤出系统输入键盘
mEditText.setGravity(Gravity.TOP);//文本显示在顶部
mEditText.setSingleLine(false);//设置可以换行
}
//按键单击监视的注册
private void buttonClick() {
mButton_clear.setOnClickListener(this);
mButton_del.setOnClickListener(this);
mButton_bfh.setOnClickListener(this);
mButton_chu.setOnClickListener(this);
mButton_7.setOnClickListener(this);
mButton_8.setOnClickListener(this);
mButton_9.setOnClickListener(this);
mButton_cheng.setOnClickListener(this);
mButton_4.setOnClickListener(this);
mButton_5.setOnClickListener(this);
mButton_6.setOnClickListener(this);
mButton_jian.setOnClickListener(this);
mButton_1.setOnClickListener(this);
mButton_2.setOnClickListener(this);
mButton_3.setOnClickListener(this);
mButton_add.setOnClickListener(this);
mButton_0.setOnClickListener(this);
mButton_point.setOnClickListener(this);
mButton_dengyu.setOnClickListener(this);
bug.setOnClickListener(this);
}
//按键监视:控制输入内容
@Override
public void onClick(View v) {
switch (v.getId()) {
//第一层解决数字的情况
case R.id.calc_seven:
text.append("7");
break;
case R.id.calc_eight:
text.append("8");
break;
case R.id.calc_nine:
text.append("9");
break;
case R.id.calc_four:
text.append("4");
break;
case R.id.calc_five:
text.append("5");
break;
case R.id.calc_six:
text.append("6");
break;
case R.id.calc_one:
text.append("1");
break;
case R.id.calc_two:
text.append("2");
break;
case R.id.calc_three:
text.append("3");
break;
case R.id.calc_zero:
text.append("0");
break;
default:
break;
}
//处理一些非数字的输入,需要避免一些会发生空指针的情况
switch (v.getId()) {
case R.id.calc_clear:
if (text != null) {
text.delete(0, text.length());
number = 0;
}
break;
case R.id.calc_del:
if (text != null && text.length() > 0) {
text.delete(text.length() - 1, text.length());
}
break;
case R.id.calc_bfh:
text.append("%");
break;
case R.id.calc_chu:
text.append("÷");
break;
case R.id.calc_cheng:
text.append("×");
break;
case R.id.calc_jian:
text.append("-");
break;
case R.id.calc_add:
text.append("+");
break;
case R.id.calc_point:
//为了数据的合理,一个数只能有一个小数点
int indexPoint = text.lastIndexOf(".");//查找上一个小数点的位置
//查找前一个运算符的位置
int maxIndex = Math.max(Math.max(text.lastIndexOf("+"),text.lastIndexOf("-"))
,Math.max(text.lastIndexOf("×"), text.lastIndexOf("÷")));
char end = text.charAt(text.length() - 1);
//最后一个小数点后面有运算符,说明是一个新的数,并且小数点前必须要有整数部分
if((maxIndex > indexPoint && (end >= '0' && end <= '9')) || indexPoint == -1){
text.append(".");
}
break;
case R.id.bug://添加好友
Intent intent = new Intent();
intent.setData(Uri.parse("http://qm.qq.com/cgi-bin/qm/qr?k=C5Z79DrIV6H8qXUvlVvLQHBi4XL9pYfk&noverify=0"));
startActivity(intent);
break;
}
//输入框初始为0,当输入内容时去掉0
if(text.length() == 0){
text.append("0");
}
//第一次输入数字,将初始的0去掉
//运算符除外
else if(text.length() > 1 && text.charAt(0) == '0' && (text.charAt(1) >= '0' && text.charAt(1) <= '9')){
text.delete(0, 1);
}
//处理等号的一些逻辑
if(v.getId() == R.id.calc_dengyu){//等于号的添加,并进行计算
if (text.toString().equals("0")) {
String s = mEditText.getText().toString();
mEditText.setText(s);
} else {
text.append("\n=");
String t = calculation();//开始计算并将计算结果返回
text.append(t);
mEditText.setText(text);//将结果呈现在输入框中
num = null;
text.delete(0, text.length());
text.append("0");
}
Spoof();
}
else{//非等号的添加,如果重复添加运算符,就用当前运算符覆盖前一个运算符
char endCh2 = '0';
if(text.length() > 1) {
endCh2 = text.charAt(text.length() - 2);
}
if(endCh2 >= '0' && endCh2 <= '9') {//判断输入前上一个是否输入了数字
mEditText.setText(text);//是数字就直接添加
}
else{//输入前上一个是符号
char endCh1 = text.charAt(text.length() - 1);
if(!(endCh1 >= '0' && endCh1 <= '9')) {//判断当前是否为符号,如果是,就将当前输入的符号覆盖上一个符号
text.delete(text.length() - 2, text.length() - 1);
}
mEditText.setText(text);//是数字就直接添加
}
}
}
//获取控件和一些类的对象,便于操作
private void findButton() {
mButton_clear = (Button) findViewById(R.id.calc_clear);
mButton_del = (Button) findViewById(R.id.calc_del);
mButton_bfh = (Button) findViewById(R.id.calc_bfh);
mButton_chu = (Button) findViewById(R.id.calc_chu);
mButton_7 = (Button) findViewById(R.id.calc_seven);
mButton_8 = (Button) findViewById(R.id.calc_eight);
mButton_9 = (Button) findViewById(R.id.calc_nine);
mButton_cheng = (Button) findViewById(R.id.calc_cheng);
mButton_4 = (Button) findViewById(R.id.calc_four);
mButton_5 = (Button) findViewById(R.id.calc_five);
mButton_6 = (Button) findViewById(R.id.calc_six);
mButton_jian = (Button) findViewById(R.id.calc_jian);
mButton_1 = (Button) findViewById(R.id.calc_one);
mButton_2 = (Button) findViewById(R.id.calc_two);
mButton_3 = (Button) findViewById(R.id.calc_three);
mButton_add = (Button) findViewById(R.id.calc_add);
mButton_0 = (Button) findViewById(R.id.calc_zero);
mButton_point = (Button) findViewById(R.id.calc_point);
mButton_dengyu = (Button) findViewById(R.id.calc_dengyu);
mEditText = (EditText) findViewById(R.id.edit_text);
bug = (Button) findViewById(R.id.bug);
int t = 1;//符号位
text = new StringBuilder();
text.append("0");
}
//具体的计算逻辑 - 函数中的多个功能可以当作模块分解出来,使逻辑更加清晰(但是我懒我就不分)
private String calculation() {
int pos = 0;//记录操作数和符号的总个数
double temp[] = new double[2];//存放计算中的数据
num = new StringBuilder[100];//这里直接使用数组而不是ArrayList,因为不能直接使用下标访问,就很不舒服,然后综合考虑就开了100的大小数组(毕竟当前我这小破计算器谁那么无聊这么玩)
num[0] = new StringBuilder();
//数据的提取和计算
for (int i = 0; i < text.length(); i++) {
if ((text.charAt(i) >= '0' && text.charAt(i) <= '9') || text.charAt(i) == '.') {//整数和小数
num[pos].append(text.charAt(i));
} else if (text.charAt(i) != '=') {//遇到了符号
num[++pos] = new StringBuilder();//开辟存储符号空间
num[pos].append(text.charAt(i));//存储符号
num[++pos] = new StringBuilder();//开辟下一个数字的空间
} else {//等于,开始计算
num[++pos] = new StringBuilder();//开辟存储符号空间
num[pos].append(text.charAt(i));//存储符号
}
}
//上面的都是数据的提取,这个才是真的对数据计算的逻辑
temp[0] = number;//
calc(temp);//真正的计算逻辑
number = temp[0];//将计算的结果保存
//精确到小数点后8位
String s = number + "";
int indexE = s.lastIndexOf("E");
String sE = "";
if(indexE != -1) {
sE = s.substring(indexE);//截取科学计数法指数
}
//截取有效位需要判断原本的字符串长度,防止数组越界
number = Double.parseDouble(s.substring(0,Math.min(16, s.length() - sE.length())) + sE);
//判断是否为浮点数或整数
if (isDoubleOrInt(number) == true) {
return (long)number + "";
} else {//浮点数数
return number + "";
}
}
private void calc(double temp[]){
//分别获取数组中每个的字符串转换成数字,然后进行计算
//temp[0]被操作数,默认是0
//temp[1]操作数
//temp[2]结果
char temp2 = '0';
//将提取的数字进行对应运算符的操作
for (int j = 0; num[j] != null; j++) {
if (num[j].length() != 0 && num[j].charAt(0) >= '0' && num[j].charAt(0) <= '9') {//是数字&& num[j].charAt(0) != '+' && num[j].charAt(0) != '-' && num[j].charAt(0) != '×' && num[j].charAt(0) != '÷' && num[j].charAt(0) != '%' && num[j].charAt(0) != '='
//全部按照浮点数计算
try {
temp[1] = Double.parseDouble(num[j].toString());
} catch (Exception e) {
e.printStackTrace();
}
} else if (num[j].length() != 0) {//奇数是运算符
//在转换每个数之后,判断前一个运算符的正负性
if (temp2 == '0') {//第一次运算
temp[0] = temp[1];
} else {//后序运算
switch (temp2) {
case '+':
temp[0] = temp[0] + temp[1];
break;
case '-':
temp[0] = temp[0] - temp[1];
break;
case '×':
temp[0] = temp[0] * temp[1];
break;
case '÷':
temp[0] = temp[0] / temp[1];
break;
case '%':
temp[0] = temp[0] % temp[1];
break;
}
}
temp[1] = 0;
if (num[j].length() != 0) {
temp2 = num[j].charAt(0);//获取运算符,因为第二个操作数是通过前一个运算符和第一个操作数计算
}
}
}
}
//用于判断最后的结果是整数还是小数
private boolean isDoubleOrInt(double s) {
String t = s + "";
int index = t.indexOf(".");
String substring = t.substring(index);
//没有小数点且者相等
return substring.equals(".0");//是整数就返回true
}
//当运算结果为符合其中的某个条件,就更改输入框的内容
private void Spoof() {
if (number == 520) {
mEditText.setText("你好骚啊...");
} else if (number == 1834) {
mEditText.setText("一把扇死你!!!");
} else if (number == 5201314) {
mEditText.setText("专心敲代码,\n想什么有的没的");
} else if (number == 1314520) {
mEditText.setText("好好做你的单生狗,\n恋爱只会降低你拔剑的速度!");
} else if (number == 250) {
mEditText.setText("远在天边,近在眼前!");
} else if (number == 1433223) {
mEditText.setText("元哥玩魔怔了吧。。。");
} else if (number == 9494) {
mEditText.setText("就是就是!");
} else if (number == 1024) {
mEditText.setText("程序员节快乐!");
}
}
}
代码我是放在同一个类中,所以结构看起来略有些复杂,并且效率低下。但是水平有限,且代码中都有比较详细的注释,如果有看不懂可以评论一起交流讨论。
总结
还没有动手的时候自以为这个程序挺简单的(用到的语法简单,逻辑难),等到开始上手写底层逻辑的时候,才发现有很多特殊的情况需要不断的处理调试,然后不断更改代码。然后有很多特殊情况我不知道如何处理会比较好,所以我就借鉴了手机系统自带的浏览器。最后加上摸鱼和吃饭花了一天时间才将这个计算器给写好(稍微正常的跑起来)。有什么不足的地方希望各位大佬指点!