計算器
- 前言
- 界面設計
- 界面展示
- 整體框架
- 按鈕布局
- 應用名字圖示
- MainActivity
- 核心演算法
- 中綴轉后綴運算式
- 后綴運算式計算
前言
一個模仿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/"引入圖片檔案即可,
MainActivity
應該考慮到的例外
- 輸入多個0只顯示一個
- .4自動補充為0.4
- 1(優化為1*(
- 1.1.1不允許輸入第二個小數點
- 除零例外
- 右括號未添加提醒
- 輸入-自動顯示為(-
- 4.3優化為4.03
代碼如下
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設定透明狀態欄
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/291520.html
標籤:其他
上一篇:Flutter 的 runApp 與三棵樹誕生流程原始碼分析
下一篇:華為手機獲取root權限
