本篇文章主要是提供思路,当然文章末尾也又提供了源代码。
代码也是写了几天,重要的理解,不是直接复制粘贴就交作业了。
转载请注明出处,尊重作者劳动成果。
目录
界面的设计:
事件的响应:
计算:
详细代码如下:
总结:
要制作一个简单的计算器,首先就是对于界面的设计,然后就是其功能的实现。
对于事件的实现大概就分下面几个步骤。
- 确定事件源和监听源
- 实现监听器的接口
- 将事件源注册到监听器
然后就一起进入代码的编写吧,我是分成了几个发放来编写功能,最后有最终的代码,可以直接运行,这要注意的是,直接粘贴的话,类名和文件名要一样哦。
界面的设计:
他最上面是要有一个文本框,中间要有很多的按钮,我这里大的框架是采用BorderLayout类布局管理器,其中他的NORTH(容器顶部)是添加了一个FlowLayout类布局管理器,FlowLayout里面是一个文本框,CENTER(容器的中间)是添加了一个GirdLayout类布局管理器,形成网格状的按钮,WEST(容器的左侧),EAST(容器的右侧),SOUTH(容器底部),这些是通过添加一个空白的标签来占位置的,让界面更加好看一点。
//创建显示器面板,采用默认的流布局final JPanel viewPanel =new JPanel();//创建显示器final JTextField textField=new JTextField();//创建按钮面板final JPanel buttonPanel=new JPanel();//创建网络布局管理器对象final GridLayout gridLayout=new GridLayout(0,4);//按钮里面的内容String [][]names= {{"**","ln","lg","clear"},{"sin","cos","tan","X"},{"PI","//","%","/"},{"7","8","9","*"},{"4","5","6","-"},{"1","2","3","+"},{"_/``","0",".","="}};//创建按钮对象JButton[][] buttons=new JButton[names.length][4];//创建左侧的占位标签final JLabel leftLabel=new JLabel();//创建右侧的占位标签final JLabel rightLabel=new JLabel();//创建下侧的占位标签final JLabel bottomLabel=new JLabel();
//初始化组件public void initModule() {//初始化显示器相关数据textField.setEditable(false);//设置显示器不可编辑textField.setHorizontalAlignment(SwingConstants.RIGHT);textField.setColumns(35);//调节文本框的宽度textField.setPreferredSize(new Dimension(500,40));//初始化面板按钮gridLayout.setVgap(10);//设置组件的水平间距gridLayout.setHgap(10);//设置组件的垂直间距//初始化按钮对象for(int row=0;row<names.length;row++) {for(int col=0;col<names[row].length;col++) {buttons[row][col]=new JButton(names[row][col]);//创建按钮}}//设置左侧标签的宽度leftLabel.setPreferredSize(new Dimension(10,0));//设置右侧标签的宽度rightLabel.setPreferredSize(new Dimension(10,0));//设置底部标签的宽度,组件的有高度,可以没宽度,和两边相反bottomLabel.setPreferredSize(new Dimension(0,10));}
//初始化面板public void initPanel() {//初始化组件initModule();viewPanel.add(textField);viewPanel.setPreferredSize(new Dimension(100,80));this.getContentPane().add(viewPanel,BorderLayout.NORTH);buttonPanel.setLayout(gridLayout);//按钮面板采用网络布局this.getContentPane().add(buttonPanel,BorderLayout.CENTER);//将按钮面板添加到窗体中间//把按钮添加到按钮面板中,虽然可以和初始化按钮写一起,但是便于理解还是把他们分开写了for(int row=0;row<names.length;row++) {for(int col=0;col<names[row].length;col++) {buttonPanel.add(buttons[row][col]);}}this.getContentPane().add(leftLabel,BorderLayout.WEST);this.getContentPane().add(rightLabel,BorderLayout.EAST);this.getContentPane().add(bottomLabel,BorderLayout.SOUTH);}
//初始化窗体public void initFrame() {//设置窗体的标题this.setTitle("计算器");//设置窗体大小不可改变this.setResizable(false);//设置界面置顶(就是页面不会别其他页面覆盖,界面始终在最上面)this.setAlwaysOnTop(true);//设置窗体的位置和大小,位置应该失效了,因为设置了居中//this.setBounds(300,150,400,500);//那还是改成setSize吧,设置窗体的大小就行了this.setSize(400,500);//居中this.setLocationRelativeTo(null);//设置窗体关闭按钮的动作作为退出this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//将显示器面板添加到窗体顶部this.getContentPane().add(viewPanel,BorderLayout.NORTH);}
遇到的问题:
问题1:上层的文本框的大小的宽度设置不了,然后一直没有变化。
解决:通过修改setColumns();调节成功了,这个我还不知道原理,有知道的希望大家可以告诉我呢,之前一直在修改setPreferredSize();然后没反应,修改流布局管理器也只是把空白的地方变多了。
问题2:底部宽度bottomLabel.setPreferredSize(new Dimension(10,0));用这个修改没反应。
解决:这个当然不会有反应,添加加底部的宽是10,高是0,结果当然不会显示,之前偷懒直接复制左右侧的导致没显示,所以应该是bottomLabel.setPreferredSize(new Dimension(0,10));才对。
事件的响应:
Java | swing 如何清空JTextField中的内容_如何清空jtextfield的数据_黄佳俊、的博客-CSDN博客情况描述:在一个JTextField中输入了数据,用一个按钮来清空里面的数据,要如何实现啊!首先要说的是:没有这个方法,clear,能设置JTextField内容为空但是可以这样巧妙地做到:使用jTextField.setText(“”);把内容替换为空字符串,来实现清空JTextField中的内容…https://blog.csdn.net/weixin_48419914/article/details/121470250解决java添加/点击JButton后键盘监听无效的问题_QASWINE的博客-CSDN博客问题:在点击JButton后,原来可以用的键盘操作用不了了原因:点击JButton,焦点在按钮上,原来的有键盘监听器的组件(JFrame、-JPanel)失去焦点解决方法:如果键盘监听器在frame上,添加 frame.requestFocus(),使他重获焦点。下面是一个例子: button.addActionListener(new ActionListener() { …https://blog.csdn.net/qq_33831360/article/details/103280448Java 键盘事件无效的几种原因_mapcontrol.addkeylistener(_imonkeyi的博客-CSDN博客..https://blog.csdn.net/imonkeyi/article/details/86177348
具体的方法就看方法内部的调用吧,有应该都有写方法的作用的,接下来就是确定事件源和监听源,我是打算通过键盘输入一些字符和通过鼠标点击按钮来实现。
1.确定事件源和监听源
事件源:JButton[][] buttons=new JButton[names.length][4];按钮,和自己本身框架
监听源:ActionListener,KeyListener
2.实现监听器的接口
//重写鼠标点击事件@Overridepublic void actionPerformed(ActionEvent e) {// TODO Auto-generated method stubJButton button =(JButton)e.getSource();//获得触发此次动作事件的按钮对象String buttonName =e.getActionCommand();//获得触发此次动作事件的按钮的标签文本if(buttonName.equals("X")) {//没有字符串之后就不能删除了if(sb.length()!=0) {sb.deleteCharAt(sb.length()-1);//textField.setText(output);//删除之后还需要立即显示一次,不然没有反应}}else if(buttonName.equals("R")){//就是把[0,length)的内容删除即可sb.delete(0, sb.length());//删除之后还需要立即显示一次,不然没有反应//textField.setText(output);}else if(buttonName.equals("=")) {//计算结果result();}else {sb.append(buttonName);//textField.setText(output);}//反正每次响应事件之后都要更新,干脆直接放在最后outPut();//要重新使框架获得焦点,这要写呢,不写就按下按钮之后键盘就没反应了if(buttonName.equals("=")) {//就是把[0,length)的内容删除即可sb.delete(0, sb.length());//结果出来之后就是重新输入计算下一个}this.requestFocus();}
@Overridepublic void keyTyped(KeyEvent e) {// TODO Auto-generated method stub}//按着按键不松时调用@Overridepublic void keyPressed(KeyEvent e) {// TODO Auto-generated method stub}//按键松开后执行@Overridepublic void keyReleased(KeyEvent e) {// TODO Auto-generated method stub//System.out.println("我被调用了啦");int code = e.getKeyCode();//输出每次按下的键盘按钮对应的code//System.out.println(code);//用键盘添加数字//单纯输入数字,shift键没有被按下,不然就加减乘除无法被响应了if(code>=48 && code<=57 && !e.isShiftDown()) {sb.append((char)code);//outPut();}else if(code==56 && e.isShiftDown()) {sb.append("*");//outPut();}else if(code==47 && !e.isShiftDown()) {sb.append("/");}else if(code==8) {//Backspace键//删除最后的一个字符sb.deleteCharAt(sb.length()-1);}else if(code==53 && e.isShiftDown()) {sb.append("%");}else if(code==61 && e.isShiftDown()) {sb.append("+");}else if(code==61 && !e.isShiftDown()) {//"="//计算结果result();}else if(code==45 && !e.isShiftDown()) {sb.append("-");}else if(code==46 && !e.isShiftDown()) {sb.append(".");}else if(code==10) {//Enter键//计算结果result();}//每次键盘输入之后都要更新,所以干脆就直接放判断最后outPut();//"="和"Enter"键if(code==61 && !e.isShiftDown()||code==10) {//就是把[0,length)的内容删除即可sb.delete(0, sb.length());//结果出来之后就是重新输入计算下一个}}}
3.将事件源注册到监听器
public void buttonAction() {//按钮绑定动作事件,和自己绑定,自己实现的监听的方法for(int row=0;row<names.length;row++) {for(int col=0;col=48 && code<=57 && !e.isShiftDown()) {sb.append((char)code);//outPut();}else if(code==56 && e.isShiftDown()) {sb.append("*");//outPut();}else if(code==47 && !e.isShiftDown()) {sb.append("/");}else if(code==8) {//Backspace键//删除最后的一个字符sb.deleteCharAt(sb.length()-1);}else if(code==53 && e.isShiftDown()) {sb.append("%");}else if(code==61 && e.isShiftDown()) {sb.append("+");}else if(code==61 && !e.isShiftDown()) {//"="//计算结果result();}else if(code==45 && !e.isShiftDown()) {sb.append("-");}else if(code==46 && !e.isShiftDown()) {sb.append(".");}else if(code==10) {//Enter键//计算结果result();}//每次键盘输入之后都要更新,所以干脆就直接放判断最后outPut();//"="和"Enter"键if(code==61 && !e.isShiftDown()||code==10) {//就是把[0,length)的内容删除即可sb.delete(0, sb.length());//结果出来之后就是重新输入计算下一个}}});}public void buttonAction() {//按钮绑定动作事件,和自己绑定,自己实现的监听的方法for(int row=0;row<names.length;row++) {for(int col=0;col=48 && code<=57 && !e.isShiftDown()) {sb.append((char)code);//outPut();}else if(code==56 && e.isShiftDown()) {sb.append("*");//outPut();}else if(code==47 && !e.isShiftDown()) {sb.append("/");}else if(code==8) {//Backspace键//删除最后的一个字符sb.deleteCharAt(sb.length()-1);}else if(code==53 && e.isShiftDown()) {sb.append("%");}else if(code==61 && e.isShiftDown()) {sb.append("+");}else if(code==61 && !e.isShiftDown()) {//"="//计算结果result();}else if(code==45 && !e.isShiftDown()) {sb.append("-");}else if(code==46 && !e.isShiftDown()) {sb.append(".");}else if(code==10) {//Enter键//计算结果result();}//每次键盘输入之后都要更新,所以干脆就直接放判断最后outPut();//"="和"Enter"键if(code==61 && !e.isShiftDown()||code==10) {//就是把[0,length)的内容删除即可sb.delete(0, sb.length());//结果出来之后就是重新输入计算下一个}}});}
问题3:因为没有响应之后都要有对应的应答,有时候要输出,但是输入的按钮有些是含义多个字符的字符串,在计算和删除的时候删除的是单个的字符不是整个的字符串,比如sin输入的是“sin”删除只能删除最后的’n’。
解决:我这里是用到了HashMap来解决,在程序里面是单个的字符,输出是输出字符表示的字符串,这样删除计算等操作都方便了。
问题4:键盘输入对于*,+等的字符无法得到。
解决:我是通过e.getKeyCode();来得到键盘输入的code,但是对于*和8是同一个code,都是56,不同的是shift键是否按下,之前是想写在keyPressed里面来响应,但是感觉也实现不来,后面就是看到了e.isShiftDown();来判断shift键是否按下,也确实抱着试一试的态度,结果发现还真可以,就加上了e.isShiftDown();的判断。就解决了’*’和’8’有相同code的情况。
//按钮里面的内容String [][]names= {{"**","ln","lg","clear"},{"sin","cos","tan","X"},{"PI","//","%","/"},{"7","8","9","*"},{"4","5","6","-"},{"1","2","3","+"},{"_/``","0",".","="}};//程序里面的内容String [][]target= {{"A","N","G","R"},{"S","C","T","X"},{"P","B","%","/"},{"7","8","9","*"},{"4","5","6","-"},{"1","2","3","+"},{"D","0",".","="}};//用来对应文本框的输出,还是选择用HashMap来存储Mapmap=new HashMap();//通过哈希表实现输出字符串和里面的字符串的不同标识public void Stringbind() {//map.put(getWarningString(), getName())for(int row=0;row<names.length;row++) {for(int col=0;col<names[row].length;col++) {map.put(target[row][col], names[row][col]);//System.out.println("执行成功");}}//用来判断值有没有填写进去//System.out.println(map.size());}
问题5:在查找的时候输出的是null,不是目标的字符串。
解决:这我找了挺久的,最后还看了HashMap里面的元素有多少,发现是存进去的,问题出现在字符串“A”和’A’不是一样的东西,所以哈希表没找到。
输出是通过一个函数来实现
//用来更新文本框输出的字符串StringBuffer sb=new StringBuffer();//用来对应文本框的输出,还是选择用HashMap来存储Mapmap=new HashMap();//输出的字符串放这个里面,每次使用都需要清零哈String output="";//记录每次将输出的字符串public void outPut() {output="";for(int i=0;i<sb.length();i++) {//这我可能知道原因了,字符串"A"和'A'不一样output= output+map.get(String.valueOf(sb.charAt(i)));}//每次更新都要输出,我直接写到方法里面算了textField.setText(output);}
问题6:键盘的输入失灵。
解决:要通过聚焦,才能实现,调用this.setFocusable(true);
问题7:开始输入可以通过键盘输出,但是在按下面板中的按钮之后键盘无法输入。
解决:在每次按下按钮之后重新聚焦this.requestFocus();要重新使框架获得焦点,这要写呢,不写就按下按钮之后键盘就没反应了。
最后就是计算器功能实现的部分了,比较好实现的就是除‘=’号的按钮,直接添加到字符串里面,在最后打印就行。我这里采用的是StringBuffer因为要添加和更新什么的。
删除按钮和清空按钮分别就是删除字符串最后一个字符和删除字符串所有的字符。
//删除最后一个字符if(sb.length()!=0) {sb.deleteCharAt(sb.length()-1);//textField.setText(output);//删除之后还需要立即显示一次,不然没有反应}//删除所有的字符,确定是范围是[0,length)sb.delete(0, sb.length());
问题8:按下按钮之后没有显示删除。
解决:这个是因为没有显示,在程序中已经删除了,但是呢,用户看到是还没删除的样子,所以还是要更新一下输出。
计算:
java 实现字符串算式的计算_段593405572的博客-CSDN博客输入一个字符串(包括数字、小数点,加减乘除百分比),模拟计算器进行运算。 思路:按运算符优先级依次运算百分比、乘除、加减。遍历字符串,将运算符前或前后的数字字符转为double进行计算,计算后的结果替换原字符串中的子算式。 import java.util.Scanner;public class Main{public static double f(String s){double p = 0;//百分比计算for(int i = 0; i &…https://blog.csdn.net/d593405572/article/details/106713937
Java中角度和弧度的转换,三角函数,反三角函数_java角度转弧度-CSDN博客Java中角度和弧度的转换,三角函数,反三角函数https://blog.csdn.net/senxu_/article/details/126025606
接下来就是最后的计算环节了。
首先确定计算的优先级,我这里没有引入括号了,所以把lg,ln,sin,cos,tan,开根号都是和右边的操作数看成一个整体的。
字符串的运算就是通过运算符来分割,通过寻找到他的运算数,进行运算,把运算结果的字符串和运算前的字符串进行替换即可。
//计算每次的结果public void result() {//对应关系//**--->Aln--->Nlg--->Gsin--->S cos--->C//tan--->TPI--->P//--->B/``--->D//计算是按照优先级来的//PI不算运算,直接填进去吧//也不行,本来就是用一个符号来表示,这突然变成一长串数字,那还是运算的时候展开吧//展开PItry {pi();//ln,lg,sin,cos,tan,开根号的运算special();//乘方运算power();//乘除,整除,求余运算multiplyDivide();//加减计算addAndSubtract();}catch(Exception e) {//弹出警告的弹窗warning();}finally {System.out.println("今天又是元气满满的一天");}}
PI就是一个替换,把PI在计算的时候换成Math.PI就行了,因为我用哈希表的初衷就是希望在程序中字符串是用一个的字符表示,所以PI我是在计算的时候才进行展开。
//展开PIpublic void pi() {for(int i=0;i<sb.length();i++) {if(sb.charAt(i)=='P') {double res=Math.PI;String resString=res+"";//更新字符串sb=sb.replace(i, i+1, resString);i=resString.length()-1;continue;}}}
这些我不是用什么名字,就用这个表示了,这些的特点就是只需要一个操作数即可。
所以只需要往右边找操作数,统计操作数的长度,把找到的数进行相应的修改和替换。
问题9:sin和tan不好用,用PI直接报错了。
解决:这个弧度制好像是越界了,我又没加括号,那还是Math.toRadians(num)输入角度制转成弧度制来运算吧。
//ln,lg,sin,cos,tan,开根号的运算public void special() {//都是用右边一个操作数的运算for(int i=0;i<sb.length();i++) {if(sb.charAt(i)=='N'||sb.charAt(i)=='G'||sb.charAt(i)=='S'||sb.charAt(i)=='C'||sb.charAt(i)=='T'||sb.charAt(i)=='D') {double num=0;int len=0;//记录字符串长度,之后还要进行字符串的替换呢//只需要应该一边的数字即可for(int j=i+1;j<sb.length();j++) {//是j不是i,咋又错在这里了if(sb.charAt(j)=='*'||sb.charAt(j)=='/'||sb.charAt(j)=='%'||sb.charAt(j)=='B'||sb.charAt(j)=='+'||sb.charAt(j)=='-'||sb.charAt(j)=='A'||sb.charAt(j)=='N'||sb.charAt(j)=='G'||sb.charAt(j)=='S'||sb.charAt(j)=='C'||sb.charAt(j)=='T'||sb.charAt(j)=='D') {String s1=sb.substring(i+1,j);num=Double.parseDouble(s1);len=s1.length();break;}//找到最右边咯,中间没有运算符那也要停止了if(j==sb.length()-1) {//这边是到j+1哈,找错误找了半天//如果右边是一位的数字,就会导致运算是数是空//如果右边是多位的数字,那么最右边的数不会参与运算,所以需要[i+1,j+1)String s1=sb.substring(i+1,j+1);num=Double.parseDouble(s1);len=s1.length();break;}}//进行运算//ln运算if(sb.charAt(i)=='N') {double res=Math.log(num);String resString=res+"";//更新字符串sb=sb.replace(i, i+len+1, resString);i=resString.length()-1;continue;}//lg运算if(sb.charAt(i)=='G') {//换底公式double res=Math.log(num)/Math.log(10);String resString=res+"";//更新字符串sb=sb.replace(i, i+len+1, resString);i=resString.length()-1;continue;}//sin运算if(sb.charAt(i)=='S') {//还是采用角度制吧,弧度制取值用PI越界了,如果要除的话要加入括号的机制//偷懒一下就用角度值吧,不用加入括号double res=Math.sin(Math.toRadians(num));String resString=res+"";//更新字符串sb=sb.replace(i, i+len+1, resString);i=resString.length()-1;continue;}//cos运算if(sb.charAt(i)=='C') {//还是采用角度制吧,弧度制取值用PI越界了,如果要除的话要加入括号的机制//偷懒一下就用角度值吧,不用加入括号double res=Math.cos(Math.toRadians(num));String resString=res+"";//更新字符串sb=sb.replace(i, i+len+1, resString);i=resString.length()-1;continue;}//tan运算if(sb.charAt(i)=='T') {//还是采用角度制吧,弧度制取值用PI越界了,如果要除的话要加入括号的机制//偷懒一下就用角度值吧,不用加入括号double res=Math.tan(Math.toRadians(num));String resString=res+"";//更新字符串sb=sb.replace(i, i+len+1, resString);i=resString.length()-1;continue;}//开根号运算if(sb.charAt(i)=='D') {double res=Math.sqrt(num);String resString=res+"";//更新字符串sb=sb.replace(i, i+len+1, resString);i=resString.length()-1;continue;}}}}
接下来就是两个操作数的啦。以乘方为例。
往左边和右边寻找操作数,左边的话需要考虑优先级低的操作字符以及字符串越界的情况,但是右边不仅是需要考虑这个还需要考虑本身这个字符。
就是往左边判断是(sb.charAt(j)==’*’||sb.charAt(j)==’/’||sb.charAt(j)==’%’||sb.charAt(j)==’B’||sb.charAt(j)==’+’||sb.charAt(j)==’-‘),而右边判断是(sb.charAt(j)==’*’||sb.charAt(j)==’/’||sb.charAt(j)==’%’||sb.charAt(j)==’B’||sb.charAt(j)==’+’||sb.charAt(j)==’-‘||sb.charAt(j)==’A’),这也要理解吧,因为字符串吧遍历是从左往右,右边还有这个‘A’没有判断,但是左边已经判断结束了。
//乘方运算public void power(){for(int i=0;i=0;j--) {//得到第一个操作数,遇到加减乘除就停止了,相当于分隔了操作数if(sb.charAt(j)=='*'||sb.charAt(j)=='/'||sb.charAt(j)=='%'||sb.charAt(j)=='B'||sb.charAt(j)=='+'||sb.charAt(j)=='-') {String s1=sb.substring(j+1,i);num1=Double.parseDouble(s1);len1=s1.length();break;}//找到边界了,只能停止了咯if(j==0) {String s1=sb.substring(j,i);num1=Double.parseDouble(s1);len1=s1.length();break;}}//往右边找第二个操作数,第二个操作数的话遇到乘除也需要提前终止,第一个不需要//因为如果第一个操作数有乘除这种符号,就已经提前终止了,只有第二个操作数才需要考虑for(int j=i+1;j<sb.length();j++) {if(sb.charAt(j)=='*'||sb.charAt(j)=='/'||sb.charAt(j)=='%'||sb.charAt(j)=='B'||sb.charAt(j)=='+'||sb.charAt(j)=='-'||sb.charAt(j)=='A') {String s1=sb.substring(i+1,j);num2=Double.parseDouble(s1);len2=s1.length();break;}//找到最右边咯,中间没有运算符那也要停止了if(j==sb.length()-1) {//这边是到j+1哈,找错误找了半天//如果右边是一位的数字,就会导致运算是数是空//如果右边是多位的数字,那么最右边的数不会参与运算,所以需要[i+1,j+1)String s1=sb.substring(i+1,j+1);num2=Double.parseDouble(s1);len2=s1.length();break;}}//进行运算double res=Math.pow(num1, num2);String resString=res+"";//更新字符串sb=sb.replace(i-len1, i+len2+1, resString);i=i-len1-1+resString.length();continue;}}}
加减乘除就和这个一样了。
问题10:会出现多个异常抛出的情况。
解答:循环里面操作数的判断,里面是sb.charAt(j)写成了sb.charAt(i);第二种就是在第二个操作数边界判断的时候String s1=sb.substring(i+1,j+1);写成了String s1=sb.substring(i+1,j);这也是直接复制前面的结果,因为之前停止是遇到运算符停止的,所以边界判断不需要考虑到j+1,但是这已经到底了,指针指向的就是数,如果右边是一位的数字,就会导致运算是数是空,如果右边是多位的数字,那么最右边的数不会参与运算,所以需要[i+1,j+1)。
运算解决了,就是对异常的处理,之前的代码就有显示我加入了抛出异常的地方,如果错误是会出现一个弹窗。我还用finally来检验程序的健壮性。本来是对除数为0的情况加上了一个判断,因为是浮点数所以为0的判断条件设置成Math.abs(num2)<=1e-6,但是他没反应了,我干脆就直接不判断了这也算是异常,直接抛出弹出弹窗就行了。这还是方便一点。
注:这里有一个bug,因为操作数我最开始是设置成0,就导致如果只有一个数的时候也可以运算,比如“*1”是可以运行的,因为num1等于0,这个大家感兴趣可以自己完善一下哈。
//警告标签,放在弹窗里面final JLabel warningLabel=new JLabel();//警告,输入不合法的时候弹出public void warning() {JDialog jDialog=new JDialog();//创建弹窗对象jDialog.setTitle("警告");//设置弹窗标题,和Frame差不多,可能还要通过标签来提示jDialog.setSize(500,600);//设置弹窗的大小jDialog.setAlwaysOnTop(true);//让弹窗置顶jDialog.setLocationRelativeTo(null);//让弹窗居中jDialog.setModal(true);//弹窗不关闭则无法操作下面的界面//设置字体的类型,加粗,和大小warningLabel.setFont(new Font("Microsoft YaHei",Font.BOLD,30));//输出警告提示符warningLabel.setText("就是你小子在搞乱的是吧!!!");//标签的位置和大小warningLabel.setBounds(60,180,500,100);//这个也要取消布局管理器才行jDialog.getContentPane().setLayout(null);//往弹窗中添加标签jDialog.getContentPane().add(warningLabel);jDialog.setVisible(true);//让弹窗显示出来}
警告的弹窗和内容都是自己设计的,弹窗就和框架一样,里面也是一个面板,我取消了布局,通过x,y坐标的方法进行移动。弹窗弹出如下所示。
问题11:在打印按钮的时候出现数组越界
因为整个数组不是矩阵,所以内存循环和外循环不是同一个边界值,内层循环的界限是用外层循环对应一维数组的长度作为边界值.
详细代码如下:
import java.awt.*;import java.awt.event.*;import java.util.HashMap;import java.util.Map;import javax.swing.*;public class CalculatorFrame extends JFrame implements ActionListener{//主方法public static void main(String[] args) {CalculatorFrame cf=new CalculatorFrame();}//创建显示器面板,采用默认的流布局final JPanel viewPanel =new JPanel();//创建显示器final JTextField textField=new JTextField();//创建按钮面板final JPanel buttonPanel=new JPanel();//创建网络布局管理器对象final GridLayout gridLayout=new GridLayout(0,4);//按钮里面的内容String [][]names= {{"**","ln","lg","clear"},{"sin","cos","tan","X"},{"PI","//","%","/"},{"7","8","9","*"},{"4","5","6","-"},{"1","2","3","+"},{"_/``","0",".","="}};//程序里面的内容String [][]target= {{"A","N","G","R"},{"S","C","T","X"},{"P","B","%","/"},{"7","8","9","*"},{"4","5","6","-"},{"1","2","3","+"},{"D","0",".","="}};//创建按钮对象JButton[][] buttons=new JButton[names.length][4];//创建左侧的占位标签final JLabel leftLabel=new JLabel();//创建右侧的占位标签final JLabel rightLabel=new JLabel();//创建下侧的占位标签final JLabel bottomLabel=new JLabel();//存储计算结果double result=0;//用来更新文本框输出的字符串StringBuffer sb=new StringBuffer();//用来对应文本框的输出,还是选择用HashMap来存储Mapmap=new HashMap();//输出的字符串放这个里面,每次使用都需要清零哈String output="";//警告标签,放在弹窗里面final JLabel warningLabel=new JLabel();public CalculatorFrame(){//初始化窗体initFrame();//初始化面板initPanel();//初始化哈希表里面的数据Stringbind();//绑定事件.键盘绑定也放在里面buttonAction();//窗体可见,放最后吧,不然里面的东西不会显示呢this.setVisible(true);}//初始化窗体public void initFrame() {//设置窗体的标题this.setTitle("计算器");//设置窗体大小不可改变this.setResizable(false);//设置界面置顶(就是页面不会别其他页面覆盖,界面始终在最上面)this.setAlwaysOnTop(true);//设置窗体的位置和大小,位置应该失效了,因为设置了居中//this.setBounds(300,150,400,500);//那还是改成setSize吧,设置窗体的大小就行了this.setSize(400,500);//居中this.setLocationRelativeTo(null);//设置窗体关闭按钮的动作作为退出this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//将显示器面板添加到窗体顶部this.getContentPane().add(viewPanel,BorderLayout.NORTH);//添加屏幕焦点,没有这个用键盘输入没反应埃this.setFocusable(true);}//初始化面板public void initPanel() {//初始化组件initModule();viewPanel.add(textField);viewPanel.setPreferredSize(new Dimension(100,80));this.getContentPane().add(viewPanel,BorderLayout.NORTH);buttonPanel.setLayout(gridLayout);//按钮面板采用网络布局this.getContentPane().add(buttonPanel,BorderLayout.CENTER);//将按钮面板添加到窗体中间//把按钮添加到按钮面板中,虽然可以和初始化按钮写一起,但是便于理解还是把他们分开写了for(int row=0;row<names.length;row++) {for(int col=0;col<names[row].length;col++) {buttonPanel.add(buttons[row][col]);}}this.getContentPane().add(leftLabel,BorderLayout.WEST);this.getContentPane().add(rightLabel,BorderLayout.EAST);this.getContentPane().add(bottomLabel,BorderLayout.SOUTH);}//初始化组件public void initModule() {//初始化显示器相关数据textField.setEditable(false);//设置显示器不可编辑textField.setHorizontalAlignment(SwingConstants.RIGHT);textField.setColumns(35);//调节文本框的宽度textField.setPreferredSize(new Dimension(500,40));//初始化面板按钮gridLayout.setVgap(10);//设置组件的水平间距gridLayout.setHgap(10);//设置组件的垂直间距//初始化按钮对象for(int row=0;row<names.length;row++) {for(int col=0;col<names[row].length;col++) {buttons[row][col]=new JButton(names[row][col]);//创建按钮}}//静态的初始化设置一些,把多个单词的给标识起来/*buttons[0][0].setActionCommand("A");//"**"buttons[0][1].setActionCommand("N");//"ln"buttons[0][2].setActionCommand("G");//"lg"buttons[0][3].setActionCommand("R");//clearbuttons[1][0].setActionCommand("S");//sinbuttons[1][1].setActionCommand("C");//cosbuttons[1][2].setActionCommand("T");//tanbuttons[2][0].setActionCommand("P");//PTbuttons[2][1].setActionCommand("B");//"//"buttons[6][0].setActionCommand("D");//"+/-"*///还是用循环全部绑定吧,自己一个一个绑代码灵活性不高for(int row=0;row<names.length;row++) {for(int col=0;col<names[row].length;col++) {buttons[row][col].setActionCommand(target[row][col]);}}//设置左侧标签的宽度leftLabel.setPreferredSize(new Dimension(10,0));//设置右侧标签的宽度rightLabel.setPreferredSize(new Dimension(10,0));//设置底部标签的宽度,组件的有高度,可以没宽度,和两边相反bottomLabel.setPreferredSize(new Dimension(0,10));}//通过哈希表实现输出字符串和里面的字符串的不同标识public void Stringbind() {//map.put(getWarningString(), getName())for(int row=0;row<names.length;row++) {for(int col=0;col<names[row].length;col++) {map.put(target[row][col], names[row][col]);//System.out.println("执行成功");}}//用来判断值有没有填写进去//System.out.println(map.size());}public void buttonAction() {//按钮绑定动作事件,和自己绑定,自己实现的监听的方法for(int row=0;row<names.length;row++) {for(int col=0;col=48 && code<=57 && !e.isShiftDown()) {sb.append((char)code);//outPut();}else if(code==56 && e.isShiftDown()) {sb.append("*");//outPut();}else if(code==47 && !e.isShiftDown()) {sb.append("/");}else if(code==8) {//Backspace键//删除最后的一个字符sb.deleteCharAt(sb.length()-1);}else if(code==53 && e.isShiftDown()) {sb.append("%");}else if(code==61 && e.isShiftDown()) {sb.append("+");}else if(code==61 && !e.isShiftDown()) {//"="//计算结果result();}else if(code==45 && !e.isShiftDown()) {sb.append("-");}else if(code==46 && !e.isShiftDown()) {sb.append(".");}else if(code==10) {//Enter键//计算结果result();}//每次键盘输入之后都要更新,所以干脆就直接放判断最后outPut();//"="和"Enter"键if(code==61 && !e.isShiftDown()||code==10) {//就是把[0,length)的内容删除即可sb.delete(0, sb.length());//结果出来之后就是重新输入计算下一个}}});}//重写鼠标点击事件@Overridepublic void actionPerformed(ActionEvent e) {// TODO Auto-generated method stubJButton button =(JButton)e.getSource();//获得触发此次动作事件的按钮对象String buttonName =e.getActionCommand();//获得触发此次动作事件的按钮的标签文本if(buttonName.equals("X")) {//没有字符串之后就不能删除了if(sb.length()!=0) {sb.deleteCharAt(sb.length()-1);//textField.setText(output);//删除之后还需要立即显示一次,不然没有反应}}else if(buttonName.equals("R")){//就是把[0,length)的内容删除即可sb.delete(0, sb.length());//删除之后还需要立即显示一次,不然没有反应//textField.setText(output);}else if(buttonName.equals("=")) {//计算结果result();}else {sb.append(buttonName);//textField.setText(output);}//反正每次响应事件之后都要更新,干脆直接放在最后outPut();//要重新使框架获得焦点,这要写呢,不写就按下按钮之后键盘就没反应了if(buttonName.equals("=")) {//就是把[0,length)的内容删除即可sb.delete(0, sb.length());//结果出来之后就是重新输入计算下一个}this.requestFocus();}//记录每次将输出的字符串public void outPut() {output="";for(int i=0;iAln--->Nlg--->Gsin--->S cos--->C//tan--->TPI--->P//--->B/``--->D//计算是按照优先级来的//PI不算运算,直接填进去吧//也不行,本来就是用一个符号来表示,这突然变成一长串数字,那还是运算的时候展开吧//展开PItry {pi();//ln,lg,sin,cos,tan,开根号的运算special();//乘方运算power();//乘除,整除,求余运算multiplyDivide();//加减计算addAndSubtract();}catch(Exception e) {//弹出警告的弹窗warning();}finally {System.out.println("今天又是元气满满的一天");}}//展开PIpublic void pi() {for(int i=0;i<sb.length();i++) {if(sb.charAt(i)=='P') {double res=Math.PI;String resString=res+"";//更新字符串sb=sb.replace(i, i+1, resString);i=resString.length()-1;continue;}}}//ln,lg,sin,cos,tan,开根号的运算public void special() {//都是用右边一个操作数的运算for(int i=0;i<sb.length();i++) {if(sb.charAt(i)=='N'||sb.charAt(i)=='G'||sb.charAt(i)=='S'||sb.charAt(i)=='C'||sb.charAt(i)=='T'||sb.charAt(i)=='D') {double num=0;int len=0;//记录字符串长度,之后还要进行字符串的替换呢//只需要应该一边的数字即可for(int j=i+1;j<sb.length();j++) {//是j不是i,咋又错在这里了if(sb.charAt(j)=='*'||sb.charAt(j)=='/'||sb.charAt(j)=='%'||sb.charAt(j)=='B'||sb.charAt(j)=='+'||sb.charAt(j)=='-'||sb.charAt(j)=='A'||sb.charAt(j)=='N'||sb.charAt(j)=='G'||sb.charAt(j)=='S'||sb.charAt(j)=='C'||sb.charAt(j)=='T'||sb.charAt(j)=='D') {String s1=sb.substring(i+1,j);num=Double.parseDouble(s1);len=s1.length();break;}//找到最右边咯,中间没有运算符那也要停止了if(j==sb.length()-1) {//这边是到j+1哈,找错误找了半天//如果右边是一位的数字,就会导致运算是数是空//如果右边是多位的数字,那么最右边的数不会参与运算,所以需要[i+1,j+1)String s1=sb.substring(i+1,j+1);num=Double.parseDouble(s1);len=s1.length();break;}}//进行运算//ln运算if(sb.charAt(i)=='N') {double res=Math.log(num);String resString=res+"";//更新字符串sb=sb.replace(i, i+len+1, resString);i=resString.length()-1;continue;}//lg运算if(sb.charAt(i)=='G') {//换底公式double res=Math.log(num)/Math.log(10);String resString=res+"";//更新字符串sb=sb.replace(i, i+len+1, resString);i=resString.length()-1;continue;}//sin运算if(sb.charAt(i)=='S') {//还是采用角度制吧,弧度制取值用PI越界了,如果要除的话要加入括号的机制//偷懒一下就用角度值吧,不用加入括号double res=Math.sin(Math.toRadians(num));String resString=res+"";//更新字符串sb=sb.replace(i, i+len+1, resString);i=resString.length()-1;continue;}//cos运算if(sb.charAt(i)=='C') {//还是采用角度制吧,弧度制取值用PI越界了,如果要除的话要加入括号的机制//偷懒一下就用角度值吧,不用加入括号double res=Math.cos(Math.toRadians(num));String resString=res+"";//更新字符串sb=sb.replace(i, i+len+1, resString);i=resString.length()-1;continue;}//tan运算if(sb.charAt(i)=='T') {//还是采用角度制吧,弧度制取值用PI越界了,如果要除的话要加入括号的机制//偷懒一下就用角度值吧,不用加入括号double res=Math.tan(Math.toRadians(num));String resString=res+"";//更新字符串sb=sb.replace(i, i+len+1, resString);i=resString.length()-1;continue;}//开根号运算if(sb.charAt(i)=='D') {double res=Math.sqrt(num);String resString=res+"";//更新字符串sb=sb.replace(i, i+len+1, resString);i=resString.length()-1;continue;}}}}//乘方运算public void power(){for(int i=0;i=0;j--) {//得到第一个操作数,遇到加减乘除就停止了,相当于分隔了操作数if(sb.charAt(j)=='*'||sb.charAt(j)=='/'||sb.charAt(j)=='%'||sb.charAt(j)=='B'||sb.charAt(j)=='+'||sb.charAt(j)=='-') {String s1=sb.substring(j+1,i);num1=Double.parseDouble(s1);len1=s1.length();break;}//找到边界了,只能停止了咯if(j==0) {String s1=sb.substring(j,i);num1=Double.parseDouble(s1);len1=s1.length();break;}}//往右边找第二个操作数,第二个操作数的话遇到乘除也需要提前终止,第一个不需要//因为如果第一个操作数有乘除这种符号,就已经提前终止了,只有第二个操作数才需要考虑for(int j=i+1;j<sb.length();j++) {if(sb.charAt(j)=='*'||sb.charAt(j)=='/'||sb.charAt(j)=='%'||sb.charAt(j)=='B'||sb.charAt(j)=='+'||sb.charAt(j)=='-'||sb.charAt(j)=='A') {String s1=sb.substring(i+1,j);num2=Double.parseDouble(s1);len2=s1.length();break;}//找到最右边咯,中间没有运算符那也要停止了if(j==sb.length()-1) {//这边是到j+1哈,找错误找了半天//如果右边是一位的数字,就会导致运算是数是空//如果右边是多位的数字,那么最右边的数不会参与运算,所以需要[i+1,j+1)String s1=sb.substring(i+1,j+1);num2=Double.parseDouble(s1);len2=s1.length();break;}}//进行运算double res=Math.pow(num1, num2);String resString=res+"";//更新字符串sb=sb.replace(i-len1, i+len2+1, resString);i=i-len1-1+resString.length();continue;}}}//乘除,整除,求余运算public void multiplyDivide() {for(int i=0;i=0;j--) {//得到第一个操作数,遇到加减就停止了,相当于分隔了操作数if(sb.charAt(j)=='+'||sb.charAt(j)=='-') {String s1=sb.substring(j+1,i);num1=Double.parseDouble(s1);len1=s1.length();break;}//找到边界了,只能停止了咯if(j==0) {String s1=sb.substring(j,i);num1=Double.parseDouble(s1);len1=s1.length();break;}}//往右边找第二个操作数,第二个操作数的话遇到乘除也需要提前终止,第一个不需要//因为如果第一个操作数有乘除这种符号,就已经提前终止了,只有第二个操作数才需要考虑for(int j=i+1;j<sb.length();j++) {if(sb.charAt(j)=='*'||sb.charAt(j)=='/'||sb.charAt(j)=='%'||sb.charAt(j)=='B'||sb.charAt(j)=='+'||sb.charAt(j)=='-') {String s1=sb.substring(i+1,j);num2=Double.parseDouble(s1);len2=s1.length();break;}//找到最右边咯,中间没有运算符那也要停止了if(j==sb.length()-1) {//这边是到j+1哈,找错误找了半天//如果右边是一位的数字,就会导致运算是数是空//如果右边是多位的数字,那么最右边的数不会参与运算,所以需要[i+1,j+1)String s1=sb.substring(i+1,j+1);num2=Double.parseDouble(s1);len2=s1.length();break;}}//进行运算if(sb.charAt(i)=='*') {double res=num1*num2;String resString=res+"";//更新字符串sb=sb.replace(i-len1, i+len2+1, resString);i=i-len1-1+resString.length();continue;}if(sb.charAt(i)=='/') {//除数不能为0,有异常抛出的弹窗了,就去掉这个了/*if(Math.abs(num2)<=1e-6) {System.out.println("输出警告");continue;}*/double res=num1/num2;String resString=res+"";//更新字符串sb=sb.replace(i-len1, i+len2+1, resString);i=i-len1-1+resString.length();continue;}if(sb.charAt(i)=='%') {//整数才能求余,第二个操作数也不能为0/*if(Math.abs(num2)<=1e-6) {System.out.println("输出警告");continue;}*///是求余%double res=(int)num1%(int)num2;String resString=res+"";//更新字符串sb=sb.replace(i-len1, i+len2+1, resString);i=i-len1-1+resString.length();continue;}if(sb.charAt(i)=='B') {//整数的运算//除数不能为0/*if(Math.abs(num2)<=1e-6) {System.out.println("输出警告");continue;}*/double res=(int)num1/(int)num2;String resString=res+"";//更新字符串sb=sb.replace(i-len1, i+len2+1, resString);i=i-len1-1+resString.length();continue;}}}}//加减运算public void addAndSubtract() {for(int i=0;i=0;j--) {if(j==0) {String s1=sb.substring(j,i);//得到第一个操作数num1=Double.parseDouble(s1);len1=s1.length();break;}}for(int j=i+1;j<sb.length();j++) {if(sb.charAt(j)=='+'||sb.charAt(j)=='-') {String s1=sb.substring(i+1,j);//得到第二个操作数num2=Double.parseDouble(s1);len2=s1.length();break;}if(j==sb.length()-1) {String s1=sb.substring(i+1,j+1);num2=Double.parseDouble(s1);len2=s1.length();break;}}//进行加运算if(sb.charAt(i)=='+') {double res=num1+num2;//String s2=sb.substring(i-len1,i+len2+1);//String s3=res+ "";//sb=sb.replace(s2, s3);String resString=res+"";//更新字符串sb=sb.replace(i-len1, i+len2+1, resString);i=i-len1-1+resString.length();continue;}//进行减运算if(sb.charAt(i)=='-') {double res=num1-num2;String resString=res+"";//更新字符串sb=sb.replace(i-len1, i+len2+1, resString);i=i-len1-1+resString.length();continue;}}}}//警告,输入不合法的时候弹出public void warning() {JDialog jDialog=new JDialog();//创建弹窗对象jDialog.setTitle("警告");//设置弹窗标题,和Frame差不多,可能还要通过标签来提示jDialog.setSize(500,600);//设置弹窗的大小jDialog.setAlwaysOnTop(true);//让弹窗置顶jDialog.setLocationRelativeTo(null);//让弹窗居中jDialog.setModal(true);//弹窗不关闭则无法操作下面的界面//设置字体的类型,加粗,和大小warningLabel.setFont(new Font("Microsoft YaHei",Font.BOLD,30));//输出警告提示符warningLabel.setText("就是你小子在搞乱的是吧!!!");//标签的位置和大小warningLabel.setBounds(60,180,500,100);//这个也要取消布局管理器才行jDialog.getContentPane().setLayout(null);//往弹窗中添加标签jDialog.getContentPane().add(warningLabel);jDialog.setVisible(true);//让弹窗显示出来}}
总结:
这写下来还是写了挺长的时间了,目前还是比较满意的,因为是刚学JAVA语言,所以有很多地方都没太弄懂,有错误的地方,希望大家能够指正。对于计算器的功能编写其实还是能够添加很多功能也可以把Math.E添加进去,进行其他运算,大家可以根据自己的需求进行编写。写这个我的感受就是就是把我学的东西都用到了哈希表,异常抛出,控件都用上了,我对于他有一个比较清晰的认识,他要实现什么功能,虽然看着简单,但是卡在了很多地方,还有到处找资料来分析他的原因,尽管是有点简陋,我还加入了一些自己的东西,按下Backspace键和Enter键也有对应的响应,这也是努力的结果,希望未来越来越好吧。
6月14日,给按钮添加颜色
这个的话看自己想怎么弄,如果有规律的话可以尝试用循环,不然就需要一个个的调整,建议这个添加到initModule方法里面,这个是我专门用来设计组件信息的方法。(下面是一个示范的案例)
buttons[0][0].setBackground(Color.red);//给第一个按钮添加红色背景的按钮
6月15日,调节按钮中字体的大小。
在创建按钮的时候,给字体统一设置。对原始程序中的修改如下。
//初始化按钮对象 for(int row=0;row<names.length;row++) { for(int col=0;col<names[row].length;col++) {buttons[row][col]=new JButton(names[row][col]);//创建按钮//设置字体的类型,是否加粗和字体的大小buttons[row][col].setFont(new Font("Microsoft YaHei",Font.BOLD,20));} }