- 菜单
- 菜单栏(menu bar)、菜单项(menu items)、 子菜单(submenus)。
- 菜单创建:
- 创建菜单栏
- 将菜单栏添加到框架的顶部
- 为每个菜单建立一个菜单对象
- 将顶层菜单添加到菜单栏中
- 向菜单对象添加菜单项、分隔符和子菜单
- 通常,采用扩展抽象类AbstractAction来定义一个实现了Action接口的类。
- javax.swing.JMenu
- JMenu(String label)
- JMenuItem add(JMenuItem item) //添加一个菜单项或菜单
- JMenuItem add(String label)
- JMenuItem add(Action a)
- void addSeparator() //将一个分隔符行添加到菜单中
- JMenuItem insert(JMenuItem menu, int index)
- JMenuItem insert(Action a, int index)
- void insertSeperator(int index)
- void remove(int index)
- void remove(JMenu item)
- javax.swing.JMenuItem
- JMenuItem(String label)
- JMenuItem(Action a)
- javax.swing.AbstractButton
- void setAction(Action a)
- void setHorizontalTextPosition(int pos)
- javax.swing.JFrame
- void setJMenuBar(JMenuBar menubar)
- 菜单项与按钮很相似。实际上,JMenuItem扩展了AbstractButton类。与按钮一样,可以用文本或者图标构造菜单项。
- 复选框和单选钮菜单项:
- 创建复选框菜单项的代码:
- JCheckBoxMenuItem readonlyItem = new JCheckBoxMenuItem("Read-only");
- optionsMenu.add(readonlyItem);
- 单选钮菜单项:加入到按钮组(ButtonGroup)中。单选钮菜单项JRadioButtonMenuItem加入到按钮组以及菜单中。使用这些菜单项,不需要立刻得到用户选择菜单项的通知,而是使用isSelected方法来测试菜单项的当前状态(意味着应该保留一个实例域保存这个 菜单项 的引用)。使用setSelected方法设置状态。
- 创建复选框菜单项的代码:
- 弹出菜单:
- 弹出菜单(pop-up menu)是不固定在菜单栏中随处浮动的菜单。
- 弹出菜单的创建与常规菜单类似,但没有标题。
- 弹出菜单必须调用show方法才能显示出来。调用时,需要给出父组件以及相对父组件坐标的显示位置。
- javax.swing.JPopupMenu
- void show(Component c, int x, int y)
- boolean isPopupTrigger(MouseEvent event) //鼠标事件是否是弹出菜单的触发器
- javax.swing.JComponent
- JPopupMenu getComponentPopupMenu()
- void setComponentPopupMenu(JPopupMenu popup)
- void setInheritsPopupMenu(boolean b)
- 快捷键和加速器
- 可以使用快捷键从当前打开的菜单中选择相应的菜单项
- 加速器是在不打开菜单的情况下选择菜单项的快捷键。
- javax.swing.JMenuItem
- JMenuItem(String label, int mnemonic)
- void setAccelerator(KeyStroke k)
- javax.swing.AbstractButton
- void setMnemonic(int mnemonic)
- void setDisplayedMnemonicIndex(int index)
- 启用和禁用菜单项
- 启用和禁用菜单项有两种策略:
- 每次环境发生变化就对相关的菜单项或动作调用setEnabled
- 在显示菜单之前就禁用这些菜单项。
- 这里需要为“菜单选中”事件注册监听器(Menu显示之前会调用menuSelected方法,在这个方法里面可以禁用选项)。MenuListener接口,包含三个方法:
- void MenuSelected(MenuEvent event) //被选择但尚未被打开前调用
- void menuDeSelected(MenuEvent event) //菜单取消选择并且已经关闭之后调用
- void menuCanceled(MenuEvent event) //菜单被取消时调用
- 这种方式不适用于带有加速键的菜单项,因为按下加速键的时候并没有打开菜单,因此动作没有被禁用,所以加速键依旧能够触发菜单项。
- 这里需要为“菜单选中”事件注册监听器(Menu显示之前会调用menuSelected方法,在这个方法里面可以禁用选项)。MenuListener接口,包含三个方法:
- 启用和禁用菜单项有两种策略:
- 示例程序:
package menu;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.event.*;
public class MenuFrame extends JFrame {
public static void main(String[] args) {
MenuFrame frame = new MenuFrame();
frame.setTitle("MenuFrame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_HEIGHT = 200;
private Action saveAction;
private Action saveAsAction;
private JCheckBoxMenuItem readonlyItem;
private JPopupMenu popup;
class TestAction extends AbstractAction{
public TestAction(String name) {
super(name);
}
public void actionPerformed(ActionEvent event) {
System.out.println(getValue(Action.NAME) + " selected.");
}
}
class MyMenuListener implements MenuListener{
private String name;
public MyMenuListener(String name) {
this.name = name;
}
public void menuSelected(MenuEvent event) {
System.out.println(name + " menu is selected.");
}
public void menuDeselected(MenuEvent event) {
System.out.println(name + " menu is deselected.");
}
public void menuCanceled(MenuEvent event) {
System.out.println(name + " menu is canceled.");
}
}
public MenuFrame() {
setSize(DEFAULT_WIDTH,DEFAULT_HEIGHT);
JMenuBar menuBar = new JMenuBar();
//*File
JMenu fileMenu = new JMenu("File");
fileMenu.addMenuListener(new MyMenuListener("File"));
//File->New
fileMenu.add(new TestAction("New"));
//File->Open
JMenuItem openItem = fileMenu.add(new TestAction("Open"));
openItem.setAccelerator(KeyStroke.getKeyStroke("ctrl O"));
//separator between Open and Save
fileMenu.addSeparator();
//File->Save
saveAction = new TestAction("Save");
JMenuItem saveItem = fileMenu.add(saveAction);
saveItem.setAccelerator(KeyStroke.getKeyStroke("ctrl S"));
//File->Save As
saveAsAction = new TestAction("Save As");
fileMenu.add(saveAsAction);
//separator between Save As and Exit
fileMenu.addSeparator();
//File->Exit
fileMenu.add(new AbstractAction("Exit"){
public void actionPerformed(ActionEvent event) {
System.exit(0);
}
});
//*Edit
JMenu editMenu = new JMenu("Edit");
editMenu.addMenuListener(new MyMenuListener("Edit"));
readonlyItem = new JCheckBoxMenuItem("Read-Only");
readonlyItem.addActionListener(event->{
boolean saveOk = !readonlyItem.isSelected();
saveAction.setEnabled(saveOk);
saveAsAction.setEnabled(saveOk);
});
ButtonGroup group = new ButtonGroup();
JRadioButtonMenuItem insertItem = new JRadioButtonMenuItem("Insert");
insertItem.setSelected(true);
JRadioButtonMenuItem overtypeItem = new JRadioButtonMenuItem("Overtype");
group.add(insertItem);
group.add(overtypeItem);
Action cutAction = new TestAction("cut");
cutAction.putValue(Action.SMALL_ICON, new ImageIcon("cut.gif"));
Action copyAction = new TestAction("copy");
copyAction.putValue(Action.SMALL_ICON, new ImageIcon("copy.gif"));
Action pasteAction = new TestAction("paste");
pasteAction.putValue(Action.SMALL_ICON, new ImageIcon("paste.gif"));
editMenu.add(cutAction);
editMenu.add(copyAction);
editMenu.add(pasteAction);
JMenu optionMenu = new JMenu("Options");
optionMenu.add(readonlyItem);
optionMenu.addSeparator();
optionMenu.add(insertItem);
optionMenu.add(overtypeItem);
editMenu.add(optionMenu);
//*Help
JMenu helpMenu = new JMenu("Help");
helpMenu.setMnemonic('H');
helpMenu.addMenuListener(new MyMenuListener("Help"));
JMenuItem indexItem = new JMenuItem("Index");
indexItem.setMnemonic('I');
indexItem.addActionListener(new TestAction("Index"));
helpMenu.add(indexItem);
Action aboutAction = new TestAction("About");
aboutAction.putValue(Action.MNEMONIC_KEY, Integer.valueOf('A'));
helpMenu.add(aboutAction);
setJMenuBar(menuBar);
menuBar.add(fileMenu);
menuBar.add(editMenu);
menuBar.add(helpMenu);
popup = new JPopupMenu();
popup.add(cutAction);
popup.add(copyAction);
popup.add(pasteAction);
JPanel panel = new JPanel();
panel.setComponentPopupMenu(popup);
add(panel);
}
}
- 工具栏
- 工具栏是在程序中提供的快速访问常用命令的按钮栏。
- 工具栏的特殊之处是可以将它随处移动。工具栏只有位于采用边框布局或者任何支持North、East、South和West约束布局管理器的容器内才能被拖拽。
- 工具栏可以完全脱离框架。关闭包含工具栏的框架时,它会回到原始的框架中。
- 按钮是工具栏中最常见的组件类型,但组件不限于此。
- 工具提示:tooltips
- 注意:动作名在菜单中就是菜单项名,而在工具栏中就是简短的说明。
- javax.swing.JToolBar
- JToolBar()
- JToolBar(String titleString)
- JToolBar(int orientation)
- JToolBar(String titleString, int orientation)
- JButton add(Action a)
- void addSeparator()
- javax.swing.JComponent
- void setToolTipText(String text)
- 示例程序:
package toolBar;
import javax.swing.JFrame;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class ToolBarFrame extends JFrame {
public static void main(String[] args) {
ToolBarFrame frame = new ToolBarFrame();
frame.setTitle("ToolBarFrame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_HEIGHT = 200;
private JPanel panel;
private Color originalColor;
public ToolBarFrame() {
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
panel = new JPanel();
originalColor = panel.getBackground();
JToolBar toolBar = new JToolBar();
Action blueAction = new ColorAction("blue", new ImageIcon("blue-ball.gif"), Color.BLUE);
Action yellowAction = new ColorAction("yellow", new ImageIcon("yellow-ball.gif"), Color.YELLOW);
Action redAction = new ColorAction("red", new ImageIcon("red-ball.gif"), Color.RED);
Action exitAction = new AbstractAction("Exit", new ImageIcon("exit.gif")) {
public void actionPerformed( ActionEvent event ) {
System.exit(0);
}
};
exitAction.putValue(Action.SHORT_DESCRIPTION, "Exit");
toolBar.add(blueAction);
toolBar.add(yellowAction);
toolBar.add(redAction);
toolBar.addSeparator();
toolBar.add(exitAction);
add(toolBar, BorderLayout.NORTH);
add(panel, BorderLayout.CENTER);
JPopupMenu popup = new JPopupMenu();
Action recoverAction = new AbstractAction("recover") {
public void actionPerformed(ActionEvent event) {
panel.setBackground(originalColor);
}
};
recoverAction.putValue(Action.SHORT_DESCRIPTION, "recover the background color to original color");
popup.add(recoverAction);
panel.setComponentPopupMenu(popup);
}
class ColorAction extends AbstractAction{
public ColorAction(String name, Icon icon, Color c ) {
this.putValue(Action.SHORT_DESCRIPTION, "set background color to " + name);
this.putValue(Action.SMALL_ICON, icon);
this.putValue("color", c);
}
public void actionPerformed(ActionEvent event) {
panel.setBackground((Color)this.getValue("color"));
}
}
}
- 复杂的布局管理
- 网格组布局(GridBagLayout),这种布局将组件按行和列排列。行和列的大小可以灵活改变,并且组件可以横跨多行多列。特性:灵活但复杂。
- 2005年,NetBeans开发队伍发明了 Matisse技术,这种技术将布局工具与布局管理器结合起来。用户界面设计者可以使用工具将组件拖拽到容器中,并指出组件的排列方式。即使没有用 NetBeans作为IDE,也应该考虑使用它的GUI生成工具。
- 网格组布局
- 将容器分解为网格单元,行和列的尺寸不需要相同!
- GridBagConstraints参数
- gridx和gridy指定了被添加组件的行、列位置。
- gridwidth和gridheight
- 增量域:weightx 和 weighty 。如果将增量值设为0,则这个区域将永远为初始尺寸。行和列的增量等于每行或每列单元格的增量的最大值。
- fill和anchor参数:
- 如果不希望组件拉伸至整个区域,就需要设置fill约束,有四个有效值:
- GridBagConstraints.NONE(默认值)
- GridBagConstraints.HORIZONTAL
- GridBagConstraint.VERTICAL
- GridBagConstraints.BOTH
- 如果组件没有填充整个区域,可以通过设置anchor域指定其位置:
- GridBagConstraints.NORTH
- GridBagConstraints.CENTER (默认值)
- GridBagConstraints.NORTHEAST
- GridBagConstraints.EAST
- 等等
- 如果不希望组件拉伸至整个区域,就需要设置fill约束,有四个有效值:
- 填充
- 设置insets域在组件周围添加附加的空白区域,通过设置Insets对象的left top right bottom。(外部填充)
- 通过设置ipadx和ipady指定内部填充。
- 要想使用网格组管理器进行布局,必须经过下列的过程
- 建立一个 GridBagLayout对象
- 将 GridBagLayout对象设置成组件的布局管理器
- 为每个组件建立一个GridBagConstraints对象。设置GridBagConstraints的域(约束条件)。
- 调用 add(component, constraints)
- 示例程序:
package gbc;
import javax.swing.JFrame;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class FontFrame extends JFrame {
public static void main(String[] args) {
EventQueue.invokeLater(()->{
FontFrame frame = new FontFrame();
frame.setTitle("FontFrame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
});
}
private JTextArea text;
private JComboBox<String> styleBox;
private JComboBox<Integer> sizeBox;
private JCheckBox bold;
private JCheckBox italic;
private static final int TEXT_ROWS = 10;
private static final int TEXT_COLUMNS = 20;
private static final int DEFAULT_FONT_SIZE = 8;
private static final String DEFAULT_FONT_STYLE = Font.SERIF;
public FontFrame() {
setLayout(new GridBagLayout());
//Style label
JLabel styleLabel = new JLabel("Style: ");
add(styleLabel, new GBC(0,0).setAnchor(GBC.EAST));
//FontListener
ActionListener listener = new FontListener();
//styleBox
styleBox = new JComboBox<>( new String[] {
Font.SERIF, Font.SANS_SERIF, Font.MONOSPACED,Font.DIALOG,Font.DIALOG_INPUT} );
styleBox.addActionListener(listener);
add(styleBox, new GBC(1,0).setFill(GBC.HORIZONTAL).setWeight(100, 0).setInsets(1));
//Size label
JLabel sizeLabel = new JLabel("Size: ");
add(sizeLabel, new GBC(0,1).setAnchor(GBC.EAST) );
//sizeBox
sizeBox = new JComboBox<>(new Integer[] {8, 12, 16, 18, 24, 36 });
sizeBox.addActionListener(listener);
add(sizeBox, new GBC(1,1).setFill(GBC.HORIZONTAL).setWeight(100, 0).setInsets(1));
//bold
bold = new JCheckBox("Bold");
bold.setSelected(false);
bold.addActionListener(listener);
add(bold, new GBC(0,2,2,1).setAnchor(GBC.CENTER).setWeight(100, 100) );
//italic
italic = new JCheckBox("Italic");
italic.setSelected(false);
italic.addActionListener(listener);
add(italic, new GBC(0,3,2,1).setAnchor(GBC.CENTER).setWeight(100, 100));
//text
text = new JTextArea(TEXT_ROWS, TEXT_COLUMNS);
text.setText("I just can't keep living this way.");
text.setFont(new Font(DEFAULT_FONT_STYLE, Font.PLAIN, DEFAULT_FONT_SIZE) );
//text.setEditable(false);
text.setLineWrap(true);
text.setBorder(BorderFactory.createEtchedBorder());
add(text, new GBC(2,0,1,4).setFill(GBC.BOTH).setWeight(100, 100) );
pack();
}
class FontListener implements ActionListener{
public void actionPerformed(ActionEvent event) {
String style = styleBox.getItemAt(styleBox.getSelectedIndex());
int mode = 0;
if(bold.isSelected()) mode += Font.BOLD;
if(italic.isSelected()) mode += Font.ITALIC;
int size = sizeBox.getItemAt(sizeBox.getSelectedIndex());
text.setFont(new Font(style, mode, size));
text.repaint();
}
}
}
package gbc;
import java.awt.GridBagConstraints;
import java.awt.*;
public class GBC extends GridBagConstraints {
public GBC(int gridx, int gridy) {
this.gridx = gridx;
this.gridy = gridy;
}
public GBC(int gridx, int gridy, int gridwidth, int gridheight) {
this.gridx = gridx;
this.gridy = gridy;
this.gridwidth = gridwidth;
this.gridheight = gridheight;
}
public GBC setAnchor(int anchor) {
this.anchor = anchor;
return this;
}
public GBC setFill(int fill) {
this.fill = fill;
return this;
}
public GBC setWeight(int weightx, int weighty) {
this.weightx = weightx;
this.weighty = weighty;
return this;
}
public GBC setInsets( int distance ) {
this.insets = new Insets(distance,distance,distance,distance);
return this;
}
public GBC setInsets(int top, int left, int bottom, int right ) {
this.insets = new Insets(top,left,bottom,right);
return this;
}
public GBC setIpad(int ipadx, int ipady) {
this.ipadx = ipadx;
this.ipady = ipady;
return this;
}
}
- 不使用布局管理器
- 只想把组件放在一个固定的位置上(通常称为绝对定位)
- 步骤
- 将布局管理器设置为null
- 将组件添加到容器中
- 指定想要放置的位置和大小
frame.setLayout(null);
JButton ok = new JButton("OK");
frame.add(ok);
ok.setBounds(10,10,30,15);
//void setBounds(int x, int y, int width, int height)
- 定制布局管理器
- 定制布局管理器必须实现 LayoutManager接口并且覆盖下面5个方法
- void addLayoutComponent(String s, Component c)
- void removeLayoutComponent(Component c)
- Dimension preferredLayoutSize(Container parent)
- Dimension minimumLayoutSize(Container parent)
- void layoutContainer(Container parent)
- AWT还有第二个接口 LayoutManager2,其中包含10个需要实现的方法。这个接口的主要特点是允许用户使用带有约束的add方法。
- 定制布局管理器必须实现 LayoutManager接口并且覆盖下面5个方法
- 遍历顺序
- 键盘焦点的遍历顺序
- 顺序:从左到右,从上到下
- 容器包含其他容器:若遍历到子容器,则遍历完子容器之后继续
- 调用 component.setFocusable(false)可以从焦点遍历中删除一个组件。
- 对话框
- AWT也分为 模式对话框 和 无模式对话框。
- 模式对话框:结束处理前,不允许用户与应用程序的其他窗口进行交互。
- 无模式对话框:允许用户同时在对话框和应用程序的其他窗口中输入信息。
- 如何实现自己的对话框编写一个复杂的对话框
- 对话框之间如何传递数据
- 选项对话框
- JOptionPane有4个用于显示这些对话框的静态方法:
- showMessageDialog 显示一条消息并等待用户点击OK
- showConfirmDialog 显示一条消息并等待用户确认(与 OK/Cancel 类似)
- showOptionDialog 显示一条消息并获得用户在一组选项中的选择
- showInputDialog 显示一条消息并获得用户输入的一行文本
- 对话框有下列组件
- 一个图标
- 一条消息
- 一个或多个按钮
- 图标将由五种消息类型决定:
- ERROR_MESSAGE
- INFORMATION_MESSAGE
- WARNING_MESSAGE
- QUESTION_MESSAGE
- PLAIN_MESSAGE
- 可以为每个对话框类型指定一条消息。这里的消息可以是字符串、图标、用户界面组件,也可以是其他类型的对象。
- 步骤:
- 选择对话框的类型
- 选择图标(标准图标或自定义)
- 选择消息(字符串、图表、自定义组件或者它们的集合)
- 对于确认对话框,选择选项类型
- 对于选项对话框,选择选项(字符串、图表、自定义组件)和默认选项
- 对于输入对话框,选择文本框或组合框
- 调用JOptionPane API中的相应方法
- JOptionPane有4个用于显示这些对话框的静态方法:
- 示例程序:
package optionDialog;
import javax.swing.JFrame;
import java.awt.*;
import javax.swing.*;
import java.util.*;
import java.awt.geom.*;
import java.awt.event.*;
public class OptionDialogFrame extends JFrame {
public static void main(String[] args) {
EventQueue.invokeLater(()->{
OptionDialogFrame frame = new OptionDialogFrame();
frame.setTitle("OptionDialogFrame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
});
}
private ButtonPanel typePanel;
private ButtonPanel msgTypePanel;
private ButtonPanel messagePanel;
private ButtonPanel confirmPanel;
private ButtonPanel optionPanel;
private ButtonPanel inputPanel;
private String messageString = "Message";
private Icon messageIcon = new ImageIcon("blue-ball.gif");
private Object messageObject = new Date();
private Component messageComponent = new SampleComponent();
public OptionDialogFrame() {
JPanel gridPanel = new JPanel();
gridPanel.setLayout( new GridLayout(2,3) );
typePanel = new ButtonPanel("Type", "Message", "Confirm", "Option", "Input" );
msgTypePanel = new ButtonPanel("Message Type", "ERROR_MESSAGE", "INFORMATION_MESSAGE",
"WARNING_MESSAGE", "QUESTION_MESSAGE","PLAIN_MESSAGE");
messagePanel = new ButtonPanel("Message", "String", "Icon", "Component",
"Other", "Object[]" );
confirmPanel = new ButtonPanel("Confirm", "DEFAULT_OPTION", "YES_NO_OPTION",
"YES_NO_CANCEL_OPTION","OK_CANCEL_OPTION");
optionPanel = new ButtonPanel("Option", "String[]", "Icon[]", "Object[]");
inputPanel = new ButtonPanel("Input", "Text field", "Combo box");
gridPanel.add(typePanel);
gridPanel.add(msgTypePanel);
gridPanel.add(messagePanel);
gridPanel.add(confirmPanel);
gridPanel.add(optionPanel);
gridPanel.add(inputPanel);
JButton showButton = new JButton("show");
showButton.addActionListener(new ShowAction());
JPanel showPanel = new JPanel();
showPanel.add(showButton);
add(gridPanel, BorderLayout.CENTER);
add(showPanel, BorderLayout.SOUTH);
pack();
}
public Object getMessage() {
String s = messagePanel.getSelection();
if( s.equals("String")) return messageString;
else if(s.equals("Icon")) return messageIcon;
else if(s.equals("Component")) return messageComponent;
else if(s.equals("Object[]")) return new Object[] {messageString, messageIcon,
messageComponent, messageObject };
else if(s.equals("Other")) return messageObject;
else return null;
}
public Object[] getOptions() {
String s = optionPanel.getSelection();
if(s.equals("String[]")) return new String[] {"Yellow", "Blue", "Red" };
else if(s.equals("Icon[]")) return new ImageIcon[] { new ImageIcon("yellow-ball.gif"),
new ImageIcon("blue-ball.gif"), new ImageIcon("red-ball.gif") };
else if(s.equals("Object[]")) return new Object[] { messageString, messageIcon,
messageComponent, messageObject };
else return null;
}
public int getType(ButtonPanel panel) {
String s = panel.getSelection();
try {
return JOptionPane.class.getField(s).getInt(null);
}catch(Exception e ) {
return -1;
}
}
private class ShowAction implements ActionListener{
public void actionPerformed(ActionEvent event) {
if(typePanel.getSelection().equals("Confirm")){
JOptionPane.showConfirmDialog(OptionDialogFrame.this, getMessage(), "Title",
getType(confirmPanel), getType(msgTypePanel));
}else if( typePanel.getSelection().equals("Option")) {
JOptionPane.showOptionDialog(OptionDialogFrame.this, getMessage(),
"Title", getType(confirmPanel), getType(msgTypePanel),
null, getOptions(), getOptions()[0]);
}else if( typePanel.getSelection().equals("Message")) {
JOptionPane.showMessageDialog(OptionDialogFrame.this, getMessage(),
"Title", getType(msgTypePanel) );
}else if( typePanel.getSelection().equals("Input")) {
if( inputPanel.getSelection().equals("Text field")) {
JOptionPane.showInputDialog(OptionDialogFrame.this, getMessage(),
"Title", getType(msgTypePanel));
}else {
JOptionPane.showInputDialog(OptionDialogFrame.this, getMessage(),
"Title", getType(msgTypePanel), null, new String[] { "Yellow", "Blue", "Red"},
"Blue");
}
}
}
}
class SampleComponent extends JComponent{
public void paintComponent(Graphics g) {
Graphics2D graph = (Graphics2D) g;
Rectangle2D.Double rect = new Rectangle2D.Double(0,0, this.getWidth()- 1, this.getHeight() - 1);
graph.setPaint(Color.YELLOW);
graph.fill(rect);
graph.setPaint(Color.BLUE);
graph.draw(rect);
}
public Dimension getPreferredSize() { return new Dimension(10,10); }
}
}
package optionDialog;
import javax.swing.JPanel;
import javax.swing.*;
public class ButtonPanel extends JPanel {
private ButtonGroup group;
public ButtonPanel(String title, String... options ) {
setBorder(BorderFactory.createTitledBorder( BorderFactory.createEtchedBorder(), title));
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
group = new ButtonGroup();
for(String option : options) {
JRadioButton button = new JRadioButton(option);
button.setActionCommand(option);
add(button);
group.add(button);
button.setSelected(option == options[0]);
}
}
public String getSelection() {
return group.getSelection().getActionCommand();
}
}
- 创建对话框
- 要想实现一个对话框,需要从JDialog派生一个类。(默认不可见)
- 在对话框构造器中,调用超类JDialog构造器
- 添加对话框的用户界面组件
- 添加事件处理器
- 设置对话框的大小
- 在调用超类构造器时,需要提供 拥有者框架(owner frame)、对话框标题 以及 模式特征。
- 拥有者框架:控制对话框的显示位置。如果设为null,对话框将由一个隐藏框架所拥有。
- 模式特征:指定对话框处于显示状态时,应用程序的其他窗口是否被锁住。无模式不锁住,模式锁住。
- javax.swing.JDialog
- public JDialog(Frame parent, String title, boolean modal) //modal 为 true代表模式对话框
- 示例程序
- 要想实现一个对话框,需要从JDialog派生一个类。(默认不可见)
package dialog;
import javax.swing.JFrame;
import java.awt.*;
import javax.swing.*;
public class DialogFrame extends JFrame {
public static void main(String[] args) {
EventQueue.invokeLater(()->{
DialogFrame frame = new DialogFrame();
frame.setTitle("DialogFrame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
});
}
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_HEIGHT = 200;
private AboutDialog aboutDialog;
public DialogFrame() {
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
JMenuBar menuBar = new JMenuBar();
JMenu fileMenu = new JMenu("File");
JMenuItem aboutItem = new JMenuItem("About");
aboutItem.addActionListener(event->{
if( aboutDialog == null )
aboutDialog = new AboutDialog(DialogFrame.this);
aboutDialog.setVisible(true);
});
JMenuItem exitItem = new JMenuItem("Exit");
exitItem.addActionListener(event->{
System.exit(0);
});
fileMenu.add(aboutItem);
fileMenu.add(exitItem);
menuBar.add(fileMenu);
add(menuBar, BorderLayout.NORTH);
}
}
package dialog;
import javax.swing.JDialog;
import java.awt.BorderLayout;
import javax.swing.*;
public class AboutDialog extends JDialog {
public AboutDialog(JFrame owner) {
super(owner, "About Dialog", true);
JLabel label = new JLabel("<html><h1><i>Core Java</i></h1><hr>By Cay Horstmann</html>");
add(label, BorderLayout.CENTER);
JPanel panel = new JPanel();
JButton button = new JButton("OK");
button.addActionListener( event->{ this.setVisible(false); } );
panel.add(button);
add(panel, BorderLayout.SOUTH);
pack();
}
}
- 数据交换
- 用户解除模式对话框前,会一直调用setVisible(true)阻塞。
- 很多情况下,一个对话框可能会有多个拥有者框架,所以最好在准备显示对话框时再确定拥有者框架,而不是在构造对象时。技巧:让自定义框架扩展JPanel而不是JDialog,在showDialog方法的时候再动态创建JDialog对象。
- javax.swing.SwingUtilities
- Container getAncestorOfClass(Class c, Component comp) //返回给定组件的最先的父容器。这个组件属于给定的类或者其子类之一。
- javax.swing.JComponent
- JRootPane getRootPane() //获得最靠近这个组件的根窗格,如果这个组件的祖先没有根窗格就返回null。
- javax.swing.JRootPane
- void setDefaultButton(JButton b) //设置根窗格的默认按钮。如果不想设置默认,传入null。
- 示例程序:
package dataExchange;
import javax.swing.JFrame;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class DataExchangeFrame extends JFrame {
public static void main(String[] args) {
EventQueue.invokeLater(()->{
DataExchangeFrame frame = new DataExchangeFrame();
frame.setTitle("DataExchangeFrame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
});
}
private PasswordChooser dialog;
private JTextArea text;
private static final int TEXT_ROWS = 20;
private static final int TEXT_COLUMNS = 40;
private static final int DEFAULT_FONT_SIZE = 18;
public DataExchangeFrame() {
JMenuBar menuBar = new JMenuBar();
JMenu fileMenu = new JMenu("File");
JMenuItem connectItem = new JMenuItem("Connect");
ActionListener listener = new ConnectListener();
connectItem.addActionListener(listener);
JMenuItem exitMenu = new JMenuItem("Exit");
exitMenu.addActionListener(event->System.exit(0));
fileMenu.add(connectItem);
fileMenu.add(exitMenu);
menuBar.add(fileMenu);
add(menuBar, BorderLayout.NORTH);
text = new JTextArea(TEXT_ROWS, TEXT_COLUMNS);
text.setFont(new Font(Font.DIALOG_INPUT, Font.PLAIN, DEFAULT_FONT_SIZE));
JScrollPane scrollPane = new JScrollPane(text);
add(scrollPane, BorderLayout.CENTER);
pack();
}
class ConnectListener implements ActionListener{
public void actionPerformed(ActionEvent event) {
if( dialog == null ) {
dialog = new PasswordChooser();
}
dialog.setUser(new User("yourname", null));
if( dialog.showDialog(DataExchangeFrame.this, "Connect")) {
User u = dialog.getUser();
String s = "username: " + u.getName() + " , password: " + new String(u.getPassword());
text.append(s + "\n");
}
}
}
}
package dataExchange;
import javax.swing.JPanel;
import javax.swing.*;
import java.awt.*;
import java.util.Arrays;
public class PasswordChooser extends JPanel {
private JDialog dialog;
private JTextField userName;
private JPasswordField password;
private JButton okButton;
private JButton cancelButton;
private boolean ok;
public PasswordChooser() {
setLayout(new BorderLayout());
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(2,2));
JLabel userLabel = new JLabel("User name: ");
JPanel userLabelPanel = new JPanel();
userLabelPanel.setLayout(new BorderLayout());
userLabelPanel.add(userLabel, BorderLayout.EAST);
userName = new JTextField("");
JLabel pswLabel = new JLabel("Password: ");
JPanel pswLabelPanel = new JPanel();
pswLabelPanel.setLayout(new BorderLayout());
pswLabelPanel.add(pswLabel, BorderLayout.EAST);
password = new JPasswordField("");
panel.add(userLabelPanel);
panel.add(userName);
panel.add(pswLabelPanel);
panel.add(password);
add(panel, BorderLayout.CENTER);
JPanel buttonPanel = new JPanel();
okButton = new JButton("OK");
okButton.addActionListener(event->{
ok = true;
dialog.setVisible(false);
});
cancelButton = new JButton("Cancel");
cancelButton.addActionListener(event->dialog.setVisible(false));
buttonPanel.add(okButton);
buttonPanel.add(cancelButton);
add(buttonPanel, BorderLayout.SOUTH);
}
public void setUser( User u ) {
userName.setText(u.getName());
}
public User getUser() {
return new User(userName.getText(), password.getPassword() );
}
public boolean showDialog( Component parent, String title ) {
ok = false;
Frame owner = null;
if( parent instanceof Frame ) owner = (Frame) parent;
else owner = (Frame) SwingUtilities.getAncestorOfClass(Frame.class, parent);
if( dialog == null || dialog.getOwner() != owner ) {
dialog = new JDialog(owner, true);
dialog.add(this);
dialog.getRootPane().setDefaultButton(okButton);
dialog.pack();
}
dialog.setTitle(title);
dialog.setVisible(true);
return ok;
}
}
package dataExchange;
import java.util.Arrays;
public class User{
private String username;
private char[] password;
public User( String name, char[] psw ) {
this.username = name;
this.password = psw;
}
public String getName() { return username; }
public char[] getPassword() {
return Arrays.copyOf(password, password.length);
}
}
- 文件对话框
- Swing提供了 JFileChooser类,是一个模式对话框。注:它并不是JDialog的子类。
- 显示需要调用showOpenDialog或者showSaveDialog 。
- 构造器不需要指定父组件。允许在多个框架中 重用 一个文件选择器。(因为JFileChooser的构造器相当耗费时间)
- 建立文件对话框的步骤:
- 建立一个 JFileChooser对象。
- 调用 setCurrentDirectory 设置当前目录。
- 可以调用setSelectedFile方法指定选择的默认文件名。
- 如果允许选择多个文件,可以调用 setMultiSelectionnEnabled方法设置。
- 如果想只显示某一种类型的文件,需要设置文件过滤器。setFileFilter方法。可以一个文件选择器安装多个过滤器,addChooserFileFilter方法。
- 默认,文件选择器只能选择文件。也可以选择目录。
- 调用showOpenDialog或showSaveDialog。调用需提供父组件。仅当用户处理完对话框时才返回调用。
- 调用getSelectedFile或者getSelectedFiles获得选择的文件。如果需要知道文件对象名时,可以调用getPath方法。
- 使用文件对话框的主要困难是:指定用户需要选择的文件子集。需要创建一个实现了抽象类 javax.swing.filechooser.FileFilter的对象。文件选择器将每个文件传递给文件过滤器。实现FileFilter超类的两个方法即可:
- public boolean accept(File f)
- public String getDescription()
- 默认情况下,All files过滤器总是显示在组合框中。
- 如果为加载和保存不同类型的文件重用一个文件选择器,就需要调用:resetChoosableFilter() 添加新文件过滤器之前清除旧文件过滤器。
- 示例程序:
package fileChooser;
import javax.swing.JFrame;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.filechooser.FileFilter;
import java.io.*;
import java.awt.*;
import javax.swing.*;
public class ImageViewerFrame extends JFrame {
public static void main(String[] args) {
EventQueue.invokeLater(()->{
ImageViewerFrame frame = new ImageViewerFrame();
frame.setTitle("ImageViewerFrame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
});
}
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_HEIGHT = 400;
private JLabel label;
private JFileChooser chooser;
public ImageViewerFrame() {
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
JMenu fileMenu = new JMenu("File");
menuBar.add(fileMenu);
JMenuItem openItem = new JMenuItem("Open");
fileMenu.add(openItem);
openItem.addActionListener(event->{
chooser.setCurrentDirectory(new File("."));
int result = chooser.showDialog(ImageViewerFrame.this, "Open");
if( result == JFileChooser.APPROVE_OPTION ) {
String name = chooser.getSelectedFile().getPath();
label.setIcon(new ImageIcon(name));
pack();
}
});
JMenuItem exitItem = new JMenuItem("Exit");
exitItem.addActionListener( event->System.exit(0) );
fileMenu.add(exitItem);
label = new JLabel();
add(label);
chooser = new JFileChooser();
FileFilter filter = new FileNameExtensionFilter("Image files", "jpg", "jpeg", "gif", "png");
chooser.setFileFilter(filter);
chooser.setAccessory(new ImagePreviewer(chooser));
chooser.setFileView( new FileIconView(filter, new ImageIcon("palette.gif") ) );
}
}
package fileChooser;
import javax.swing.JLabel;
import javax.swing.*;
import java.awt.*;
import java.io.*;
public class ImagePreviewer extends JLabel {
public ImagePreviewer( JFileChooser chooser ) {
setPreferredSize(new Dimension(100,100));
setBorder(BorderFactory.createEtchedBorder());
chooser.addPropertyChangeListener(event->{
if(event.getPropertyName() == JFileChooser.SELECTED_FILE_CHANGED_PROPERTY ) {
File f = (File) event.getNewValue();
if( f == null ) {
setIcon(null);
return;
}
ImageIcon icon = new ImageIcon(f.getPath());
if( icon.getIconWidth() > getWidth() ) {
icon = new ImageIcon( icon.getImage().getScaledInstance(getWidth(), -1, Image.SCALE_DEFAULT));
}
setIcon(icon);
}
});
}
}
package fileChooser;
import javax.swing.filechooser.FileView;
import java.io.File;
import javax.swing.*;
import javax.swing.filechooser.*;
import javax.swing.filechooser.FileFilter;
public class FileIconView extends FileView {
private FileFilter fileFilter;
private Icon icon;
public FileIconView(FileFilter filter, Icon icon ) {
fileFilter = filter;
this.icon = icon;
}
public Icon getIcon(File f) {
if( !f.isDirectory() && fileFilter.accept(f) ) return icon;
else return null;
}
}
- GUI程序排错
- 调式技巧
- Swing图形化调试器。使用 JComponent 类的 setDebugGraphicsOptions方法打开一个对Swing组件的调试。
- DebugGraphics.FLASH_OPTION
- ~.LOG_OPTION
- ~.BUFFERED_OPTION
- ~.NONE_OPTION
- Swing图形化调试器。使用 JComponent 类的 setDebugGraphicsOptions方法打开一个对Swing组件的调试。
- 让 AWT机器人完成工作
- Robot类可以向任何AWT程序发送按键和鼠标点击事件。