本篇文章主要是提供思路,当然文章末尾也又提供了源代码。
代码也是写了几天,重要的理解,不是直接复制粘贴就交作业了。
转载请注明出处,尊重作者劳动成果。
目录
计算:
要制作一个简单的计算器,首先就是对于界面的设计,然后就是其功能的实现。
对于事件的实现大概就分下面几个步骤。
- 确定事件源和监听源
- 实现监听器的接口
- 将事件源注册到监听器
然后就一起进入代码的编写吧,我是分成了几个发放来编写功能,最后有最终的代码,可以直接运行,这要注意的是,直接粘贴的话,类名和文件名要一样哦。
界面的设计:
他最上面是要有一个文本框,中间要有很多的按钮,我这里大的框架是采用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
//初始化面板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
//初始化窗体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
具体的方法就看方法内部的调用吧,有应该都有写方法的作用的,接下来就是确定事件源和监听源,我是打算通过键盘输入一些字符和通过鼠标点击按钮来实现。
确定事件源和监听源
事件源:JButton[][] buttons=new JButton[names.length][4];按钮,和自己本身框架
监听源:ActionListener,KeyListener
实现监听器的接口
//重写鼠标点击事件@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();}
@Override public void keyTyped(KeyEvent e) { // TODO Auto-generated method stub } //按着按键不松时调用 @Override public void keyPressed(KeyEvent e) { // TODO Auto-generated method stub } //按键松开后执行 @Override public 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());//结果出来之后就是重新输入计算下一个 } } }
将事件源注册到监听器
public void buttonAction() {//按钮绑定动作事件,和自己绑定,自己实现的监听的方法for(int row=0;row=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=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
问题5:在查找的时候输出的是null,不是目标的字符串。
解决:这我找了挺久的,最后还看了HashMap里面的元素有多少,发现是存进去的,问题出现在字符串“A”和'A'不是一样的东西,所以哈希表没找到。
输出是通过一个函数来实现
//用来更新文本框输出的字符串StringBuffer sb=new StringBuffer();//用来对应文本框的输出,还是选择用HashMap来存储Mapmap=new HashMap();//输出的字符串放这个里面,每次使用都需要清零哈String output=""; //记录每次将输出的字符串public void outPut() {output="";for(int i=0;i
问题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:按下按钮之后没有显示删除。
解决:这个是因为没有显示,在程序中已经删除了,但是呢,用户看到是还没删除的样子,所以还是要更新一下输出。
计算:
接下来就是最后的计算环节了。
首先确定计算的优先级,我这里没有引入括号了,所以把lg,ln,sin,cos,tan,开根号都是和右边的操作数看成一个整体的。
字符串的运算就是通过运算符来分割,通过寻找到他的运算数,进行运算,把运算结果的字符串和运算前的字符串进行替换即可。
//计算每次的结果public void result() {//对应关系///还是用循环全部绑定吧,自己一个一个绑代码灵活性不高for(int row=0;row=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;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
总结:
这写下来还是写了挺长的时间了,目前还是比较满意的,因为是刚学JAVA语言,所以有很多地方都没太弄懂,有错误的地方,希望大家能够指正。对于计算器的功能编写其实还是能够添加很多功能也可以把Math.E添加进去,进行其他运算,大家可以根据自己的需求进行编写。写这个我的感受就是就是把我学的东西都用到了哈希表,异常抛出,控件都用上了,我对于他有一个比较清晰的认识,他要实现什么功能,虽然看着简单,但是卡在了很多地方,还有到处找资料来分析他的原因,尽管是有点简陋,我还加入了一些自己的东西,按下Backspace键和Enter键也有对应的响应,这也是努力的结果,希望未来越来越好吧。
6月14日,给按钮添加颜色
这个的话看自己想怎么弄,如果有规律的话可以尝试用循环,不然就需要一个个的调整,建议这个添加到initModule方法里面,这个是我专门用来设计组件信息的方法。(下面是一个示范的案例)
buttons[0][0].setBackground(Color.red);//给第一个按钮添加红色背景的按钮
6月15日,调节按钮中字体的大小。
在创建按钮的时候,给字体统一设置。对原始程序中的修改如下。
//初始化按钮对象 for(int row=0;row
来源地址:https://blog.csdn.net/weixin_64066303/article/details/130383385