第4章 JComponent类
JComponent类是所有Swing轻量组件的基类,因此,我们单独用一章对它进行讨论。JComponent 对Swing的意义就如同https://www.doczj.com/doc/f6276738.html,ponent对AWT的意义一样,它们都是它们各自框架组件的基类。
作为所有Swing轻量组件的基类,JComponent提供了大量的基本功能。要全面了解Swing,就必须知道JComponent类提供的功能,还必须知道如何使用JComponent类。
4.1 JComponent类概览
JComponent扩展java.awt.Container,而java.awt.Container又扩展https://www.doczj.com/doc/f6276738.html,ponent,因此,所有的Swing组件都是AWT容器。Component和Container类本身提供了大量的功能,因此,JComponent继承了大量的功能。本章(实际上本书)假设读者有AWT组件和容器的基本知识,这些基本知识在《Java2图形设计,卷Ⅰ:AWT》中用了大量的篇幅来介绍。
因为JComponent为几乎所有的Swing组件提供下层构件,因此,它是一个很大的类,包括100
多个public方法。JComponent为它的扩展提供了下面的功能:
■边框。■自动滚动。
■可访问性。■工具提示。
■双缓存。■键击处理。
■调试图形。■客户属性。
4.1.1 边框
任何JComponent的扩展都可以带边框。Swing提供了许多不同风格的边框,如雕刻边框、带标题边框和蚀刻边框。虽然一个组件只能有一个边框,但是边框是可以组合的。因此,从效果上来看,单个组件可有多个边框。图4-1示出了组合边框、带标题边框和定制边框。
边框通常用来组织组件集,但在其他情况下也是很有用的。例如,图4-1示出的组合框可以作为一显示艺术品略图的图形程序的图片帧。通常,可操作的边框在绘图程序中用来移动和改变对象的大小,而且作为Swing的定制边框,这种边框实现起来也很容易。
本章不详细介绍边框,有关边框的知识,请参见第5章“边框、图标和动作”。
4.1.2 可访问性
可访问性是使人人都能使用软件。例如,为视力不好的用户放大字体或为听力不好的用户显示带声音的标题。
Swing的插入式界面模式体系结构通过允许把可选择的界面样式分配给一组组件来支持可访问性。图4-2所示的SwingSet样例应用程序使用一个定制的界面样式,它具有高反差、大字体的外观,为视力不好的用户提供了更好的可读性。
除了Swing插入式界面模式外,使用一个可访问API和一组可访问工具也能支持可访问性。在第4.11节“支持可访问性”中介绍了可访问性。
4.1.3 双缓存
在更新组件(擦除然后重绘组件)时,会产生可察觉的闪烁。双缓存通过在屏外缓存区中更新组件,然后把屏外缓存区的相应部分拷贝到组件的屏上代表中来消除闪烁。
所有的Swing轻量组件都继承了双缓存它们显示内容的能力。一个屏外缓存(由Swing的
RepaintManager维护)常用于双缓存JComponent的扩展。
图4-3示出了一个篮球图像的简单动画和含有可拖动的轻量组件的应用程序。
除了为双缓存轻量组件使用屏外缓存外,开发人员还可以为拖动轻量组件或实现动画等其他目的而使用屏外缓存。
4.1.4 调试图形
4.1.5 自动滚动
4.1.6 工具提示
4.1.7 键击处理和客户属性
4.2 JComponent类结构
4.2.1 Swing组件是AWT容器
例4-1 作为容器使用的Swing按钮
import javax.swing.*;
import java.awt.*;
public class ButtonAsContainer extends JApplet {
public void init() {
JButton b = new JButton("Swing Buttons Are Containers");
b.setLayout(new FlowLayout());
b.add(new Button("AWT Button"));
b.add(new JButton("Swing Button"));
getContentPane().add(b);
}
}
4.2.2 最小尺寸、最大尺寸和首选尺寸
例4-2 为JComponent显式地设置首选大小
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Test extends JApplet {
JComboBox sizeCombo = new J ComboBox(new Object[] { "null",
"100 x 100"
});
JList list = new JList(new Object[] {
"item 1",
"item 2",
"item 3",
"item 4",
"item 5",
});
public void init() {
final Container contentPane = getContentPane();
list.setBorder(
BorderFactory.createLineBorder(Color.black));
sizeCombo.setSelectedIndex(0);
contentPane.setLayout(new FlowLayout());
contentPane.add(list);
contentPane.add(new JLabel("preferred size for list:")); contentPane.add(sizeCombo);
sizeCombo.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int index = sizeCombo.getSelectedIndex();
if(index == 0)
list.setPreferredSize(null);
else
list.setPreferredSize(
new Dimension(100, 100));
list.revalidate();
}
});
}
}
4.3 绘制JComponent组件
4.3.1 Swing组件中的定制绘制
4.3.2 在AWT组件中重载绘制方法
例4-3 为定制AWT组件而重载paint()
import java.applet.Applet;
import java.awt.*;
public class TestApplet extends Applet {
public void init() {
ImageCanvas imageCanvas = new ImageCanvas("sphere.gif"); //ImageCanvas imageCanvas = new ImageCanvas(
add(imageCanvas);
//setVisible(true);
}
}
class ImageCanvas extends Canvas {
Image image;
public ImageCanvas(String imageName) {
image = Toolkit.getDefaultToolkit().getImage(imageName);
MediaTracker mt = new MediaTracker(this);
try {
mt.addImage(image, 0);
mt.waitForID(0);
}
catch(InterruptedException ex) {
ex.printStackTrace();
}
}
public void paint(Graphics g) {
g.drawIm age(image, 0, 0, null);
}
public Dimension getPreferredSize() {
return new Dimension(image.getWidth(null),
image.getHeight(null));
}
}
4.3.3 在Swing组件中重载绘制方法
例4-4 带有从JComponent派生的图像画布的小应用程序
import javax.swing.*;
import java.awt.*;
public class TestApplet extends JApplet {
public void init() {
Container contentPane = getContentPane();
ImageCanvas imagePanel = new ImageCanvas(
"sphere.gif",
"a blue sphere");
imagePanel.setBorder(
BorderFactory.createTitledBorder("ImageCanvas"));
contentPane.setLayout(new FlowLayout());
contentPane.add(imagePanel);
}
}
例4-5 为定制Swing组件而重载paintComponent()
class ImageCanvas extends J Panel {
ImageIcon icon;
public ImageCanvas(String imageName, String description) {
//icon = new ImageIcon(imageNam e, description);
icon =new ImageIcon(this.getClass().getResource(imageName),description); }
public void paintComponent(Graphics g) {
Insets insets = getInsets();
super.paintComponent(g);
icon.paintIcon(this, g, insets.left, insets.top);
}
public Dimension getPreferredSize() {
Insets insets = getInsets();
return new Dimension(
icon.getIconWidth() + insets.left + insets.right,
icon.getIconHeight() + insets.top + insets.bottom);
}
}
4.3.4 paint、repaint和update方法
4.3.5 validate、invalidate和revalidate方法
4.3.6 不透明组件与透明组件的比较
例4-6 不透明测试小应用程序
public class OpaqueTest extends JApplet {
public void init() {
Container contentPane = getContentPane();
RainPanel rainPanel = new RainPanel();
ColoredPanel opaque = new ColoredPanel(),
transparent = new ColoredPanel();
// JComponents are opaque by default, so the opaque
// property only needs to be set for transparent
transparent.setOpaque(false);
rainPanel.add(opaque);
rainPanel.add(transparent);
contentPane.add(rainPanel, BorderLayout.CENTER);
}
}
例4-7 不容透明Swing组件
import javax.swing.*;
import java.awt.*;
public class OpaqueTest extends JApplet {
public void init() {
Container contentPane = getContentPane();
RainPanel rainPanel = new RainPanel();
ColoredPanel opaque = new ColoredPanel(),
transparent = new ColoredPanel();
// JComponents are opaque by default, so the opaque
// property only needs to be set for transparent
transparent.setOpaque(false);
rainPanel.add(opaque);
rainPanel.add(transparent);
contentPane.add(rainPanel, BorderLayout.CENTER);
System.out.println(opaque.isOpaque());
System.out.println(transparent.isOpaque());
}
}
class RainPanel extends JPanel {
ImageIcon rain = new ImageIcon(this.getClass().getResource("rain.gif")); private int rainw = rain.getIconWidth();
private int rainh = rain.getIconHeight();
public void paintComponent(Graphics g) {
Dimension size = getSize();
for(int row=0; row < size.height; row += rainh)
for(int col=0; col < size.width; col += rainw)
rain.paintIcon(this,g,col,row);
}
}
class ColoredPanel extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
Dimension size = getSize();
g.setColor(Color.black);
g.drawRect(0,0,size.width-1,size.height-1);
g.setColor(Color.red);
g.fillRect(size.width/2-25,size.height/2-25,50,50);
}
public Dimension getPreferredSize() {
return new Dimension(100,100);
}
}
4.3.7 立即绘制Swing组件
例4-8 在事件处理方法中使用paintImmediately()
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class TestApplet extends JApplet {
public void init() {
Container contentPane = getContentPane();
final JPanel panel = new JPanel();
JButton button = new JButton("repaint");
panel.setBackground(Color.blue);
panel.setPreferredSize(new Dimension(100,100));
contentPane.setLayout(new FlowLayout()); contentPane.add(button);
contentPane.add(panel);
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Color c = panel.getBackground();
Dimension sz = panel.getSize();
panel.setBackground(
c == Color.blue ? Color.re
d : Color.blue);
panel.paintImmediately(
0,0,sz.width,sz.height);
// for illustrative purposes only
try {
Thread.currentThread().sleep(5000);
}
catch(InterruptedException ex) {
ex.printStackTrace();
}
}
});
}
}
4.4 双缓存
例4-9 双缓存测试小应用程序
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class DoubleBufferingTest extends JApplet {
public void init() {
final JSlider slider =
new JSlider(JSlider.HORIZONTAL,0,100,50);
final Container contentPane = getContentPane(); JCheckBox dbcheckBox = new JCheckBox("double buffered"); JPanel controlPanel = new JPanel();
dbcheckBox.setSelected(true);
controlPanel.add(dbcheckBox);
slider.setPaintTicks(true);
slider.setMinorTickSpacing(5);
slider.setMajorTickSpacing(15);
contentPane.add(controlPanel, "North");
contentPane.add(slider, "Center");
dbcheckBox.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent event) {
if(event.getStateChange() == ItemEvent.SELECTED) { slider.setDoubleBuffered(true);
}
else {
slider.setDoubleBuffered(false);
}
}
});
}
}
例4-10 为组件的容器禁用双缓存
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class DoubleBufferingTest extends JApplet {
public void init() {
final Container contentPane = getContentPane();
JSlider slider = new JSlider(JSlider.HORIZONTAL,0,100,50); JCheckBox dbcheckBox = new JCheckBox("double buffered"); JPanel controlPanel = new JPanel();
controlPanel.add(dbcheckBox);
slider.setPaintTicks(true);
slider.setMinorTickSpacing(5);
slider.setMajorTickSpacing(15);
contentPane.add(controlPanel, "North");
contentPane.add(slider, "Center");
dbcheckBox.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent event) {
JComponent cp = (JComponent)getContentPane();
JComponent rp = (JComponent)getRootPane();
if(event.getStateChange() == ItemEvent.SELECTED) {
rp.setDoubleBuffered(true);
cp.setDoubleBuffered(true);
}
else {
rp.setDoubleBuffered(false);
cp.setDoubleBuffered(false);
}
}
});
}
}
例4-11 调用RepaintManager.getDoubleBufferingEnabled()
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class DoubleBufferingTest extends JApplet {
public void init() {
Container contentPane = getContentPane();
JCheckBox dbcheckBox = new JCheckBox("double buffered");
JPanel controlPanel = new JPanel();
final JSlider slider =
new JSlider(JSlider.HORIZONTAL,0,100,50);
controlPanel.add(dbcheckBox);
slider.setPaintTicks(true);
slider.setMinorTickSpacing(5);
slider.setMajorTickSpacing(15);
contentPane.add(controlPanel, "North");
contentPane.add(slider, "Center");
dbcheckBox.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent event) { RepaintManager rm =
RepaintManager.currentManager(slider);
if(event.getStateChange() == ItemEvent.SELECTED) { rm.setDoubleBufferingEnabled(true);
}
else {
rm.setDoubleBufferingEnabled(false);
}
}
});
}
}
例4-12 动画测试小应用程序
例4-13 AnimationPane类清单
4.5 调试图形
例4-14 为调试图形使用Log(日志)和Flash(闪烁)选项
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
public class DebugGraphicsTest extends JApplet {
private JSlider slider = new JSlider();
boolean logIsOn = false, flashIsOn = false;
public void init() {
Container cp = getContentPane();
RepaintManager rm =
RepaintManager.currentManager(slider);
rm.setDoubleBufferingEnabled(false);
cp.setLayout(new BoxLayout(cp, BoxLayout.Y_AXIS));
cp.add(slider);
cp.add(makeControlPanel());
slider.setEnabled(false);
}
private JPanel makeControlPanel() {
JPanel controls = new JPanel(),
checkBoxes = new JPanel();
JCheckBox logCheckBox = new JCheckBox("Log"),
flashCheckBox = new JCheckBox("Flash");
JButton repaintButton = new JButton("repaint");
final JSlider flashTimeSlider =
new JSlider(JSlider.HORIZONTAL,0,250,100);
flashTimeSlider.setPaintTicks(true);
flashTimeSlider.setMajorTickSpacing(10);
flashTimeSlider.setMinorTickSpacing(5);
controls.setLayout(new BoxLayout(controls,
BoxLayout.X_AXIS));
checkBoxes.setLayout(new BoxLayout(checkBoxes, BoxLayout.Y_AXIS));
flashTimeSlider.setBorder(
BorderFactory.createTitledBorder("Flash Time")); controls.setBorder(
BorderFactory.createTitledBorder("Controls"));
checkBoxes.add(logCheckBox);
checkBoxes.add(flashCheckBox);
controls.add(repaintButton);
controls.add(flashTimeSlider);
controls.add(checkBoxes);
repaintButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) {
int opts = 0;
if(logIsOn) opts |= DebugGraphics.LOG_OPTION;
if(flashIsOn) opts |= DebugGraphics.FLASH_OPTION;
slider.setDebugGraphicsOptions(opts);
repaint();
}
});
flashTimeSlider.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { DebugGraphics.setFlashTime(
flashTimeSlider.getValue());
}
});
flashCheckBox.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { AbstractButton b = (AbstractButton)e.getSource();
if(b.isSelected()) flashIsOn = true;
else flashIsOn = false;
}
});
logCheckBox.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) { AbstractButton b = (AbstractButton)e.getSource();
if(b.isSelected()) logIsOn = true;
else logIsOn = false;
}
});
return controls;
}
public static void main(String args[]) {
final JFrame f = new JFram e();
JApplet applet = new DebugGraphicsTest();
applet.init();
f.setContentPane(applet.getContentPane());
f.setBounds(100,100,300,175);
f.setTitle("DebugGraphicsTest");
f.setVisible(true);
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent event) {
f.dispose();
System.exit(0);
}
});
}
}
4.6 自动滚动
例4-15 包裹在滚动窗格中的一个列表
import javax.swing.*;
import java.awt.*;
public class TestApplet extends JApplet {
public void init() {
String[] items = { "one", "two", "three",
"four", "five", "six" };
Container contentPane = getContentPane();
JList list = new JList(items);
list.setVisibleRowCount(3);
contentPane.setLayout(new FlowLayout());
contentPane.add(new JScrollPane(list));
}
}
例4-16 自动滚动测试小应用程序
例4-17 AutoscrollViewport列表
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
class AutoscrollViewport extends JViewport {
Point scrollTo = new Point(), last = new Point();
boolean manualDragUnderway = false;
final int increment;
public AutoscrollViewport(Component component, int inc) { this.increm ent = inc;
setView(component);
setAutoscrolls(true);
setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
last.x = e.getPoint().x;
last.y = e.getPoint().y;
manualDragUnderway = true;
}
});
addMouseMotionListener(new MouseMotionAdapter() {
public void mouseDragged(MouseEvent e) {
Point drag = e.getPoint();
Point viewPos = getViewPosition(); Point offset = new Point(drag.x - last.x, drag.y - last.y);
last.x = drag.x;
last.y = drag.y;
if(contains(drag)) {
if(manualDragUnderway) {
scrollTo.x = viewPos.x - offset.x; scrollTo.y = viewPos.y - offset.y; setViewPosition(scrollTo);
}
}
else { // autoscrolling ...
Rectangle bounds = getBounds(); manualDragUnderway = false;
if(drag.x > bounds.x + bounds.width) { // scroll right
viewPos.x -= increment; setViewPosition(viewPos);
}
if(drag.x < 0) {
// scroll left
viewPos.x += increment; setViewPosition(viewPos);
}
if(drag.y > bounds.y + bounds.height) { // scroll down
viewPos.y -= increment; setViewPosition(viewPos);
}
if(drag.y < 0) {
// scroll up
viewPos.y += increment; setViewPosition(viewPos);
}
}
}
});
}
}
4.7 工具提示
例4-18 为一个按钮设置工具提示文本
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class ToolTipTest extends JApplet {
public void init() {
Container contentPane = getContentPane();
JButton button = new JButton("I've got a tooltip");
button.setMnemonic(KeyEvent.VK_G);
button.setToolTipText(
"rather lengthy tooltip text for button");
contentPane.setLayout(new FlowLayout());
contentPane.add(button);
}
}
4.7.1 基于鼠标位置的工具提示
例4-19 根据鼠标位置来显示不同的工具提示
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class ToolTipsBasedOnMousePosition extends JApplet {
public void init() {
Container contentPane = getContentPane();
//ImageMap map = new ImageMap("tiger.gif");
Icon icon = new ImageIcon(this.getClass().getResource("tiger.gif")); ImageMap map = new ImageMap(icon);
contentPane.setLayout(new FlowLayout()); contentPane.add(map);
}
}
class ImageMap extends JLabel {
private Rectangle teeth= new Rectangle(62,203,80,55), nose = new Rectangle(37,164,130,30),
ear = new Rectangle(228,10,65,55),
rEye = new Rectangle(137,103,20,17),
lEye = new Rectangle(65,97,16,15);
//public ImageMap(String imageName) {
public ImageMap(Icon imageName) {
//super(new ImageIcon(imageName));
super(imageName);
setToolTipText("tiger!");
}
public String getToolTipText(MouseEvent e) {
Point p = e.getPoint();
String s = null;
if(teeth.contains(p)) s = "ooooh, big teeth!";
else if(nose.contains(p)) s = "keen sense of smell"; else if(ear.contains(p)) s = "acute hearing";
else if(rEye.contains(p) || lEye.contains(p))
s = "excellent vision";
return s == null ? getToolTipText() : s;
}
}
4.7.2 工具提示的首选位置
4.7.3 定制工具提示的行为
4.7.4 定制工具提示的界面样式
4.8 键击处理
例4-20 处理嵌套键击
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class KeystrokeTest extends JApplet {
private JButton button = new JButton("button");
public void init() {
Container contentPane = getContentPane();
JPanel panel = new JPanel();
JCheckBox checkbox = new JCheckBox("checkbox");
JButton southButton = new JButton("south button"); Listener listener = new Listener();
panel.setBorder(
BorderFactory.createTitledBorder(
("Ancestor of button and checkbox")));
checkbox.registerKeyboardAction(
listener,
KeyStroke.getKeyStroke(KeyEvent.VK_F,0,false), JComponent.WHEN_FOCUSED);
panel.registerKeyboardAction(
listener,
KeyStroke.getKeyStroke(KeyEvent.VK_A,
InputEvent.ALT_MASK, false),
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
southButton.registerKeyboardAction(
listener,
KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, true), JComponent.WHEN_IN_FOCUSED_WINDOW);
panel.add(button);
panel.add(checkbox);
contentPane.add(panel, "Center");
contentPane.add(southButton, "South");