一个模仿iPhoneUI的计算器,支持大数运算,带括号的运算。
如图所示,为该计算器UI界面。主要采用LinearLayout
布局。并且在其中嵌套LinearLayout
。
首先,在整体框架内添加一下几种属性:
orientation
为垂直布局fitsSystemWindows
为true
,让状态栏可改变<LinearLayout 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"
android:background="#000"
android:fitsSystemWindows="true"
android:orientation="vertical">
这里还设置了一个取消标题栏的效果。只需在AndroidManifest.xml
文件内的onCreate()
方法内加入以下代码即可
ActionBar actionBar = getSupportActionBar();//隐藏标题栏
if (actionBar != null) {
actionBar.hide();
}
并且,我们发现状态栏也变成了透明色,这里我们直接在setContentView()
前添加一段代码即可。代码如下:
if (Build.VERSION.SDK_INT >= 21) {//状态栏透明
View decorView = getWindow().getDecorView();
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
getWindow().setStatusBarColor(Color.TRANSPARENT);
}
如此整体布局大概完成。
选择在LinearLayout
里面嵌套LinearLayout
。
android:autoSizeTextType="uniform
感兴趣的朋友可以了解一下,但是一旦使用了这个属性,我们在TextView
里设置的hint
就会失效。drawavle
文件下,先创建按前和按后的状态的xml文件。接着再新建一个xml文件将这两种状态引入。用android:state_pressed:true
控制按钮变色状态。最后将文件引入activity_main
并在每一个按钮添加android:background=""
将各自的按钮变化引入。 <LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="3">
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_gravity="bottom"
android:layout_marginRight="20dp"
android:autoSizeMaxTextSize="90dp"
android:autoSizeTextType="uniform"
android:gravity="bottom|right"
android:hint="0"
android:maxLines="3"
android:textColor="#fff"
android:textColorHint="#fff" />
LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="horizontal">
<Button
android:id="@+id/button_AC"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_marginLeft="15dp"
android:background="@drawable/button_shape1_play"
android:text="AC"
android:textColor="#000"
android:textSize="30dp" />
<Button
android:id="@+id/button_left_bracket"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_marginLeft="15dp"
android:background="@drawable/button_shape1_play"
android:text="("
android:textColor="#000"
android:textSize="30dp" />
<Button
android:id="@+id/button_right_bracket"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_marginLeft="15dp"
android:background="@drawable/button_shape1_play"
android:text=")"
android:textColor="#000"
android:textSize="30dp" />
<Button
android:id="@+id/button_div"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_marginLeft="15dp"
android:background="@drawable/button_shape2_play"
android:text="÷"
android:textColor="#fff"
android:textSize="30dp" />
LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="horizontal">
<Button
android:id="@+id/button_7"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_marginLeft="15dp"
android:background="@drawable/button_shape3_play"
android:text="7"
android:textColor="#FFF"
android:textSize="30dp" />
<Button
android:id="@+id/button_8"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_marginLeft="15dp"
android:background="@drawable/button_shape3_play"
android:text="8"
android:textColor="#FFF"
android:textSize="30dp" />
<Button
android:id="@+id/button_9"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_marginLeft="15dp"
android:background="@drawable/button_shape3_play"
android:text="9"
android:textColor="#FFF"
android:textSize="30dp" />
<Button
android:id="@+id/button_mul"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_marginLeft="15dp"
android:background="@drawable/button_shape2_play"
android:text="×"
android:textColor="#fff"
android:textSize="35dp" />
LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<Button
android:id="@+id/button_4"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_marginLeft="15dp"
android:background="@drawable/button_shape3_play"
android:text="4"
android:textColor="#FFF"
android:textSize="30dp" />
<Button
android:id="@+id/button_5"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_marginLeft="15dp"
android:background="@drawable/button_shape3_play"
android:text="5"
android:textColor="#FFF"
android:textSize="30dp" />
<Button
android:id="@+id/button_6"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_marginLeft="15dp"
android:background="@drawable/button_shape3_play"
android:text="6"
android:textColor="#FFF"
android:textSize="30dp" />
<Button
android:id="@+id/button_minus"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_marginLeft="15dp"
android:background="@drawable/button_shape2_play"
android:text="-"
android:textColor="#fff"
android:textSize="30dp" />
LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<Button
android:id="@+id/button_1"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_marginLeft="15dp"
android:background="@drawable/button_shape3_play"
android:text="1"
android:textColor="#FFF"
android:textSize="30dp" />
<Button
android:id="@+id/button_2"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_marginLeft="15dp"
android:background="@drawable/button_shape3_play"
android:text="2"
android:textColor="#FFF"
android:textSize="30dp" />
<Button
android:id="@+id/button_3"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_marginLeft="15dp"
android:background="@drawable/button_shape3_play"
android:text="3"
android:textColor="#FFF"
android:textSize="30dp" />
<Button
android:id="@+id/button_add"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_marginLeft="15dp"
android:background="@drawable/button_shape2_play"
android:text="+"
android:textColor="#fff"
android:textSize="30dp" />
LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<Button
android:id="@+id/button_0"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_marginLeft="15dp"
android:background="@drawable/button_shape3_play"
android:text="0"
android:textColor="#FFF"
android:textSize="30dp" />
<Button
android:id="@+id/button_point"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_marginLeft="15dp"
android:background="@drawable/button_shape3_play"
android:text="."
android:textColor="#FFF"
android:textSize="30dp" />
<Button
android:id="@+id/button_delete"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_marginLeft="15dp"
android:background="@drawable/button_shape3_play"
android:text="←"
android:textColor="#FFF"
android:textSize="30dp" />
<Button
android:id="@+id/button_equal"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_marginLeft="15dp"
android:background="@drawable/button_shape2_play"
android:text="="
android:textColor="#fff"
android:textSize="30dp" />
LinearLayout>
修改AndroidMainfest.xml
内的android:label="计算器"
来修改软件名称。
并且在draeable
中添加png图片文件,再回到AndroidMainfest.xml
,修改android:icon="@drawable/"
引入图片文件即可。
应该考虑到的异常
代码如下
package com.example.androidcalculator;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import android.content.DialogInterface;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
int left_brackets_flag = 0;//左括号数量,与有括号匹配使用
boolean minus_add = false;//负号添加
boolean brackets_add = false;//括号添加
boolean zero = false;//判断0的数量
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT >= 21) {//状态栏透明
View decorView = getWindow().getDecorView();
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
getWindow().setStatusBarColor(Color.TRANSPARENT);
}
setContentView(R.layout.activity_main);
ActionBar actionBar = getSupportActionBar();//隐藏标题栏
if (actionBar != null) {
actionBar.hide();
}
Button button_0 = (Button) findViewById(R.id.button_0);
Button button_1 = (Button) findViewById(R.id.button_1);
Button button_2 = (Button) findViewById(R.id.button_2);
Button button_3 = (Button) findViewById(R.id.button_3);
Button button_4 = (Button) findViewById(R.id.button_4);
Button button_5 = (Button) findViewById(R.id.button_5);
Button button_6 = (Button) findViewById(R.id.button_6);
Button button_7 = (Button) findViewById(R.id.button_7);
Button button_8 = (Button) findViewById(R.id.button_8);
Button button_9 = (Button) findViewById(R.id.button_9);
Button button_point = (Button) findViewById(R.id.button_point);
Button button_add = (Button) findViewById(R.id.button_add);
Button button_mul = (Button) findViewById(R.id.button_mul);
Button button_div = (Button) findViewById(R.id.button_div);
Button button_minus = (Button) findViewById(R.id.button_minus);
Button button_delete = (Button) findViewById(R.id.button_delete);
Button button_AC = (Button) findViewById(R.id.button_AC);
Button button_equal = (Button) findViewById(R.id.button_equal);
Button button_left_bracket = (Button) findViewById(R.id.button_left_bracket);
Button button_right_bracket = (Button) findViewById(R.id.button_right_bracket);
textView = (TextView) findViewById(R.id.textView);
//调用监听器
button_0.setOnClickListener(this);
button_1.setOnClickListener(this);
button_2.setOnClickListener(this);
button_3.setOnClickListener(this);
button_4.setOnClickListener(this);
button_5.setOnClickListener(this);
button_6.setOnClickListener(this);
button_7.setOnClickListener(this);
button_8.setOnClickListener(this);
button_9.setOnClickListener(this);
button_point.setOnClickListener(this);
button_add.setOnClickListener(this);
button_minus.setOnClickListener(this);
button_mul.setOnClickListener(this);
button_div.setOnClickListener(this);
button_equal.setOnClickListener(this);
button_add.setOnClickListener(this);
button_delete.setOnClickListener(this);
button_AC.setOnClickListener(this);
button_left_bracket.setOnClickListener(this);
button_right_bracket.setOnClickListener(this);
}
//数字输出
public String figure(int number, String str, boolean zero){
if (str.length() != 0 && str.charAt(str.length() - 1) == ')')
str+="*"+number;
else {
if (zero == true)
str=str.substring(0,str.length()-1);
str+=number;
}
return str;
}
@Override
public void onClick(View view) {
int number;
Character dig = '0';
String str = null;
str = textView.getText().toString();//获取textView里的字符串
AlertDialog.Builder dialog = new AlertDialog.Builder(MainActivity.this);//警告
if (str.length() == 0 || str.charAt(str.length() - 1) != '0') {//判断数字前零的存在。
zero = false;
}
switch (view.getId()) {
case R.id.button_0:
if (str.length() == 0 || str.charAt(str.length() - 1) != '/') {//判断除数是否为零
if (str.length() == 0 || (!dig.isDigit(str.charAt(str.length() - 1)) && str.charAt(str.length() - 1) != '.')) {
str += "0";
zero = true;
}
if (zero == false) {//清除多余的0
if (str.length() == 1 && str.charAt(0) == '0') {
zero = true;
break;
}
str += "0";
}
textView.setText(str);
} else {
dialog.setTitle("注意!");
dialog.setMessage("除数不能为0!");
dialog.setCancelable(false);
dialog.setPositiveButton("OK", new DialogInterface.
OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
// dialog. setNegativeButton( "Cancel",new DialogInterface .
// OnClickListener( ) {
// @Override
// public void onClick(DialogInterface dialog,int which) {
// }
// });
dialog.show();
}
break;
case R.id.button_1:
str = figure(1, str, zero);
textView.setText(str);
break;
case R.id.button_2:
str = figure(2, str, zero);
textView.setText(str);
break;
case R.id.button_3:
str = figure(3, str, zero);
textView.setText(str);
break;
case R.id.button_4:
str = figure(4, str, zero);
textView.setText(str);
break;
case R.id.button_5:
str = figure(5, str, zero);
textView.setText(str);
break;
case R.id.button_6:
str = figure(6, str, zero);
textView.setText(str);
break;
case R.id.button_7:
str = figure(7, str, zero);
textView.setText(str);
break;
case R.id.button_8:
str = figure(8, str, zero);
textView.setText(str);
break;
case R.id.button_9:
str = figure(9, str, zero);
textView.setText(str);
break;
case R.id.button_point:
if (str.length() > 0 && !dig.isDigit(str.charAt(str.length() - 1)))
break;
if (str.length() == 0) {
str += "0.";
} else {//遍历字符串,防止输入第二个”.“
int len = str.length() - 1;
while (len >= 0 && dig.isDigit(str.charAt(len))) {
len--;
}
if (len >= 0 && str.charAt(len) == '.') {
Toast.makeText(MainActivity.this, "不能再添加小数点!", Toast.LENGTH_SHORT).show();
break;
} else {
str += ".";
}
}
textView.setText(str);
break;
case R.id.button_add:
if(str.length() != 0 && str.charAt(str.length()-1)=='.')
str+="0"+"+";
if (str.length() != 0 && (str.charAt(str.length() - 1) == ')' || dig.isDigit(str.charAt(str.length() - 1)))) {//加号补全
if (brackets_add) {
str += ")+";
brackets_add = false;
} else
str += "+";
}
textView.setText(str);
break;
case R.id.button_minus:
if(str.length() != 0 && str.charAt(str.length()-1)=='.')
str+="0"+"-";
if (str.length() == 0 || str.charAt(str.length() - 1) == '(') {
minus_add = true;
str += "-";
} else if (str.charAt(str.length() - 1) == ')' || dig.isDigit(str.charAt(str.length() - 1))) {
if (brackets_add) {
str += ")-";
left_brackets_flag--;
brackets_add = false;
} else {
str += "-";
minus_add = false;
}
} else if (str.length() - 2 >= 0 &&
(dig.isDigit(str.charAt(str.length() - 2)) || str.charAt(str.length() - 2) == ')')) {//负数补全
str += "(-";
left_brackets_flag++;
brackets_add = true;
minus_add = true;
}
textView.setText(str);
break;
case R.id.button_mul:
if(str.length() != 0 && str.charAt(str.length()-1)=='.')
str+="0"+"*";
if (str.length() != 0 && (str.charAt(str.length() - 1) == ')' || dig.isDigit(str.charAt(str.length() - 1)))) {
if (brackets_add) {
str += ")*";
left_brackets_flag--;
brackets_add = false;
} else {
str += "*";
}
}
textView.setText(str);
break;
case R.id.button_div:
if(str.length() != 0 && str.charAt(str.length()-1)=='.')
str+="0"+"/";
if (str.length() != 0 && (str.charAt(str.length() - 1) == ')' || dig.isDigit(str.charAt(str.length() - 1)))) {
if (brackets_add) {
str += ")/";
left_brackets_flag--;
brackets_add = false;
} else
str += "/";
}
textView.setText(str);
break;
case R.id.button_equal:
if (left_brackets_flag != 0) {
dialog.setTitle("注意!");
dialog.setMessage("未添加右括号!");
dialog.setCancelable(false);
dialog.setPositiveButton("OK", new DialogInterface.
OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
dialog.show();
break;
}
if (str.length() == 0)
break;
if (brackets_add) {
str += ")";
left_brackets_flag--;
brackets_add = false;
}
String s = InfixToSuffix.Suffix(InfixToSuffix.Infix(str));
textView.setText(s);
str = s;
break;
case R.id.button_left_bracket:
if (str.length() == 0) {
str += "(";
left_brackets_flag++;
} else if (str.charAt(str.length() - 1) != ')') {
if (!dig.isDigit(str.charAt(str.length() - 1)))
str += "(";
else {
str += "*(";
brackets_add = true;
}
left_brackets_flag++;
}
textView.setText(str);
break;
case R.id.button_right_bracket:
if (str.length() != 0 && left_brackets_flag > 0 && (str.charAt(str.length() - 1) == ')'
|| dig.isDigit(str.charAt(str.length() - 1)))) {
if (brackets_add) {
brackets_add = false;
}
str += ")";
left_brackets_flag--;
}
textView.setText(str);
break;
case R.id.button_AC:
str = "";
left_brackets_flag = 0;
brackets_add = false;
textView.setText(str);
break;
case R.id.button_delete:
if (str.length() == 0 || str.length() == 1) {
str = "";
left_brackets_flag = 0;
} else {
if (str.charAt(str.length() - 1) == '(') {
if (brackets_add)
brackets_add = false;
left_brackets_flag--;
} else if (str.charAt(str.length() - 1) == ')')
left_brackets_flag++;
str = str.substring(0, str.length() - 1);
if (str.charAt(str.length() - 1) == '-' && minus_add)
str = str.substring(0, str.length() - 1);
}
textView.setText(str);
break;
}
}
}
首先,初始化一个队列和一个栈,其中队列用于存储结果,栈用来存储运算符,再new一个StringBuilder
用来储存数字。
List<String> list= new ArrayList<>();//储存中间结果结果队列
Stack<Character> stack= new Stack<>();//运算符栈
StringBuilder sb = new StringBuilder("");//储存数字
遍历字符串
遇到数字直接添加到队列中。
遇到运算符与stack栈顶元素比较优先级
遇到括号
最后输入到队列内即可。
代码如下:
List<String> list = new ArrayList<>();//储存中间结果结果队列
Stack<Character> stack = new Stack<>();//运算符栈
StringBuilder sb = new StringBuilder("");//储存数字
for (int i = 0; i < str.length(); i++) {
// 如果是数字
if ((i == 0 && str.charAt(i) != '(') || (i != 0 && isDigit(str.charAt(i), str.charAt(i - 1)))) {
sb.append(str.charAt(i));
// 如果是最后一位 或者下一位是字符,数字添加到队列,sb清空
if (i == str.length() - 1 || (i + 1 < str.length() && isSymbol(str.charAt(i + 1)))) {
list.add(sb.toString());
sb = new StringBuilder("");
}
// 如果是括号
} else if (isBracket(str.charAt(i))) {
// 如果是左括号 直接入栈
if (str.charAt(i) == '(') {
stack.push(str.charAt(i));
} else {
// 右括号
// 将元素出栈 添加到list直到遇到'(',将这一对 '(' ')' 舍去
char temp;
while ((temp = stack.pop()) != '(') {
list.add(temp + "");
}
}
// 如果是操作符
} else if (isOperation(str.charAt(i))) {
while (true) {
// 空的栈直接入栈
if (stack.isEmpty()) {
stack.push(str.charAt(i));
break;
// 如果栈顶的符号优先级小于 扫描到的符号 入栈
} else if (getPriority(stack.peek()) < getPriority(str.charAt(i))) {
stack.push(str.charAt(i));
break;
// 栈顶的符号优先级大于等于 扫描到的符号 出栈给list,并继续扫描栈顶下一个符号
} else {
list.add(stack.pop() + "");
}
}
}
}
// 将剩余的符号全部入list
while (!stack.isEmpty()) {
list.add(stack.pop() + "");
}
return list;
}
// 获取优先级
public static int getPriority(char ch) {
switch (ch) {
case '(':
return 0;
case '+':
case '-':
return 1;
case '*':
case '/':
return 2;
}
throw new RuntimeException("Error");
}
//符号判断
public static boolean isSymbol(char ch) {
return isOperation(ch) || isBracket(ch);
}
//操作符
public static boolean isOperation(char ch) {
return ch == '+' || ch == '-' || ch == '*' || ch == '/';
}
//括号判断
public static boolean isBracket(char ch) {
return ch == '(' || ch == ')';
}
public static boolean isDigit(char ch, char leftBracket) {
// 如果前一个是左括号 右边的数字可能带有正负号
if (leftBracket == '(') {
return ch == '-' || ch == '+' || (ch >= 48 && ch <= 57);
}
// 前一个符号不是左括号只能是数字或小数点
return (ch >= 48 && ch <= 57) || ch == '.';
}
新建一个栈,遍历字符串,遇到运算数进栈,遇到运算符,则在两数之间做运算。
//后缀表达式
public static String Suffix(List<String> suffixExp) {
Stack<BigDecimal> numStack = new Stack<>();
for (String str : suffixExp) {
// 如果是操作符
if (str.length() == 1 && isOperation(str.charAt(0))) {
BigDecimal num2 = numStack.pop();
BigDecimal num1 = numStack.pop();
numStack.push(calcValueOfTwoNum(num1, num2, str));
} else {
numStack.push(new BigDecimal(str));
}
}
return String.valueOf(numStack.peek());
}
//后缀表达式计算
public static BigDecimal calcValueOfTwoNum(BigDecimal num1, BigDecimal num2, String op) {
switch (op) {
case "+":
return num1.add(num2);
case "-":
return num1.subtract(num2);
case "*":
return num1.multiply(num2);
case "/":
if (num2.signum() == 0) {
throw new RuntimeException("Error");
}
// 除法保留2位小数,四舍五入
return num1.divide(num2, 2, RoundingMode.HALF_UP);
}
throw new RuntimeException("Error");
}
项目源代码github地址:
Android Calculator
参考文章:
android设置透明状态栏