学了一个学期的Java程序设计,终于在课程设计中实现了一个外观界面比较良好的计算器。
就基本的代数运算法则,设计一种简易的计算器操作界面。通过按钮在计算器的操作框中输入相应数据,实现基本的数字计算,并将计算结果显示在操作界面的操作框中。
(1) 该简易计算器可具有加减乘除法运算、正弦函数运算、余弦函数运算、正切函数运算、反正弦函数运算、反余弦函数运算、反正切函数运算、平方根运算、乘方运算、基本对数运算、阶乘运算、求余运算、角度—弧度转化运算等13种运算功能;
(2) 针对除数为0、对数真数小于0、反三角函数运算操作数不在定义域范围内、平方根运算操作数小于0等几大异常情况,程序可以实现相应的异常处理;
(3) 点击操作界面内的“DEL”键和“AC”键,可实现在操作框中单个字符删除和操作框清除的功能。
(1)由于教师给出的课程设计时间较短,所以把问题进行了简化,忽略了混合计算的功能,仅仅只能进行单步计算;
(2)针对误用键盘在计算器文本框内输入无效字符而引起的异常,本程序没有给出相应的异常处理机制;
(3)计算器图形化界面的设计还不够美观,缺少必要的高清logo。
(1)包
包括Main(主类所在包)和ProblemField(问题域类所在包)两大自定义包,以及java.awt.、java.awt.event.、java.swing.*等引用包,共计使用包4次。
(2)类
Main包:包括GUI类(用于图形用户界面搭建和控制按钮事件响应)、Main类(用于建立计算器的操作界面)、TextFieldMethod类(写入对关于JTextField操作的方法)
ProblemField包:包括OperatorSingle类(单目运算符类,用于单目运算符运算的数字计算)、OperatorDouble类(双目运算符类,用于双目运算符的数字计算)、OperationFactory类(就不同运算符建立不同的双目运算符类)。
各模块的层次结构可见下列截图(Calculator为此程序所在工程):
/***************************
* Author: Donald Shallwing
* Email: [email protected]
* Function: This file is used for GUI module design
* Date: 24/12/2018
***************************/
package Main;
import java.awt.*;
import ProblemField.*;
import java.awt.event.*;
import javax.swing.*;
public class GUI extends JFrame implements ActionListener{
private static final long serialVersionUID = 1L;
private JButton[][] button1 = new JButton[3][6];
private JButton[][] button2 = new JButton[4][5];
private JButton button = new JButton("±");
private JTextField inputField = new JTextField(12);
private JLabel jl2 = new JLabel
(" Constructed by");
private JLabel jl3 = new JLabel("Donald Shallwing");
private boolean isEqual;
private OperatorDouble op;
public GUI(){
setEqual(true);
Font font = new Font("Cambria Math",Font.PLAIN,30);
Font font1 = new Font("Cambria Math",Font.BOLD,70);
Font font2 = new Font("Comic Sans MS",Font.PLAIN,15);
Font font3 = new Font("Kunstler Script",Font.BOLD,42);
String[][] operator1 =
{{"X!","exp","/X","MOD","°—rad","%"},{"√","X²","X³","Xⁿ","log","ln"},
{"sin","cos","tan","arcsin","arccos","arctan"}};
String[][] operator2 =
{{"7","8","9","DEL","AC"},
{"4","5","6","×","÷"},
{"1","2","3","+","-"},
{"0",".","π","e","="}};
inputField.setFont(font1);
inputField.setForeground(Color.BLACK);
inputField.setHorizontalAlignment(SwingConstants.RIGHT);
button.setFont(font);
button.addActionListener(this);
jl2.setFont(font2);
jl3.setFont(font3);
inputField.setFont(font1);
int i,j;
for(i=0; i<3; i++){
for(j=0;j<6;j++){
button1[i][j]=new JButton(operator1[i][j]);
button1[i][j].setFont(font);
button1[i][j].addActionListener(this);
}
j=0;
}
for(i=0; i<4; i++){
for(j=0; j<5; j++){
button2[i][j]=new JButton(operator2[i][j]);
button2[i][j].setFont(font);
button2[i][j].addActionListener(this);
}
j=0;
}
JPanel jp[] = new JPanel[8];
JPanel jpLast = new JPanel(new GridLayout(1,3));
jp[0] = new JPanel(new GridLayout(1,1));
jp[0].add(inputField);
for(i=1;i<8;i++){
if(i<4){
jp[i]= new JPanel(new GridLayout(1,6));
}
else
{
jp[i]= new JPanel(new GridLayout(1,5));
}
}
for(i=0; i<3; i++){
for(j=0; j<6; j++){
jp[i+1].add(button1[i][j]);
}
j=0;
}
for(i=0; i<4; i++){
for(j=0; j<5; j++){
jp[i+4].add(button2[i][j]);
}
j=0;
}
jpLast.add(button);
jpLast.add(jl2);
jpLast.add(jl3);
Container c = this.getContentPane();
c.setLayout(new GridLayout(9,1));
for(i=0;i<8;i++){
c.add(jp[i]);
}
c.add(jpLast);
this.setTitle("Calculator");
this.setVisible(true);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setSize(800,800);
this.setResizable(false);
}
public void actionPerformed(ActionEvent e) {
String getButton = e.getActionCommand();
String text = inputField.getText();
if("0123456789".indexOf(getButton)!=-1){
inputField.setText(TextFieldMethod.addNumber
(getButton, text));
}
if(getButton.equals("AC")){
inputField.setText(TextFieldMethod.
clearTextField());
}
if(getButton.equals("DEL")){
inputField.setText(TextFieldMethod.deleteChar
(text));
}
if(getButton.equals(".")){
inputField.setText(TextFieldMethod.addPointer
(text));
}
if(getButton.equals("π")||getButton.equals("e")){
if(getButton.equals("π"))
inputField.setText(Math.PI+"");
else
inputField.setText(Math.E+"");
}
// calculation for OperatorSingle
if(" X! ± sin cos tan arctan exp °—rad % X² X³ √ arcsin arccos log ln /X"
.indexOf(getButton)!=-1){
if(text.equals("")){}
else{
char c = text.charAt(text.length()-1);
double number =0.0;
if("0123456789".indexOf(c)!=-1){
number = Double.parseDouble(text);
}
if(text.equals("π")||text.equals("e")){
if(text.equals("π")){
number = Math.PI;
}
if(text.equals("e")){
number = Math.E;
}
}
if("log ln".indexOf(getButton)!=-1){
if(number<=0)
inputField.setText("Error");
}
if("√".indexOf(getButton)!=-1){
if(number<0)
inputField.setText("Error");
}
if("X!".indexOf(getButton)!=-1){
if(text.indexOf(".")!=-1){
inputField.setText("Error");
}
}
if(getButton.equals("arcsin")){
if(number<-1||number>1)
inputField.setText("Error");
}
if(getButton.equals("arccos")){
if(number<-1||number>1)
inputField.setText("Error");
}
else{
OperatorSingle operator = new
OperatorSingle(number);
double result = operator.calculate(getButton);
inputField.setText(result+"");
}
}
}
//Calculation for OperatorDouble
if("MOD + - ÷ × Xⁿ".indexOf(getButton)!=-1&&text.equals("")==false){
op = OperationFactory.createOperate(getButton);
op.setNumberA(Double.parseDouble(inputField.getText()));
inputField.setText("");
}
else if(getButton.equals("=")){
if(text.equals("")==false)
op.setNumberB(Double.parseDouble(inputField.getText()));
if(text.equals("")){
inputField.setText("");
}
if(getButton.equals("MOD")&&op.getNumberB()<1.0){
JOptionPane.showMessageDialog(null,"MODNumber can't be smaller than 1.0!","提示",3);
inputField.setText("");
}
if(op!=null&&op.getNumberB()==0&&getButton.equals("÷")){
JOptionPane.showMessageDialog(null,"DivNumber can't be zero!","提示",3);
inputField.setText("");
}
else{
if(op!=null){
double result = op.getResult();
inputField.setText(Double.toString(result));
setEqual(true);
}
}
}
}
public boolean isEqual() {
return isEqual;
}
public void setEqual(boolean isEqual) {
this.isEqual = isEqual;
}
}
/*****************************
* Author: Donald Shallwing
* Email: [email protected]
* Function: This file is the compilation entrance in the whole project
*****************************/
package Main;
public class Main{
public static void main(String []args){
new GUI();
}
}
/****************************
* Author: Donald Shallwing
* Email: [email protected]
* Function: This file is used for processing the whole external events
* Date: 24/12/2018
****************************/
package Main;
public class TextFieldMethod {
public static String addPointer(String text){
if(text.equals("")==false){
for(int i=0;i
/****************************
* Author: Donald Shallwing
* Email: [email protected]
* Function: This file is used when events happen, create a new factory class for respond the events.
* Date: 24/12/2018
****************************/
package ProblemField;
public class OperationFactory {
public static OperatorDouble createOperate(String operate){
OperatorDouble operation = null;
if(operate.equals("+")){
operation = new Add();
}
if(operate.equals("-")){
operation = new Mins();
}
if(operate.equals("×")){
operation = new Time();
}
if(operate.equals("÷")){
operation = new Divide();
}
if(operate.equals("Xⁿ")){
operation = new Power();
}
if(operate.equals("MOD")){
operation = new MOD();
}
return operation;
}
}
/****************************
* Author: Donald Shallwing
* Email: [email protected]
* Function: This file is used for process the double operators
* Date: 24/12/2018
****************************/
package ProblemField;
public abstract class OperatorDouble{
protected double numberA,numberB;
public void setNumberA(double numberA){
this.numberA = numberA;
}
public double getNumberB() {
return numberB;
}
public void setNumberB(double numberB) {
this.numberB = numberB;
}
public double getNumberA() {
return numberA;
}
public abstract double getResult();
}
class Add extends OperatorDouble{
public double getResult(){
return this.numberA+this.numberB;
}
}
class Mins extends OperatorDouble{
public double getResult(){
return this.numberA-this.numberB;
}
}
class Time extends OperatorDouble{
public double getResult(){
return this.numberA*this.numberB;
}
}
class Divide extends OperatorDouble{
public double getResult(){
return this.numberA/this.numberB;
}
}
class Power extends OperatorDouble{
public double getResult(){
return Math.pow(this.numberA, this.numberB);
}
}
class MOD extends OperatorDouble{
public double getResult(){
if(numberB<1.0)
return numberB/0.0;
else
return (int)this.numberA%(int)this.numberB;
}
}
/****************************
* Author: Donald Shallwing
* Email: [email protected]
* Function: This file is used for process the single operators
* Date: 24/12/2018
****************************/
package ProblemField;
public class OperatorSingle {
private double number;
//Constructor
public OperatorSingle(double number) {
this.number = number;
}
public void setNumber(double number ){
this.number = number;
}
public OperatorSingle(){}
public int factorial(){
int time = 1;
for(int i=(int)number; i!=1.0; i--){
time = time*i;
}
if(number==0)
return 1;
return time;
}
public double calculate(String operator){
if(operator.equals("sin")){
if(number%Math.PI==0)
return 0.0;
else
return Math.sin(number);
}
if(operator.equals("cos")){
if(number%(0.5*Math.PI)==0)
return 0.0;
else
return Math.cos(number);
}
if(operator.equals("tan")){
if(number%Math.PI==0)
return 0;
else{
if(number%(Math.PI/2.0)!=0)
return Math.tan(number);
else
return number/0.0;
}
}
if(operator.equals("/X")){
if(number==0)
return 1.0/0.0;
else
return 1.0/number;
}
if(operator.equals("arcsin"))
return Math.asin(number);
if(operator.equals("arccos"))
return Math.acos(number);
if(operator.equals("arctan"))
return Math.atan(number);
if(operator.equals("log"))
return Math.log10(number);
if(operator.equals("ln"))
return Math.log(number);
if(operator.equals("√"))
return Math.sqrt(number);
if(operator.equals("X²"))
return number*number;
if(operator.equals("X³"))
return number*number*number;
if(operator.equals("X!"))
return this.factorial();
if(operator.equals("°—rad"))
return number*Math.PI/180.0;
if(operator.equals("exp"))
return Math.exp(number);
if(operator.equals("%"))
return number/100.0;
if(operator.equals("±"))
return number*(-1.0);
else
return 0.0;
}
}
按照上面各个.java文件的顺序排列,编译,之后显示出来的计算器界面如下:
编译前请注意:
1、eclipse平台的字符编码规则应该设置成UTF-8编码规则;
2、Windows系统内应该自带或已经安装有GUI.java 中提及的字体。
如没有满足上述两个要求,会导致编译之后显示的界面中出现乱码或产生不同于上述图片中的字体。
在这里简要说一下GUI.java 中程序的逻辑:
1、整个计算器的界面采用的是网格布局方式,并在每一行之间之间进行了网格布局的嵌套。整个计算器有九行按键,各行之间进行了网格布局;每一行的各个按键之间再进行一次网格布局,最后一行中,既有按键,也有本人的个性签名;
2、对于各个按键的处理,分单目运算符和双目运算符两类去处理,本计算器仅包含加减乘除、乘方、求余六种双目运算符。对于单目运算符,处理比较简单,当用户输入一个数之后,再去点击一下单目运算符按键,就会在文本框之中弹出结果;对于双目运算符,处理相对麻烦,当用户输入一个数之后,若再点击双目运算符,之前所输入的数会被清空,然后再会等待用户输入另一个数,输入完最后一个数最后,点击等于号,才可以在文本框中显示运算结果;
3、数字输入的部分,也有较为复杂的异常逻辑处理,当文本框之中输入了一数字之后,就不可以再输入其他非数字字符,当小数点第一次输入之后,就不可以再第二次输入小数点。如果在输入了数字字符之后,再输入‘e’、‘π’等字符,此时文本框会自动显示“0.0”,表示数字输入错误。
其他的源程序代码,都比较容易理解,就是利用继承与派生、接口等概念,设计类与方法,以满足GUI.java 中的运算要求。
整个程序的编译入口是Main.java 文件,要编译整个程序,就先得在eclipse平台里面打开Main.java。
本博客中的所有代码都是public & free 的,为了方便读者测试、改进或是继续创新,下面给出代码的github HTTPS 链接和 gitee HTTPS 链接。
github:
github链接
gitee:
gitee链接