前言
学习Android写的第一个app,前前后后用了近一周的时间,建议刚开始写的先预算好自己要处理的异常,最好全面详细的列出来,避免和我一样后期花费很大功夫进行各种预算处理。接下来分享一下我做的计算器。
真机测试如下
效果渲染
按钮点击变色
首先在Android Studio(app/src/main/res/drawable)文件夹中创建一个Drawable resource file,这里我创建的是change.xml,change1.xml(因为我的按钮背景有两个颜色所以创建两个),在Button添加背景时使用,修改代码如下
这里的drawable="@color/colorPrimary",可在res/values/colors.xml中添加你自己喜欢的,添加后可这样使用
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:drawable="@color/colorPrimary"
android:state_enabled="true" //接受触摸或者点击事件
android:state_pressed="true" //被点击时
/>
<item
android:drawable="@color/colorchange0"
android:state_enabled="true"
android:state_pressed="false"/> //未被点击时
</selector>
字体自适应显示框而改变大小
先看效果
这里建议使用autoSizeTextType=“uniform”//字体自适应功能一两行就搞定(做完才知道的,不懂得可百度)
我在这里是使用了多线程(网上看到如果直接用getLineCount()方法,获取的行数不准确),在多线性中获取getLineCount()获取此时的行数,然后经过一系列的判断设置setTextSize(),注意:这种方法在按C/del时要做判断
textView_1.post(new Runnable() {
@Override
public void run() {
int line = textView_1.getLineCount();
if(line==1 && flag3==0){
textView_1.setTextSize(80);
flag3 = 1; //这里是我定义的用于判断行数的全局变量
}
if(line==2 && flag1==0){
length1 = str.length()-1;
Log.d("mainActivity","length1"+ +length1);
textView_1.setTextSize(60);
flag1 = 1;
}
if(line==3 && flag2==0){
length2 = str.length()-1;
Log.d("mainActivity","length2"+ +length2);
textView_1.setTextSize(40);
flag2 = 1;
}
}
});
布局文件
这里我用的线性布局,相对简单,里面嵌套6个线性布局,对应布局中的文本区和六行按钮。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFFFFF"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="3">
<TextView
android:id="@+id/TextView_1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textSize="80dp"
android:maxLines="3"
android:gravity="bottom|right"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="horizontal"
android:layout_marginBottom="7dp"
android:layout_weight="1">
<Button
android:id="@+id/button_c"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginRight="7dp"
android:textColor="#00CC00"
android:background="@drawable/change1"
android:text="C"/>
<Button
android:id="@+id/button_divede"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginRight="7dp"
android:textColor="#00CC00"
android:background="@drawable/change1"
android:text="/"/>
<Button
android:id="@+id/button_multiply"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:textColor="#00CC00"
android:layout_marginRight="7dp"
android:background="@drawable/change1"
android:text="*"/>
<Button
android:id="@+id/button_del"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:textColor="#00CC00"
android:background="@drawable/change1"
android:text="Del"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="#FFFFFF"
android:layout_marginBottom="7dp"
android:layout_weight="1">
<Button
android:id="@+id/button_7"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/change"
android:layout_marginRight="7dp"
android:text="7"/>
<Button
android:id="@+id/button_8"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/change"
android:layout_marginRight="7dp"
android:text="8"/>
<Button
android:id="@+id/button_9"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginRight="7dp"
android:background="@drawable/change"
android:text="9"/>
<Button
android:id="@+id/button_reduce"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:textColor="#00cc00"
android:background="@drawable/change1"
android:text="-"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="7dp"
android:layout_weight="1">
<Button
android:id="@+id/button_4"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginRight="7dp"
android:background="@drawable/change"
android:text="4"/>
<Button
android:id="@+id/button_5"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginRight="7dp"
android:background="@drawable/change"
android:text="5"/>
<Button
android:id="@+id/button_6"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginRight="7dp"
android:background="@drawable/change"
android:text="6"/>
<Button
android:id="@+id/button_add"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:textColor="#00FF00"
android:background="@drawable/change1"
android:text="+"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="7dp"
android:layout_weight="1">
<Button
android:id="@+id/button_1"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginRight="7dp"
android:background="@drawable/change"
android:text="1"/>
<Button
android:id="@+id/button_2"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginRight="7dp"
android:background="@drawable/change"
android:text="2"/>
<Button
android:id="@+id/button_3"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginRight="7dp"
android:background="@drawable/change"
android:text="3"/>
<Button
android:id="@+id/button_point"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/change1"
android:textColor="#00CC00"
android:text="."/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="7dp"
android:layout_weight="1">
<Button
android:id="@+id/button_leftparenthesis"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginRight="7dp"
android:background="@drawable/change1"
android:textColor="#00CC00"
android:text="("/>
<Button
android:id="@+id/button_0"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginRight="7dp"
android:background="@drawable/change"
android:text="0"/>
<Button
android:id="@+id/button_rightparenthesis"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:textColor="#00CC00"
android:layout_marginRight="7dp"
android:background="@drawable/change1"
android:text=")"/>
<Button
android:id="@+id/button_equals"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:textColor="#00CC00"
android:background="@drawable/change1"
android:text="="/>
</LinearLayout>
</LinearLayout>
MainActivity
处理的异常
-
当输入“-”时,如果代表负号,此时,屏幕显示的应该是“(-”
-
对于a(b)这种形式,自动补成a*(b),对于(a)b,自动补成(a)*b
-
除零异常提醒
-
删除负号时,击一下del,删除两个字符
-
对于.a应补充成0.a
-
对于左右括号数不相等的,给予提示
-
对于输入异常的不予执行,如输入1.2后不能再输’.’ ,输入0000显示一个零
-
等等…
代码
package com.example.calculator;
import androidx.appcompat.app.AppCompatActivity;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private int flag1 = 0;
private int flag2 = 0;
private int flag3 = 0;
private int length1 = 0;
private int length2 = 0;
private int IShaveleftparenthesis = 0; //标记是否出现左括号
private boolean ISnegative_sign = false; //是否是负号
private boolean ISpiece = false; //是否要加(
private boolean ISmanyzero = true;
private String str;
private Button button_0;
private Button button_1;
private Button button_2;
private Button button_3;
private Button button_4;
private Button button_5;
private Button button_6;
private Button button_7;
private Button button_8;
private Button button_9;
private Button button_point; //"."
private Button button_add;
private Button button_reduce;
private Button button_multiply;
private Button button_divede;
private Button button_c; //"清除"
private Button button_del; //"删除"
private Button button_equals;
private Button button_leftparenthesis; //"("
private Button button_rightparenthesis; //")"
private TextView textView_1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
button_0 = (Button)findViewById(R.id.button_0);
button_1 = (Button)findViewById(R.id.button_1);
button_2 = (Button)findViewById(R.id.button_2);
button_3 = (Button)findViewById(R.id.button_3);
button_4 = (Button)findViewById(R.id.button_4);
button_5 = (Button)findViewById(R.id.button_5);
button_6 = (Button)findViewById(R.id.button_6);
button_7 = (Button)findViewById(R.id.button_7);
button_8 = (Button)findViewById(R.id.button_8);
button_9 = (Button)findViewById(R.id.button_9);
button_point = (Button)findViewById(R.id.button_point);
button_add = (Button)findViewById(R.id.button_add);
button_reduce = (Button)findViewById(R.id.button_reduce);
button_multiply = (Button)findViewById(R.id.button_multiply);
button_divede = (Button)findViewById(R.id.button_divede);
button_c = (Button)findViewById(R.id.button_c);
button_del = (Button)findViewById(R.id.button_del);
button_equals = (Button)findViewById(R.id.button_equals);
button_leftparenthesis = (Button)findViewById(R.id.button_leftparenthesis);
button_rightparenthesis = (Button)findViewById(R.id.button_rightparenthesis);
textView_1 = (TextView)findViewById(R.id.TextView_1);
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_reduce.setOnClickListener(this);
button_multiply.setOnClickListener(this);
button_divede.setOnClickListener(this);
button_equals.setOnClickListener(this);
button_c.setOnClickListener(this);
button_del.setOnClickListener(this);
button_leftparenthesis.setOnClickListener(this);
button_rightparenthesis.setOnClickListener(this);
}
@Override
public void onClick(View view) {
str = textView_1.getText().toString();
textView_1.post(new Runnable() {
@Override
public void run() {
int line = textView_1.getLineCount();
if(line==1 && flag3==0){
textView_1.setTextSize(80);
flag3 = 1;
}
if(line==2 && flag1==0){
length1 = str.length()-1;
Log.d("mainActivity","length1"+ +length1);
textView_1.setTextSize(60);
flag1 = 1;
}
if(line==3 && flag2==0){
length2 = str.length()-1;
Log.d("mainActivity","length2"+ +length2);
textView_1.setTextSize(40);
flag2 = 1;
}
}
});
if(str.length()==0 || str.charAt(str.length()-1)!= '0') {
ISmanyzero = true;
}
switch (view.getId()){
case R.id.button_0:
if(str.length()==0||str.charAt(str.length()-1)!='/'){
if(str.length()==0|| ( !Character.isDigit(str.charAt(str.length()-1)) && str.charAt(str.length()-1)!='.')) {
str += "0";
ISmanyzero = false;
}
if(ISmanyzero==true) {
if(str.length()==1&&str.charAt(0)=='0'){
ISmanyzero = false;
break;
}
str += "0";
}
textView_1.setText(str);
}else{
Toast.makeText(MainActivity.this,"不能除以0",Toast.LENGTH_SHORT).show();
}
break;
case R.id.button_1:
if(str.length()!=0&&str.charAt(str.length()-1)==')')
str+="*1";
else{
if(!ISmanyzero)
str = str.substring(0,str.length()-1);
str+="1";
}
textView_1.setText(str);
break;
case R.id.button_2:
if(str.length()!=0&&str.charAt(str.length()-1)==')')
str+="*2";
else{
if(!ISmanyzero)
str = str.substring(0,str.length()-1);
str+="2";
}
textView_1.setText(str);
break;
case R.id.button_3:
if(str.length()!=0&&str.charAt(str.length()-1)==')')
str+="*3";
else{
if(!ISmanyzero)
str = str.substring(0,str.length()-1);
str+="3";
}
textView_1.setText(str);
break;
case R.id.button_4:
if(str.length()!=0&&str.charAt(str.length()-1)==')')
str+="*4";
else{
if(!ISmanyzero)
str = str.substring(0,str.length()-1);
str+="4";
}
textView_1.setText(str);
break;
case R.id.button_5:
if(str.length()!=0&&str.charAt(str.length()-1)==')')
str+="*5";
else{
if(!ISmanyzero)
str = str.substring(0,str.length()-1);
str+="5";
}
textView_1.setText(str);
break;
case R.id.button_6:
if(str.length()!=0&&str.charAt(str.length()-1)==')')
str+="*6";
else{
if(!ISmanyzero)
str = str.substring(0,str.length()-1);
str+="6";
}
textView_1.setText(str);
break;
case R.id.button_7:
if(str.length()!=0&&str.charAt(str.length()-1)==')')
str+="*7";
else{
if(!ISmanyzero)
str = str.substring(0,str.length()-1);
str+="7";
}
textView_1.setText(str);
break;
case R.id.button_8:
if(str.length()!=0&&str.charAt(str.length()-1)==')')
str+="*8";
else{
if(!ISmanyzero)
str = str.substring(0,str.length()-1);
str+="8";
}
textView_1.setText(str);
break;
case R.id.button_9:
if(str.length()!=0&&str.charAt(str.length()-1)==')')
str+="*9";
else{
if(!ISmanyzero)
str = str.substring(0,str.length()-1);
str+="9";
}
textView_1.setText(str);
break;
case R.id.button_point:
if(str.length()>0 && !Character.isDigit(str.charAt(str.length()-1)) )
break;
if(str.length()==0){
str += "0.";
}
else{
int len = str.length()-1;
while(len>=0&&Character.isDigit(str.charAt(len)) ){
len--;
};
if(len>=0&&str.charAt(len)=='.')
break;
else {
str += ".";
}
}
textView_1.setText(str);
break;
case R.id.button_add:
if(str.length()!=0 && ( str.charAt(str.length()-1)==')' || Character.isDigit(str.charAt(str.length()-1))) ) {
if (ISpiece) {
str += ")+";
ISpiece = false;
} else
str += "+";
}
textView_1.setText(str);
break;
case R.id.button_reduce:
if(str.length()==0 || str.charAt(str.length()-1)=='('){
ISnegative_sign = true;
str+="-";
}
else if(str.charAt(str.length()-1)==')'||Character.isDigit(str.charAt(str.length()-1))){
if(ISpiece){
str+=")-";
IShaveleftparenthesis--;
ISpiece = false;
}else{
str+="-";
ISnegative_sign = false;
}
}else if( str.length()-2>=0 &&
( Character.isDigit(str.charAt(str.length()-2)) || str.charAt(str.length()-2)==')')) {
str += "(-";
IShaveleftparenthesis++;
ISpiece = true;
ISnegative_sign = true;
}
textView_1.setText(str);
break;
case R.id.button_multiply:
if(str.length()!=0 && ( str.charAt(str.length()-1)==')' || Character.isDigit(str.charAt(str.length()-1))) ) {
if (ISpiece) {
str += ")*";
IShaveleftparenthesis--;
ISpiece = false;
} else{
str += "*";
}
}
textView_1.setText(str);
break;
case R.id.button_divede:
if(str.length()!=0 && ( str.charAt(str.length()-1)==')' || Character.isDigit(str.charAt(str.length()-1))) ){
if(ISpiece){
str+=")/";
IShaveleftparenthesis--;
ISpiece = false;
}else
str+="/";
}
textView_1.setText(str);
break;
case R.id.button_equals:
if(IShaveleftparenthesis!=0){
Toast.makeText(MainActivity.this,"请补充右括号",Toast.LENGTH_SHORT).show();
break;
}
if(str.length()==0)
break;
if(ISpiece){
str+=")";
IShaveleftparenthesis--;
ISpiece = false;
}
try {
String s = Core_Algorithm.result2(Core_Algorithm.result1(str));
textView_1.setText(s);
str = s;
}catch (Exception p){
Toast.makeText(MainActivity.this,"输入错误",Toast.LENGTH_SHORT).show();
}
break;
case R.id.button_leftparenthesis:
if(str.length()==0) {
str+="(";
IShaveleftparenthesis ++;
}
else if( str.charAt(str.length()-1)!=')') {
if( !Character.isDigit(str.charAt(str.length()-1)) )
str+="(";
else{
if(ISpiece){
str+=")*(";
ISpiece = false;
}
else {
str += "*(";
ISpiece = true;
}
}
IShaveleftparenthesis ++;
}
textView_1.setText(str);
break;
case R.id.button_rightparenthesis:
if(str.length()!=0 && IShaveleftparenthesis >0 && ( str.charAt(str.length()-1)==')'
|| Character.isDigit(str.charAt(str.length()-1)) ) ) {
if(ISpiece){
ISpiece = false;
}
str += ")";
IShaveleftparenthesis --;
}
textView_1.setText(str);
break;
case R.id.button_c:
flag1 = flag2 = flag3 = 0;
str = "";
IShaveleftparenthesis = 0; //标记是否出现左括号
ISpiece = false; //是否要加(
textView_1.setText(str);
break;
case R.id.button_del:
if(str.length()==0||str.length()==1){
str = "";
IShaveleftparenthesis = 0;
}
else{
if(str.charAt(str.length()-1)=='('){
if(ISpiece)
ISpiece = false;
IShaveleftparenthesis--;
}
else if(str.charAt(str.length()-1)==')')
IShaveleftparenthesis++;
str = str.substring(0,str.length()-1);
if(str.charAt(str.length()-1)=='-'&& ISnegative_sign)
str = str.substring(0,str.length()-1);
}
if(str.length()<=length1) {
flag3 = 0;
flag1 = 0;
}
else if(str.length()<=length2) {
flag1 = 0;
flag2 = 0;
}
textView_1.setText(str);
break;
}
}
}
Core_Algorithm 核心算法
对计算结果的计算
一般采用中缀转后缀,后缀表达式计算法
对输出结果进行去零处理
在计算时我们一般采用BigDecimal来进行结果的计算,但是当出现除法时他的结果不会进行去零处理,也就是说会出现9/3 =3.000000000000000 这种情况,我这里用正则表达式匹配小数,然后完成去零操作
代码如下
package com.example.calculator;
import android.util.Log;
import java.math.BigDecimal;
import java.util.ArrayDeque;
import java.util.ArrayList;
public class Core_Algorithm {
public static ArrayList<String> result1(String formula){
//传入算式,计算结果返回
//中缀转后缀
ArrayDeque<String> ad = new ArrayDeque<>(); //存放操作符
ArrayList<String> list = new ArrayList<>(); //存放后缀表达式
String temp = "";
for(int i = 0; i < formula.length(); i++){
char c = formula.charAt(i);
if( (c=='-'&& i==0 ) || (c=='-'&& formula.charAt(i-1)=='(') ){
//temp+='-';
list.add("0"); //如果是负号,变成0-
ad.push("-");
}
else if(Character.isDigit(c)||c=='.'){
if(i==formula.length()-1) {
//如果最后有".",不做处理
if (c != '.')
temp += c;
list.add(temp);
}
else if(i==0 && c== '.'){
//如果左前面有小数点,变为0.c
temp+="0"+c;
}else
temp+=c;
}
else{
if(temp!=""){
list.add(temp);
temp = "";
}
if(c=='(')
ad.push(c+""); //遇到左括号直接入栈
//遇到右括号吧操作符栈中的运算符出栈并在存放表达式的栈中入栈,直到遇到"("
else if(c==')'){
while(!ad.isEmpty()&&!ad.peek().equals("(")){
list.add(ad.pop());
}
if(ad.peek().equals("("))
ad.pop();
}
//如果是加减乘除
else{
if(list.isEmpty()&&c!='-')
continue;
switch (c){
case '+':
case '-':
if(!ad.isEmpty() && priority(ad.peek())>=0){
//ad2.push(ad1.pop());
list.add(ad.pop());
ad.push(c+"");
}
else{
ad.push(c+"");
}
break;
case '*':
case '/':
ad.push(c+"");
break;
}
}
}
}
while(!ad.isEmpty()){
//ad2.push(ad1.pop());
list.add(ad.pop());
}
if(list.size()==1){
//如果只有一个运算数,变为+0
list.add("0");
list.add("+");
}
//Log.d("Core Algo",list.toString());
return list;
}
public static int priority(String p){
if(p.equals("*") || p.equals("/")){
return 1;
}else if(p.equals("+") || p.equals("-")){
return 0;
}
return -1;
}
//计算逆波兰表达式
public static String result2(ArrayList<String> list){
ArrayList<String> arr = new ArrayList<>();
for(int i = 0; i < list.size(); i++){
int j = arr.size();
switch (list.get(i)){
case "+":
//这里注意:remove后arr.size少一个所以第二个j-2可理解为arr.size()-1-1
BigDecimal a = new BigDecimal(arr.remove(j-2)).add(new BigDecimal(arr.remove(j-2)));
arr.add(String.valueOf(a));
break;
case "-":
BigDecimal b = new BigDecimal(arr.remove(j-2)).subtract(new BigDecimal(arr.remove(j-2)));
arr.add(String.valueOf(b));
break;
case "*":
BigDecimal c = new BigDecimal(arr.remove(j-2)).multiply(new BigDecimal(arr.remove(j-2)));
arr.add(String.valueOf(c));
break;
case "/":
BigDecimal d = new BigDecimal(arr.remove(j - 2)).divide
(new BigDecimal(arr.remove(j - 2)), 20, BigDecimal.ROUND_HALF_UP);
arr.add(String.valueOf(d));
break;
default:arr.add(list.get(i));
break;
}
}
String rt = arr.get(0);
if(rt.length()==1)
return rt;
if(rt.matches("^[\\\\+\\\\-]?([0-9]*[.][0-9]*)$")) {
//匹配是否是小数
int i = rt.length() - 1; //匹配成功的去除末尾的0
while (rt.charAt(i) == '0')
i--;
if (rt.charAt(i) == '.')
i--;
rt = rt.substring(0, i + 1);
}
return rt;
}
}