1.完成计算器布局
整个计算器界面主要分为两部分,一部分是上面的文本框,用于显示计算结果;另一部分是下面的几排按钮,用户输入数字与各种运算符。为了减少复杂度,我们可以精简一些功能,只保留数字与加、减、乘、除四则运算,另外补充一个开根号(求平方根)的运算。至于App的显示界面,基本与习惯的计算器界面保持一致,经过对操作按钮的适当排列,调整后的设计效果如下图所示:
完成此界面用到了以下控件:
● 线性布局LinearLayout:计算器界面整体上是从上往下布局的,所以需要垂直方向的LinearLayout;下面部分每行都有4个按钮,又需要水平方向的LinearLayout。
● 滚动视图ScrollView:虽然计算器界面不宽也不高,但是以防万一,最好还是加个垂直方向的ScrollView。
● 文本视图TextView:很明显上方标题“简单计算器”就是TextView,下面的计算结果也需要使用TextView,而且是能够自动从下往上滚动的TextView,可参考前文聊天室效果的文本视图。
● 按钮Button:绝大多数数字与运算符按钮都采用Button控件。
● 图像按钮ImageButton:开根号的运算符“√”虽然能够打出来,但是右上角少了一横,所以该按钮要用一张标准的开根号图片显示,这就用到了ImageButton。
● 九宫格图片:注意计算器界面左下角的“0”,该按钮是其他按钮的两倍宽,如果使用普通图片当背景,势必会造成边缘线被拉宽、拉模糊的问题。故而要采用点九图片避免这种问题。
布局具体实现如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_height="match_parent"android:layout_width="match_parent"android:orientation="vertical"><TextViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:textSize="30sp"android:text="简单计算器" /><TextViewandroid:id="@+id/tv_result"android:layout_width="match_parent"android:layout_height="200dp"android:scrollbars="vertical"android:textSize="25sp"android:background="@drawable/textview_border"/><LinearLayoutandroid:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"android:orientation="horizontal"><Buttonandroid:text="CE"android:id="@+id/btn_cancel"style="@style/btn_cal" /><Buttonandroid:text="÷"android:id="@+id/btn_divide"style="@style/btn_cal" /><Buttonandroid:text="×"android:id="@+id/btn_multiply"style="@style/btn_cal" /><Buttonandroid:text="C"android:id="@+id/btn_clear"style="@style/btn_cal" /></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"android:orientation="horizontal"><Buttonandroid:text="7"android:id="@+id/btn_seven"style="@style/btn_cal" /><Buttonstyle="@style/btn_cal"android:id="@+id/btn_eight"android:text="8" /><Buttonandroid:text="9"android:id="@+id/btn_nine"style="@style/btn_cal" /><Buttonandroid:text="+"android:id="@+id/btn_plus"style="@style/btn_cal" /></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"android:orientation="horizontal"><Buttonandroid:text="4"android:id="@+id/btn_four"style="@style/btn_cal" /><Buttonstyle="@style/btn_cal"android:id="@+id/btn_five"android:text="5" /><Buttonandroid:text="6"android:id="@+id/btn_six"style="@style/btn_cal" /><Buttonandroid:text="-"android:id="@+id/btn_minus"style="@style/btn_cal" /></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"android:orientation="horizontal"><Buttonandroid:text="1"android:id="@+id/btn_one"style="@style/btn_cal" /><Buttonstyle="@style/btn_cal"android:id="@+id/btn_two"android:text="2" /><Buttonandroid:text="3"android:id="@+id/btn_three"style="@style/btn_cal" /><ImageButtonandroid:id="@+id/btn_sqrt"android:src="@drawable/radical"android:scaleType="centerInside"style="@style/btn_cal" /></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"android:orientation="horizontal"><Buttonandroid:text="0"android:id="@+id/btn_zero"android:layout_weight="2"style="@style/btn_cal" /><Buttonandroid:text="."android:id="@+id/btn_dot"style="@style/btn_cal" /><Buttonandroid:text="="android:id="@+id/btn_equal"style="@style/btn_cal" /></LinearLayout>
</LinearLayout>
2.设置按钮风格、背景图片等
给按钮统一样式:
<?xml version="1.0" encoding="utf-8"?>
<resources><style name="btn_cal"><item name="android:layout_width">0dp</item><item name="android:layout_height">match_parent</item><item name="android:layout_weight">1</item><item name="android:gravity">center</item><item name="android:textColor">@color/black</item><item name="android:textSize">30sp</item><item name="android:background">@drawable/btn_bg</item><item name="android:top">3dp</item><item name="android:left">3dp</item><item name="android:right">3dp</item><item name="android:bottom">3dp</item></style>
</resources>
为计算结果框设置边框
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"><item android:left="-1dp" android:right="-1dp"> <!--消除左边框和右边框--><shape android:shape="rectangle" ><solid android:color="#FFFFFF" /><stroke android:width="1dp" android:color="#000000"/></shape></item>
</layer-list>
3.完成计算器功能
package com.example.exercise;import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import java.math.BigDecimal;class Arith {//默认除法运算精度private static final int DEF_DIV_SCALE = 10;//构造器私有,让这个类不能实例化private Arith(){}//提供精确的加法运算public static double add(double v1,double v2){BigDecimal b1 = BigDecimal.valueOf(v1);BigDecimal b2 = BigDecimal.valueOf(v2);return b1.add(b2).doubleValue();}//提供精确的减法运算public static double sub(double v1,double v2){BigDecimal b1 = BigDecimal.valueOf(v1);BigDecimal b2 = BigDecimal.valueOf(v2);return b1.subtract(b2).doubleValue();}//提供精确的乘法运算public static double mul(double v1,double v2){BigDecimal b1 = BigDecimal.valueOf(v1);BigDecimal b2 = BigDecimal.valueOf(v2);return b1.multiply(b2).doubleValue();}//提供精确的除法运算public static double div(double v1,double v2){BigDecimal b1 = BigDecimal.valueOf(v1);BigDecimal b2 = BigDecimal.valueOf(v2);return b1.divide(b2).doubleValue();}
}public class CalculatorActivity extends AppCompatActivity implements View.OnClickListener{private String operator = ""; //操作符private String firstNum = ""; //前一个操作数private String nextNum = ""; //后一个操作数private String result = ""; // 当前计算结果private String showText = ""; //显示的文本内容private TextView tv_result;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_calculator);tv_result = findViewById(R.id.tv_result);findViewById(R.id.btn_cancel).setOnClickListener(this);findViewById(R.id.btn_clear).setOnClickListener(this);findViewById(R.id.btn_equal).setOnClickListener(this);findViewById(R.id.btn_sqrt).setOnClickListener(this);findViewById(R.id.btn_divide).setOnClickListener(this);findViewById(R.id.btn_dot).setOnClickListener(this);findViewById(R.id.btn_eight).setOnClickListener(this);findViewById(R.id.btn_four).setOnClickListener(this);findViewById(R.id.btn_minus).setOnClickListener(this);findViewById(R.id.btn_multiply).setOnClickListener(this);findViewById(R.id.btn_nine).setOnClickListener(this);findViewById(R.id.btn_one).setOnClickListener(this);findViewById(R.id.btn_plus).setOnClickListener(this);findViewById(R.id.btn_seven).setOnClickListener(this);findViewById(R.id.btn_six).setOnClickListener(this);findViewById(R.id.btn_three).setOnClickListener(this);findViewById(R.id.btn_two).setOnClickListener(this);findViewById(R.id.btn_zero).setOnClickListener(this);findViewById(R.id.btn_five).setOnClickListener(this);}public void onClick(View v) {int resid = v.getId();String inputText;if (resid == R.id.btn_sqrt) {inputText = "√";} else {inputText = ((TextView) v).getText().toString();}Log.d("sqrt", "resid=" + resid + ",inputText=" + inputText);if (resid == R.id.btn_clear) {clear("0");} else if (resid == R.id.btn_cancel) {if (operator.equals("=") == true){Toast.makeText(this, "没有可取消的数字了", Toast.LENGTH_SHORT).show();return;}if (operator.equals("") == true) {if (firstNum.length() == 1) {firstNum = "0";} else if (firstNum.length() > 0) {firstNum = firstNum.substring(0, firstNum.length() - 1);} else if (showText == "0") {firstNum = "0";} else {Toast.makeText(this, "没有可取消的数字了", Toast.LENGTH_SHORT).show();}showText = firstNum;tv_result.setText(showText);} else {if (nextNum.length() == 1) {nextNum = "";} else if (nextNum.length() > 0) {nextNum = nextNum.substring(0, nextNum.length() - 1);} else {Toast.makeText(this, "没有可取消的数字了", Toast.LENGTH_SHORT).show();return;}showText = showText.substring(0, showText.length() - 1);tv_result.setText(showText);}} else if (resid == R.id.btn_equal) {if (operator.length() == 0 || operator.equals("=") == true) {Toast.makeText(this, "请输入运算符", Toast.LENGTH_SHORT).show();return;} else if (nextNum.length() <= 0) {Toast.makeText(this, "请输入数字", Toast.LENGTH_SHORT).show();return;}if(operator == "√"){if(convertToDouble(firstNum) < 0){Toast.makeText(this, "开根号的数值不能小于0", Toast.LENGTH_SHORT).show();return;}result = String.valueOf(Math.sqrt(convertToDouble(firstNum)));if((result.substring(result.length() - 2, result.length() - 1).equals(".") == true) && (result.substring(result.length() - 1).equals("0") == true)){result = result.substring(0, result.length() - 2);}operator = inputText;showText = showText + "=" + result;tv_result.setText(showText);} else if (calculate() == true) {operator = inputText;showText = showText + "=" + result;tv_result.setText(showText);} else {return;}} else if(resid == R.id.btn_minus){if(operator.length() == 0 || operator.equals("=") == true || operator.equals("√") == true){operator = inputText;showText = showText + operator;tv_result.setText(showText);} else if(operator.length() == 1) {operator = inputText;showText = showText.substring(0, showText.length()-1) + operator;tv_result.setText(showText);}else{Toast.makeText(this, "请输入数字", Toast.LENGTH_SHORT).show();return;}} else if(resid == R.id.btn_plus || resid == R.id.btn_divide || resid == R.id.btn_multiply){if(firstNum.length() <= 0){Toast.makeText(this, "请输入数字", Toast.LENGTH_SHORT).show();return;}if(operator.length() == 0 || operator.equals("=") == true || operator.equals("√") == true){operator = inputText;showText = showText + operator;tv_result.setText(showText);} else if(operator.length() == 1) {operator = inputText;showText = showText.substring(0, showText.length()-1) + operator;tv_result.setText(showText);}else{Toast.makeText(this, "请输入数字", Toast.LENGTH_SHORT).show();return;}} else if(resid == R.id.btn_sqrt){if(showText == "0" || firstNum.equals("0") == true){firstNum = showText = "";}operator = inputText;showText = showText + operator;tv_result.setText(showText);} else{if(operator.equals("=") == true){operator = "";firstNum = "";showText = "";}if(operator.equals("√")){firstNum = firstNum + inputText;nextNum = "";}if(resid == R.id.btn_dot){inputText = ".";}if(operator.equals("") == true){if(showText == "0" || firstNum.equals("0") == true){firstNum = showText = "";}firstNum = firstNum + inputText;} else {nextNum = nextNum + inputText;}showText = showText + inputText;tv_result.setText(showText);}}private double convertToDouble(String number){return Double.parseDouble(number);}private boolean calculate() {if (operator.equals("+") == true) {result = String.valueOf(Arith.add(convertToDouble(firstNum), convertToDouble(nextNum)));} else if (operator.equals("-") == true) {result = String.valueOf(Arith.sub(convertToDouble(firstNum), convertToDouble(nextNum)));} else if (operator.equals("×") == true) {result = String.valueOf(Arith.mul(convertToDouble(firstNum), convertToDouble(nextNum)));} else if (operator.equals("÷") == true) {if ("0".equals(nextNum)) {Toast.makeText(this, "被除数不能为零", Toast.LENGTH_SHORT).show();return false;} else {result = String.valueOf(Arith.div(convertToDouble(firstNum), convertToDouble(nextNum)));}}if((result.substring(result.length() - 2, result.length() - 1).equals(".") == true) && (result.substring(result.length() - 1).equals("0") == true)){result = result.substring(0, result.length() - 2);}firstNum = result;nextNum = "";return true;}private void clear(String text){showText = text;tv_result.setText(showText);operator = "";firstNum = "0";nextNum = "";result = "";}
}