如果希望可以编写一个GUI程序提示用户输入贷款总额,年利率,年数,然后点击OK按钮获取月偿还额和总偿还额。则必须使用时间驱动程序设计来编写代码。
能创建一个事件并能触发该事件的组件成为源对象或是源组件。如按钮是按钮点击动作事件的源对象。一个事件是一个事件类的实例化。事件的根类是java.util.EventObject。
除了ListSelectionEvent和ChangeEvent之外所有的事件类都包括在java.awt.event包中,ListSelectionEvent和ChangeEvent在java.swing.event包中。AWT事件本来就是为了AWT组件设计的,但是很多Swing组件都会用到它们。
Java使用一种基于委托的模型来处理事件:源对象触发一个事件,对此事件感兴趣的对象会处理它。对此事件感兴趣的对象成为监听器(listener)。一个对象要成为源对象的监听器需要具备两个条件:
1. 监听器对象的类必须是相应的事件监听器接口的实例,以确保监听器有处理这个时间的正确方法。
2. 监听器对象必须有源对象注册。
现在我们可以编写一个程序,使用两个按钮控制一个圆的大小。代码如下
import java.util.*; import javax.swing.*; import javax.swing.border.Border; import java.io.*; import java.awt.*; import java.awt.event.*; class ArcPanel extends JPanel { private int radius = 5; protected void paintComponent(Graphics g) { super.paintComponent(g); g.drawOval(getWidth() / 2 - radius, getHeight() / 2 - radius, 2 * radius, 2 * radius); } public void enlarge() { radius += 5; repaint(); } public void shrink() { radius -= 5; repaint(); } } public class Main extends JFrame { private JButton jbtEnlarge = new JButton("Enlarge"); private JButton jbtShrink = new JButton("Shrink"); private ArcPanel canvas = new ArcPanel(); public Main() { JPanel panel = new JPanel(); panel.add(jbtEnlarge); panel.add(jbtShrink); this.add(canvas, BorderLayout.CENTER); this.add(panel, BorderLayout.SOUTH); jbtEnlarge.addActionListener(new EnlargeListener()); jbtShrink.addActionListener(new ShrinkListener()); } class EnlargeListener implements ActionListener { public void actionPerformed(ActionEvent e) { canvas.enlarge(); } } class ShrinkListener implements ActionListener { public void actionPerformed(ActionEvent e) { canvas.shrink(); } } public static void main(String[] args) { Main frame = new Main(); frame.setTitle("Main"); frame.setSize(250, 300); frame.setLocationRelativeTo(null); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } }
由上面的代码显示,我将EnlargeListener和ShrinListener定义成了内部类,通常,如果一个类只是被外部类使用,就将该类定义为内部类。一个内部类有如下特征:
1. 一个内部类被编译成一个名为OutClass$InnerClass.class的类。
2. 内部类可以引用定义在它嵌套外部类中的数据和方法,所以,不需要将外部类对象的引用传递给内部类的构造方法,因此,内部类可以使程序更加简单和简洁。
3. 使用可见修饰符定义内部类时,遵从应用与在类成员上一样的可见性原则。
4. 可以讲内部类定义为static。一个static内部类可以使用外部类的名字访问。一个static类是不能访问外部类的非静态成员的。
5. 内部类对象经常在外部类中创建。但是也可以从另一个类中创建一个内部类的对象,如果该内部类是非静态的,必须先创建一个外部类的实例,然后使用下面的语法创建以一个内部类对象:
OutClass.InnerClassinnerObject = outObject.new InnerClass();
6. 如果内部类是静态的,那么使用下面的语法为它创建一个对象:
OutClass.InnerClassinnObject = new OutClass.InnerClass();
我们可以把上面的监听器重新写,代码如下:
jbtEnlarge.addActionListener(new ActionListener() { public void actionPerformed(ActionListen e) { canvas.enlarge(); } })
匿名内部类:
1. 匿名内部类必须总是扩展父类或实现接口,但是它不能有显示的extends或者implements子句。
2. 匿名类必须实现父类或者接口内的所有方法。
3. 匿名内部类总是使用它的父类的无参构造方法来创建实例,如果匿名内部类实现了接口,构造方法就是Object();
4. 匿名内部类被编译为一个名为OuterClassName$n.class的类。
话不多说,贴上代码:
import java.util.*; import javax.swing.*; import javax.swing.border.Border; import java.io.*; import java.awt.*; import java.awt.event.*; public class Main extends JFrame { private JButton jbtEnlarge = new JButton("Enlarge"); private JButton jbtShrink = new JButton("Shrink"); private ArcPanel canvas = new ArcPanel(); private ButtonListener listener = new ButtonListener(); public Main() { JPanel panel = new JPanel(); panel.add(jbtEnlarge); panel.add(jbtShrink); this.add(canvas, BorderLayout.CENTER); this.add(panel, BorderLayout.SOUTH); jbtEnlarge.addActionListener(listener); jbtShrink.addActionListener(listener); } class ButtonListener implements ActionListener { public void actionPerformed(ActionEvent e) { if(e.getSource() == jbtEnlarge) canvas.enlarge(); else if(e.getSource() == jbtShrink) canvas.shrink(); } } class ArcPanel extends JPanel { private int radius = 5; protected void paintComponent(Graphics g) { super.paintComponent(g); g.drawOval(getWidth() / 2 - radius, getHeight() / 2 - radius, 2 * radius, 2 * radius); } public void enlarge() { radius += 5; repaint(); } public void shrink() { radius -= 5; repaint(); } } public static void main(String[] args) { Main frame = new Main(); frame.setTitle("Main"); frame.setSize(250, 300); frame.setLocationRelativeTo(null); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } }
如上代码所示,我们只定义了一个监听器 listener。我们可以通过getSource()方法获取我们鼠标点击的按钮,这样可以减少类的定义从而简化代码。
要求:
(1) 创建一个如图所示的用户接口
(2) 创建一个5行2列的GridLayout面板,添加文本和标签
(3) 创建一个FlowLayout面板,并添加一个按钮
(4) 将两个面板添加到框架中
(5) 处理事件
代码如下:
import java.util.*; import javax.swing.*; import javax.swing.border.Border; import javax.swing.border.TitledBorder; import java.io.*; import java.awt.*; import java.awt.event.*; public class Main extends JFrame { private JTextField jtfAnnualInterestRate = new JTextField("please type rate here"); private JTextField jtfNumberOfYears = new JTextField("please type year here"); private JTextField jtfLoanAmount = new JTextField("please type loanAmount here"); private JTextField jtfMonthlyPayment = new JTextField("output"); private JTextField jtfTotalPayment = new JTextField("output"); private JButton jbtComputeLoan = new JButton("Compute Payment"); public class ButtonListener implements ActionListener { public void actionPerformed(ActionEvent e) { double interest = Double.parseDouble(jtfAnnualInterestRate.getText()); int year = Integer.parseInt(jtfNumberOfYears.getText()); double loanAmount = Double.parseDouble(jtfLoanAmount.getText()); // loan(interest,year,loanAmount); jtfMonthlyPayment.setText(String.format("%.2f", loanAmount));//这里只是一个替代。。。。。 jtfTotalPayment.setText(String.format("%.2f", loanAmount));// 这里只是一个替代。。。。。 } } public Main() { JPanel p1 = new JPanel(new GridLayout(5,2)); p1.add(new JLabel("Annual Interest Rate")); p1.add(jtfAnnualInterestRate); p1.add(new JLabel("Number of Years")); p1.add(jtfNumberOfYears); p1.add(new JLabel("Loan Amount")); p1.add(jtfLoanAmount); p1.add(new JLabel("Monthly Payment")); p1.add(jtfMonthlyPayment); p1.add(new JLabel("Total Payment")); p1.add(jtfTotalPayment); p1.setBorder(new TitledBorder("Enter loan amount,interest rate,and year")); JPanel p2 = new JPanel(new FlowLayout(FlowLayout.RIGHT)); p2.add(jbtComputeLoan); add(p1,BorderLayout.CENTER); add(p2,BorderLayout.SOUTH); jbtComputeLoan.addActionListener(new ButtonListener()); } public static void main(String[] args) { Main frame = new Main(); frame.pack(); frame.setTitle("LoanCalculator"); frame.setLocationRelativeTo(null); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } }
Loan类的实现我已经实现过了,可是文件丢失了,然后我又不想再写一遍。。。。。
上代码:
import java.util.*; import java.util.Timer; import javax.swing.*; import javax.swing.border.Border; import javax.swing.border.TitledBorder; import java.io.*; import java.awt.*; import java.awt.event.*; public class Main extends JFrame { static class MovableMessagePanel extends JPanel { private String s = "Welcomea To Java!"; private int x = 20; private int y = 20; public MovableMessagePanel(String s) { this.s = s; addMouseMotionListener(new MouseAdapter() { public void mouseDragged(MouseEvent e) { x = e.getX(); y = e.getY(); repaint(); } }); } protected void paintComponent(Graphics g) { super.paintComponent(g); g.drawString(s, x, y); } } public Main() { MovableMessagePanel p =new MovableMessagePanel("Hello Java!"); setLayout(new BorderLayout()); add(p); } public static void main(String[] args) { Main frame = new Main(); frame.setTitle("MoveMessageDemo"); frame.setSize(200,100); frame.setLocationRelativeTo(null); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } }
使用MouseMotionAdapter覆盖mouseDragged方法,如果内部类实现了MouseMotionListener接口,即使监听器并不关心某些事件,也必须要实现所有的处理器。
1. 事件类的根类是java.util.EventObject。EventObject的子类处理各种特殊类型的时间,例如动作事件,窗口时间,组件事件,鼠标事件和按键事件。可以使用EventObject类中的getSource()实例方法判断事件的源对象。如果一个组件能够触发整个事件,那么它的所有子类都能触发同类型的事件。
2. 监听器对象的类必须时间相应的时间监听器接口。Java语言为每种事件类提供监听器接口。XEvent的监听器接口通常命名为XListener,但是MouseMotionListener除外。例如,ActionEvent对应的监听器接口是ActionListener,每个ActionEvent的监听器都应该实现ActionListener接口,监听器接口包含成为处理器的处理事件的方法。
3. 监听器对象必须由源对象注册。注册方法依赖于事件的类型。对ActionEvent来讲,注册方法是addActionListener。一般来说XEvent的方法命名为addXListener。
4. 内部类或者是嵌套类是定义在另一个类中的类。内部类可以应用定义在它嵌套的外部类中的数据和方法。所以,不需要将外部类的引用传递给内部类的构造方法。
5. 方便适配器能够提供监听器接口中所有方法的默认实现类的支持类。Java为每一个AWT监听器接口提供多个处理器的方便监听器适配器,XListenerde 方便监听器适配器命名为Xadapter。。
6. 一个源对象可以触发几种类型的事件。对每种事件,源对象维护一个注册的监听器列表,通过调用监听器对象的处理器,通知所有已经注册的监听器取处理事件。
7. 在一个组件上点击,释放,移动或拖动鼠标就会触发鼠标事件。鼠标事件对象捕获事件,例如和事件相关的点击次数和鼠标点的位置(x,y坐标)
8. Java提供两个处理鼠标事件的监听器接口,MouseListener和MouseMotionListner来处理事件,实现MouseListener接口来监听注入按下,释放,点击,输入或退出鼠标等动作,实现MouseMotionListener接口来监听注入移动或拖动鼠标的动作。
9. KeyEvent对象描述事件的性质(即按下,释放或敲击一个键)以及相对应的键值
10. 当按下按键是就会调用keyPressed处理器,当释放按键时会调用keyReleased处理器,而当敲入一个统一码字符键是,就会调用keyTyped处理器。如果某个按键没有统一码(如功能键,修改键,动作键和控制键),则不会调用keyTyped处理器。
可以使用Timer类控制Java的动画。定时器以固定频率触发ActionEvent。监听器通过更新画面来模拟动画。