【Android实验】UI设计-Android计算器

时间:2021-02-27 03:26:39

实验目的

自主完成一个简单APP的设计工作,综合应用已经学到的Android UI设计技巧,重点注意合理使用布局

实验要求

  1. 完成一个计算器的设计,可以以手机自带的计算器为参考。设计过程中,注意考虑界面的美观性,不同机型的适应性,以及功能的完备性。
  2. 注意结合Activity的生命周期,考虑不同情况下计算器的界面状态。
  3. 如有余力,可以考虑实现一个高精度科学计算型的计算器。

实验过程

1. 界面设计

界面仿照自己手机中计算器界面进行了设计,采用的布局是线性布局,其中比较特殊的布局是最后两行的布局,由于“=”键占用两个位置,所以采用vertical和horizontal相互配合的方式进行。

界面的展示如下:

【Android实验】UI设计-Android计算器

界面代码(activity_caculator.xml):

<?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"> <LinearLayout
android:background="@drawable/a5"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="2"
android:orientation="vertical"
android:focusableInTouchMode="true"> <TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Calculator"
android:textStyle="italic"
android:textAlignment="center"
android:textSize="20sp" /> <EditText
android:id="@+id/et_input"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="0"
android:enabled="true"
android:textAlignment="textEnd"
android:textSize="30sp"
android:background="@null"/>
</LinearLayout> <LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="horizontal"
android:weightSum="4"> <Button style="?android:attr/buttonStyle"
android:id="@+id/btn_clear"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#FFE4C4"
android:text="C"
android:textAlignment="center"
android:textSize="20sp"/> <Button style="?android:attr/buttonStyle"
android:id="@+id/btn_divide"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#FFE4C4"
android:text="/"
android:textAlignment="center"
android:textSize="20sp"/> <Button style="?android:attr/buttonStyle"
android:id="@+id/btn_times"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#FFE4C4"
android:text="*"
android:textAlignment="center"
android:textSize="20sp"/> <Button
android:id="@+id/btn_delete"
style="?android:attr/buttonStyle"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#FFE4C4"
android:text="DEL"
android:textAlignment="center"
android:textSize="20sp" />
</LinearLayout> <LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="horizontal"
android:layout_weight="1"
android:weightSum="4"> <Button style="?android:attr/buttonStyle"
android:id="@+id/btn_7"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#FDF5E6"
android:text="7"
android:textAlignment="center"
android:textSize="20sp"/> <Button
android:id="@+id/btn_8"
style="?android:attr/buttonStyle"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#FDF5E6"
android:text="8"
android:textAlignment="center"
android:textSize="20sp" /> <Button style="?android:attr/buttonStyle"
android:id="@+id/btn_9"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#FDF5E6"
android:text="9"
android:textAlignment="center"
android:textSize="20sp"/> <Button style="?android:attr/buttonStyle"
android:id="@+id/btn_sub"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="#FFC0CB"
android:layout_weight="1"
android:text="-"
android:textAlignment="center"
android:textSize="20sp"/>
</LinearLayout> <LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="horizontal"
android:layout_weight="1"
android:weightSum="4"> <Button style="?android:attr/buttonStyle"
android:id="@+id/btn_4"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#FDF5E6"
android:text="4"
android:textAlignment="center"
android:textSize="20sp"/> <Button
android:id="@+id/btn_5"
style="?android:attr/buttonStyle"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#FDF5E6"
android:text="5"
android:textAlignment="center"
android:textSize="20sp" /> <Button
android:id="@+id/btn_6"
style="?android:attr/buttonStyle"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#FDF5E6"
android:text="6"
android:textAlignment="center"
android:textSize="20sp" /> <Button
android:id="@+id/btn_add"
style="?android:attr/buttonStyle"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#FFC0CB"
android:text="+"
android:textAlignment="center"
android:textSize="20sp" />
</LinearLayout> <LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="horizontal"
android:layout_weight="2"
android:weightSum="4"> <LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_weight="1"> <Button style="?android:attr/buttonStyle"
android:id="@+id/btn_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="#FDF5E6"
android:text="1"
android:textAlignment="center"
android:textSize="20sp"/> <Button style="?android:attr/buttonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="%"
android:textAlignment="center"
android:background="#FDF5E6"
android:layout_weight="1"
android:id="@+id/btn_remainder"
android:textSize="20sp"/>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_weight="1"> <Button style="?android:attr/buttonStyle"
android:id="@+id/btn_2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="#FDF5E6"
android:text="2"
android:textAlignment="center"
android:textSize="20sp"/> <Button style="?android:attr/buttonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="0"
android:textAlignment="center"
android:background="#FDF5E6"
android:layout_weight="1"
android:id="@+id/btn_0"
android:textSize="20sp"/>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_weight="1">
<Button style="?android:attr/buttonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="3"
android:textAlignment="center"
android:background="#FDF5E6"
android:layout_weight="1"
android:id="@+id/btn_3"
android:textSize="20sp"/> <Button
android:id="@+id/btn_point"
style="?android:attr/buttonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="#FDF5E6"
android:text="."
android:textAlignment="center"
android:textSize="20sp" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_weight="1">
<Button style="?android:attr/buttonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="="
android:textAlignment="center"
android:background="#FFC0CB"
android:layout_weight="1"
android:id="@+id/btn_equal"
android:textSize="20sp"/>
</LinearLayout> </LinearLayout>
</LinearLayout>

在界面设计的过程中,感觉android:layout_weight是一个比较重要的属性,整个界面的按比例排布都需要这个属性来决定,如果用绝对位置会导致翻转以后,计算器按钮无法按比例进行填充。

另外界面的一个特点就是没有了ActionBar,这个通过设置Style.xml中的parent属性,从而可以改变。

Style.xml文件如下:

<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="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>
</resources>

另外的app命令以及图标替换在AndroidManifest.xml文件中更改:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.administrator.calculator"> <application
android:allowBackup="true"
android:icon="@drawable/pjthis"
android:label="PjCalc"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
<activity android:name=".Caculator">
<intent-filter>
<action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application> </manifest>

修改APP名称在android:label属性中修改,APP图标在android:icon中修改,其中android:theme改为NoActionBar可以将头部的标题去掉,增大空间。在LinearLayout中修改android:backgroud可以对背景进行修改,这里换成了一个自定义背景。

2. 功能设计

进行事件响应的主体为:Calculator.java

对界面事件的获取以及处理主要由该文件处理:

代码如下:

package com.example.administrator.calculator;

import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText; public class Caculator extends AppCompatActivity implements View.OnClickListener { Button bt_0, bt_1, bt_2, bt_3, bt_4, bt_5, bt_6, bt_7, bt_8, bt_9;
Button bt_clear, bt_divide, bt_times, bt_delete, bt_add, bt_sub, bt_equal, bt_remainder, bt_point;
private EditText et_input; private StringBuilder fomula = new StringBuilder(); private void initP() {
bt_0 = (Button) findViewById(R.id.btn_0);
bt_1 = (Button) findViewById(R.id.btn_1);
bt_2 = (Button) findViewById(R.id.btn_2);
bt_3 = (Button) findViewById(R.id.btn_3);
bt_4 = (Button) findViewById(R.id.btn_4);
bt_5 = (Button) findViewById(R.id.btn_5);
bt_6 = (Button) findViewById(R.id.btn_6);
bt_7 = (Button) findViewById(R.id.btn_7);
bt_8 = (Button) findViewById(R.id.btn_8);
bt_9 = (Button) findViewById(R.id.btn_9);
bt_clear = (Button) findViewById(R.id.btn_clear);
bt_divide = (Button) findViewById(R.id.btn_divide);
bt_times = (Button) findViewById(R.id.btn_times);
bt_delete = (Button) findViewById(R.id.btn_delete);
bt_add = (Button) findViewById(R.id.btn_add);
bt_sub = (Button) findViewById(R.id.btn_sub);
bt_equal = (Button) findViewById(R.id.btn_equal);
bt_point = (Button) findViewById(R.id.btn_point);
bt_remainder = (Button) findViewById(R.id.btn_remainder);
et_input = (EditText) findViewById(R.id.et_input); et_input.setKeyListener(null); bt_0.setOnClickListener(this);
bt_1.setOnClickListener(this);
bt_2.setOnClickListener(this);
bt_3.setOnClickListener(this);
bt_4.setOnClickListener(this);
bt_5.setOnClickListener(this);
bt_6.setOnClickListener(this);
bt_7.setOnClickListener(this);
bt_8.setOnClickListener(this);
bt_9.setOnClickListener(this);
bt_add.setOnClickListener(this);
bt_clear.setOnClickListener(this);
bt_divide.setOnClickListener(this);
bt_times.setOnClickListener(this);
bt_delete.setOnClickListener(this);
bt_sub.setOnClickListener(this);
bt_remainder.setOnClickListener(this);
bt_point.setOnClickListener(this);
bt_equal.setOnClickListener(this); } @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_caculator);
initP();
} @Override
public void onClick(View v) {
int latest = 0;
if (fomula.length() != 0) {
latest = fomula.codePointAt(fomula.length() - 1);
}
else
latest = '.'; switch (v.getId()) {
case R.id.btn_0:
fomula = fomula.append("0");
et_input.setText(fomula);
break;
case R.id.btn_1:
fomula = fomula.append("1");
et_input.setText(fomula);
break;
case R.id.btn_2:
fomula = fomula.append("2");
et_input.setText(fomula);
break;
case R.id.btn_3:
fomula = fomula.append("3");
et_input.setText(fomula);
break;
case R.id.btn_4:
fomula = fomula.append("4");
et_input.setText(fomula);
break;
case R.id.btn_5:
fomula = fomula.append("5");
et_input.setText(fomula);
break;
case R.id.btn_6:
fomula = fomula.append("6");
et_input.setText(fomula);
break;
case R.id.btn_7:
fomula = fomula.append("7");
et_input.setText(fomula);
break;
case R.id.btn_8:
fomula = fomula.append("8");
et_input.setText(fomula);
break;
case R.id.btn_9:
fomula = fomula.append("9");
et_input.setText(fomula);
break;
case R.id.btn_sub:
if (latest >= '0' && latest <= '9')
fomula = fomula.append("-");
else {
if(latest != '.')
fomula.delete(fomula.length()-1, fomula.length());
fomula = fomula.append("-");
}
et_input.setText(fomula);
break;
case R.id.btn_add:
if (latest >= '0' && latest <= '9')
fomula = fomula.append("+");
else {
if(latest != '.')
fomula.delete(fomula.length()-1, fomula.length());
fomula = fomula.append("+");
}
et_input.setText(fomula);
break;
case R.id.btn_times:
if (latest >= '0' && latest <= '9')
fomula = fomula.append("*");
else {
if(latest != '.')
fomula = fomula.delete(fomula.length()-1,fomula.length());
fomula = fomula.append("*");
}
et_input.setText(fomula);
break;
case R.id.btn_divide:
if (latest >= '0' && latest <= '9')
fomula = fomula.append("/");
else {
if(latest != '.')
fomula.delete(fomula.length()-1, fomula.length());
fomula = fomula.append("/");
}
et_input.setText(fomula);
break;
case R.id.btn_delete:
if (fomula.length() > 0)
fomula = fomula.delete(fomula.length() - 1, fomula.length());
et_input.setText(fomula);
break;
case R.id.btn_clear:
fomula = fomula.delete(0, fomula.length());
et_input.setText(fomula);
break;
case R.id.btn_equal:
String ans="0";
if(fomula.length()>1){
InfixInToDuffix inf = new InfixInToDuffix();
try{
String a = inf.toSuffix(fomula);
System.out.println("out:");
System.out.println(a);
ans = inf.dealEquation(a);
fomula = fomula.delete(0,fomula.length());
fomula = fomula.append(ans);
}catch (Exception ex){
ans = "error";
fomula = fomula.delete(0,fomula.length());
}
}
et_input.setText(ans);
break;
case R.id.btn_point:
fomula = fomula.append(".");
et_input.setText(fomula);
break;
case R.id.btn_remainder:
if(latest >= '0' && latest <= '9'){
fomula.append("%");
}
else
{
if(latest != '.')
fomula.delete(fomula.length()-1,fomula.length());
fomula.append("%");
}
et_input.setText(fomula);
break;
}
}
}

声明各个按钮及部件,然后通过findViewById得到对应的对象,然后进行处理相应的处理。

其中有几个细节需要处理,否则会导致程序崩溃:

  • 符号处理。
    • 符号前边不能有符号。
  • 小数点处理。
    • 如果结果是整数,不要加小数点。
    • 小数点的连续使用,如1.2.3.5.1。
  • 运算符号如果出现在数字之前,需要特殊判断。
  • 多个符号连续出现问题。
    • 处理方式为将上一个符号顶替下来,换成最新的运算符。
  • 减号如果作为-负号的情况出现。

3. 运算处理

主要是通过两个栈,将中缀转化为后缀,然后进行求解。其中“*/”运算符的优先级应该与“%”优先级一致,但是这里让“%”优先级低于"*/",然后进行的处理。

package com.example.administrator.calculator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.lang.*;
import java.util.ArrayList;
import java.util.*;
/**
* Created by pprp on 2018/9/22.
*/ class InfixInToDuffix {
//使用集合定义好符号的运算优先级别
private static final Map<Character,Integer>basic =new HashMap<Character, Integer>();
static {
basic.put('-',1);
basic.put('+', 1);
basic.put('*', 3);
basic.put('/', 3);
basic.put('(', 0);
basic.put('%',2);
} //将中缀表达式转换为后缀表达式
public String toSuffix(StringBuilder infix){
List<String> queue = new ArrayList<String>();
List<Character> stack = new ArrayList<Character>(); char[] charArr = infix.substring(0,infix.length()).trim().toCharArray();
String standard = "*/+-()%";
char ch = '&';
int len = 0;
for (int i = 0; i < charArr.length; i++) {
ch = charArr[i];
if(Character.isDigit(ch)) {
len++;
}else if(ch == '.'){
len++;
}else if(standard.indexOf(ch) != -1) {
if(len > 0) {
queue.add(String.valueOf(Arrays.copyOfRange(charArr, i - len, i)));
len = 0;
}
if(ch == '(') {
stack.add(ch);
continue;
}
if (!stack.isEmpty()) {
int size = stack.size() - 1;
boolean flag = false;
while (size >= 0 && ch == ')' && stack.get(size) != '(') {
queue.add(String.valueOf(stack.remove(size)));
size--;
flag = true;
}
if(ch==')'&&stack.get(size) == '('){
flag = true;
}
while (size >= 0 && !flag && basic.get(stack.get(size)) >= basic.get(ch)) {
queue.add(String.valueOf(stack.remove(size)));
size--;
}
}
if(ch != ')') {
stack.add(ch);
} else {
stack.remove(stack.size() - 1);
}
}
if(i == charArr.length - 1) {
if(len > 0) {
queue.add(String.valueOf(Arrays.copyOfRange(charArr, i - len+1, i+1)));
}
int size = stack.size() - 1;
while (size >= 0) {
queue.add(String.valueOf(stack.remove(size)));
size--;
}
} }
String a = queue.toString();
return a.substring(1,a.length()-1);
} public String dealEquation(String equation){ String [] arr = equation.split(", ");
List<String> list = new ArrayList<String>(); for (int i = 0; i < arr.length; i++) {
int size = list.size();
switch (arr[i]) {
case "+": double a = Double.parseDouble(list.remove(size-2))+ Double.parseDouble(list.remove(size-2)); list.add(String.valueOf(a)); break;
case "-": double b = Double.parseDouble(list.remove(size-2))- Double.parseDouble(list.remove(size-2)); list.add(String.valueOf(b)); break;
case "*": double c = Double.parseDouble(list.remove(size-2))* Double.parseDouble(list.remove(size-2)); list.add(String.valueOf(c)); break;
case "/": double d = Double.parseDouble(list.remove(size-2))/ Double.parseDouble(list.remove(size-2)); list.add(String.valueOf(d)); break;
case "%": double e = Double.parseDouble(list.remove(size-2)) % Double.parseDouble(list.remove(size-2)); list.add(String.valueOf(e)); break;
default: list.add(arr[i]); break; //如果是数字 直接放进list中
}
}
return list.size() == 1 ? list.get(0) : "Fail" ;
}
}