Java基础知识:Swing用户界面组件(下)

  • 菜单
    • 菜单栏(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) //菜单被取消时调用
          • 这种方式不适用于带有加速键的菜单项,因为按下加速键的时候并没有打开菜单,因此动作没有被禁用,所以加速键依旧能够触发菜单项。
    • 示例程序:
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
            • 等等
        • 填充
          • 设置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方法

 

  • 遍历顺序
    • 键盘焦点的遍历顺序
    • 顺序:从左到右,从上到下
    • 容器包含其他容器:若遍历到子容器,则遍历完子容器之后继续
    • 调用 component.setFocusable(false)可以从焦点遍历中删除一个组件。                     
  •  对话框
    • AWT也分为 模式对话框无模式对话框
    • 模式对话框:结束处理前,不允许用户与应用程序的其他窗口进行交互。
    • 无模式对话框:允许用户同时在对话框和应用程序的其他窗口中输入信息。
      • 如何实现自己的对话框编写一个复杂的对话框
      • 对话框之间如何传递数据  
  • 选项对话框
    • JOptionPane4个用于显示这些对话框的静态方法:
      • showMessageDialog  显示一条消息并等待用户点击OK
      • showConfirmDialog   显示一条消息并等待用户确认(与 OK/Cancel 类似)
      • showOptionDialog  显示一条消息并获得用户在一组选项中的选择
      • showInputDialog  显示一条消息并获得用户输入的一行文本
    • 对话框有下列组件
      • 一个图标
      • 一条消息
      • 一个或多个按钮 
    • 图标将由五种消息类型决定:
      • ERROR_MESSAGE
      • INFORMATION_MESSAGE
      • WARNING_MESSAGE
      • QUESTION_MESSAGE
      • PLAIN_MESSAGE
    • 可以为每个对话框类型指定一条消息。这里的消息可以是字符串、图标、用户界面组件,也可以是其他类型的对象
    • 步骤:
      • 选择对话框的类型
      • 选择图标(标准图标或自定义)
      • 选择消息(字符串、图表、自定义组件或者它们的集合)
      • 对于确认对话框,选择选项类型
      • 对于选项对话框选择选项(字符串、图表、自定义组件)和默认选项
      • 对于输入对话框,选择文本框或组合框
      • 调用JOptionPane API中的相应方法
  • 示例程序:
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代表模式对话框
    • 示例程序
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的构造器相当耗费时间)
  • 建立文件对话框的步骤:
  1. 建立一个 JFileChooser对象。
  2. 调用 setCurrentDirectory 设置当前目录
  3. 可以调用setSelectedFile方法指定选择的默认文件名
  4. 如果允许选择多个文件,可以调用 setMultiSelectionnEnabled方法设置。
  5. 如果想只显示某一种类型的文件,需要设置文件过滤器。setFileFilter方法。可以一个文件选择器安装多个过滤器,addChooserFileFilter方法
  6. 默认,文件选择器只能选择文件。也可以选择目录
  7. 调用showOpenDialog或showSaveDialog。调用需提供父组件。仅当用户处理完对话框时才返回调用
  8. 调用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
  • 让 AWT机器人完成工作
    • Robot类可以向任何AWT程序发送按键和鼠标点击事件。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java核心技术 卷Ⅰ:基础知识 【中文】 第1章概述Java与其他程序设计语言不同的性能。解释这种语言的设计初衷,以及在哪些方 面达到了预期的效果。然后,简要叙述Java诞生和发展的历史。 第2章详细地论述如何下载和安装JDK以及本书的程序示例。然后,通过编译和运行三个典 型的Java程序(一个控制台应用、一个图形应用、一个applet),指导读者使用简易的JDK、可 启用Java的文本编辑器以及一个Java IDE。 第3章开始讨论Java 语言。这一章涉及的基础知识有变量、循环以及简单的函数。对于C或 C++程序员来说,学习这一章的内容将会感觉一帆风顺,因为这些语言特性的语法本质上与C 语言相同。对于没有C语言程序设计背景,但使用过其他程序设计语言(Visual Basic)的程序 员,仔细地阅读这一章是非常必要的。 第4章介绍面向对象程序设计(Object-Oriented Programming, OOP)是当今程序设计的主 流,而Java 是完全面向对象的。本章将介绍面向对象两个基本成分中最重要的—封装,以及 Java 语言实现封装的机制,即类与方法。除了Java 语言规则之外,还对如何正确地进行OOP设 计给出了忠告。最后,介绍奇妙的Javadoc 工具,它将代码注释转换为超链接的网页。熟悉 C++的程序员可以快速地浏览这一章,而没有面向对象程序设计背景的程序员,应在进一步学 习Java之前花一些时间了解OOP的有关概念。 第5章介绍类与封装仅仅是OOP中的一部分,本章将介绍另一部分—继承。继承使程序员 可以使用现有的类,并根据需要进行修改。这是Java程序设计中的基础。Java中的继承机制与 C++的继承机制十分相似。C++程序员只需关注两种语言的不同之处即可。 第6章展示如何使用Java的接口。接口可以让你的理解超越第5章的简单继承模型。掌握接 口的使用将可以获得Java完全的面向对象程序设计的能力。本章还将介绍Java的一个有用的技 术特性—内部类。内部类可以使代码更清晰、更简洁。 第7章开始细致地讨论应用程序设计。每一个Java程序员都应该了解一些图形用户界面程序 设计的知识,本卷中包含了其中的基本内容部分。本章将展示如何制作窗口、如何在窗口中绘 图、如何用几何图形作画、如何用多种字体格式化文本以及如何显示图像。 第8章详细讨论AWT(Abstract Window Toolkit )的事件模型。我们将介绍如何编写代码来响 V 应鼠标点击或敲击键盘等事件。同时,还将介绍如何处理基本的GUI元素,比如:按钮和面板。 第9章详细讨论Swing GUI 工具箱。Swing工具箱允许建立一个跨平台的图形用户界面。本 章将介绍如何建立各种各样的按钮、文本组件、边界、滑块、列表框、菜单以及对话框等等。 一些更高级的组件将在卷II中讨论。 第10章阐述如何部署自己编写的应用程序或applet。在这里将描述如何将应用程序打包到 JAR 文件中,以及如何使用Java的Web Start 机制在Internet上发布应用程序。最后,将解释 Java程部署之后如何存储、检索配置信息。 第11章讨论异常处理,即Java的健壮机制,它用于处理调试好的程序可能出现的意外的情 况。异常提供了一种将正常的处理代码与错误处理代码分开的有效手段。当然,即使程序包含 处理所有异常情况的功能,依然有可能无法按照预计的方式工作。这一章的后半部分,将给出 大量的实用调试技巧。最后,讲述如何使用各种工具完成一个示例程序。 第12章概要介绍泛型程序设计,这是Java SE5.0的一项重要改进。泛型程序设计使得程序 拥有更好的可阅读性和安全性。在这里,将展示如何使用强类型机制,而舍弃不安全的强制类 型转换,以及如何处理与旧版本Java兼容而带来的复杂问题。 第13章介绍Java平台的集合框架。当需要将大量对象收集到一起,并在过后要对它们进行 检索时,可能会想要使用集合,这是目前最为合适的,它取代了将这些元素放置在数组中。本 章将介绍如何使用预先建立好的标准集合。 第14章是本书的最后一章。在这章中,将介绍多线程,这是一种可以让程序任务并行执行 的特性(线程是程序中的控制流),并阐述如何建立线程、如何处理线程的同步问题。从Java SE 5.0开始,多线程有了很大的改进,本章将介绍所有这些新的机制

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值