阅读 69

核心Swing组件(一)

在第3章,我们简要介绍了JFC/Swing工程组件所用的Model-View-Controller(MVC)模式。在本章中,我们将会开始探讨如何使用许多可用组件中的关键部分。

所有的Swing组件都是以JComponent类为起点的。尽管Swing库的某些部分并不以JComponent类为根,但所有的组件在其继承的某些级别上共享JComponent类作为通用父类。JComponent类定义通用的行为与属性。在本章中,我们将会了解一些通用功能,例如组件绘制,自定义义,工具提示以及变化大小。

随着特定JComponent子孙类被关注 ,我们将会特别了解JLabel,JButton以及JPanel,三个更为广泛使用的Swing组件类。为了组件内显示图像,我们需要理解Icon接口,以及当使用预定义图像时的ImageIcon类与GrayFilter类的支持。另外,我们将会了解AbstractButton类,他是JButton类的父类。所有的AbstractButton的子类所共享的数据模型是ButtonModel接口;我们将会探讨这个接口及其特定实现,DefaultButtonModel。

4.1 JComponent类

JComponent类是所有的Swing组件继承的抽象基类。JComponent类有42个派生子类,每一个都继承了JComponent的功能。图4-1显示了继承层次结构。

尽管JComponent类是所有Swing组件的共同基类,但是Swing工程库中的许多类并不是由JComponent类派生类。这包括所有的高层窗口对象,例如JFrame,JApplet以及JInternalFrame;所有的MVC相关的类;事件处理相关的接口与类;等。所有这些类将会后面的章节中进行讨论。

尽管所有的Swing组件扩展JComponent,JComponent类扩展AWT的Container类,相应的,其扩展AWT的Component类。这就意味着许多的JComponent方面都是由AWT的Component与Container类所共享的。

4.1.1 组件分片

JComponent类定义了许多超出原始的AWT组件集合功能的AWT组件面。这包括自定义绘制行为以及自定义显示设置的不同方法,例如颜色,字体以及其他的客户端设置。

绘制JComponent对象

因为Swing的JComponent类是由Container类扩展而来的,因而会遵循基本的AWT绘制模型:所有的绘制都是通过paint()方法来完成的,而repaint()方法则用来触发更新。然而,许多任务的完成是不同的。JComponent类优化了绘制的许多方面从而改进性能与可扩展性。另外,RepaintManager类可以用来自定义绘制行为。

为了改进绘制性能与扩展性,JComponent将绘制操作分为三个任务。public void paint(Graphics g)方法被分为三个独立的protected方法调用。由调用的顺序,他们依次为paintComponent(g), paintBorder(g)以及paintChildren(g),通过原始的paint()调用传递Graphics参数。组件本身首先通过paintComponent(g)进行绘制。如果我们希望自定义Swing组件的绘制,我们可以重写paintComponent()方法而不是paint()方法。除非我们希望完全替换所有的绘制,我们需要首先调用super.paintComponent(),正如下面所示的,来获得默认的paintComponent()行为。

public class MyComponent extends JPanel {  protected void paintComponent(Graphics g) {    super.paintComponent(g);    // Customize after calling super.paintComponent(g)
  }
  ...
}

paintBorder()与paintChildren()方法是不可重写的。paintBorder()方法绘制组件周围的边框,第7章会对其概念进行更为完全的描述。如果在Swing容器对象内存在组件,则paintChildren()方法会绘制这些组件。

为了优化绘制,JComponent类提供了三个额外的绘制属性:opaque, optimizedDrawingEnabled以及doubleBuffered。其作用如下:

  • Optacity:JComponent的opaque属性定义了一个组件是否透明。当透明时,JComponent容器必须在组件之后绘制背景。为了改进性能,我们可以保留JComponent的不透明物并使得JComponent绘制其背景,而不要依赖于容器来绘制被覆盖的背景。

  • Optimization:optimizedDrawingEanbled属性紧邻的子元素是否可以重叠。如果子元素不可以重叠,可以极大的减少重绘时间。在默认情况下,优化绘制对于绝大多数的Swing组件是允许的,除了JDesktopPane,JLayeredPane以及JViewport。

  • Double buffering:在默认情况下,所有的Swing组件会将他们的绘制操作重复缓存到一个完整的容器层次结构所共享的缓冲区中;也就是,在一个窗体内的所有组件。这极大的改善了绘制性能,因为当允许双缓冲时(通过doubleBuffered属性),只有一个屏幕更新绘制。

JComponent的public void revalidate()方法也提供绘制支持。当这个方法被调用时,组件的高级容器会验证其本身。这与AWT的直接调用高级组件的revalidate()方法不同。

Swing组件绘制加强的最后一个方面就是RepaintManager类。

RepaintManager类

RepaintManager类负责保证当前显示的Swing组件之上的重绘请求的高效,确保当一个区域无效时只更新屏幕的最小“脏”区域。

尽管不能进行自定义,RepaintManager是公开并且提供了一个静态的安装例程来使用自定义管理器:public static void setCurrentManager(RepaintManager manager)。要获得当前的管理器,只需要调用public static void currentmanager(JComponent)方法。参数通常为null,除非我们已经自定义了管理器来提供组件级别的支持。一旦我们拥有管理器在,我们可以做的一件事就是将屏幕缓冲区获取为图像。因为缓冲区就是实际显示在屏幕上的内容,这可以使得我们高效的实现窗体内部的屏幕复制。

Component comp = ... 
RepaintManager manager = RepaintManager.currentManager(null);
Image htmlImage = manager.getOffscreenBuffer(comp, comp.getWidth(),
  comp.getHeight());// orImage volatileImage = manager.getVolatileOffscreenBuffer(comp, comp.getWidth(),
  comp.getHeight());

表4-1显示了RepaintManager的两个属性。他可以使得我们禁止一个组件(层次结构)的所有绘制操作的双缓冲,并且设置最大的双缓冲尺寸,默认为终端用户的屏幕尺寸。

RepaintManager属性

属性名
数据类型

可访问性

doubleBufferingEnabled
boolean

读写

doubleBufferMaximumSize
Dimension

读写

尽管很少实现,提供我们自己的RepaintManager子类确实允许我们自定义屏幕脏区域的绘制机制,或者是当绘制完成时的最少跟踪。重写下面四个方法的一个可以允许我们自定义机制:

public synchronized void addDirtyRegion(JComponent component, int x, int y,  int width, int height)public Rectangle getDirtyRegion(JComponent component)public void markCompletelyClean(JComponent component)public void markCompletelyDirty(JComponent component)

UIDefaults类

UIDefaults类表示为当前的观感所安装的包含显示设置的查询表,例如JList中所用的字体,在JTree节中所显示的颜色或图标。UIDefaults的使用将会在第20章探讨Java可插拨的观感体系结构时进行详细讨论。在这里,我们只是简要介绍UIDefaults表。

当我们创建一个组件时,组件会自动的请求UIManager在UIDefaults表中查找组件所用的当前设置。大多数的颜色,字体相关的组件设置,以及其他的一些与颜色与字体无关的设置,都是可配置的。如果你不喜欢一个特定的设置,我们可以简单的通过更新UIDefaults查询表中的相应项目进行修改。

首先我们需要知道我们希望修改的UIDefaults设置的名字。我们可以在本书的附录A中找到这些设置名字,在这个附录中包含J2SE 5.0中预定义观感的所有已知设置的完整列表。(由于发行版本的不同会略有不同。)另外,包含有每个组件描述的是一个包含UIResource相关属性元素的表。(要查找本书中特定组件部分,请查看内容表或是索引。)

一旦我们知道了设置的名字,我们可以使用UImanager的public static void put(Object key, Object value)方法来存放一个新的设置,其中key是键值字符串。例如,下面的代码会将新创建的按钮的背景颜色改变黑色,而前景色改变红色:

UIManager.put("Button.background", Color.BLACK);
UIManager.put("Button.foreground", Color.RED);

获取UIResource属性

如果我们正在创建自己的组件,或者是我们只需要查看当前的设置值,我们可以请求UIManager。尽管public static Object get(Object key)方法是最为通用的,却需要我们将返回值转换为合适的类型。相对应的,我们可以下列更为特定的getXXX()方法,这些方法会为我们执行錾,从而返回合适的类型:

public static boolean getBoolean(Object key)public static Border getBorder(Object key)public static Color getColor(Object key)public static Dimension getDimension(Object key)public static Font getFont(Object key)public static Icon getIcon(Object key)public static Insets getInsets(Object key)public static int getInt(Object key)public static String getString(Object key)public static ComponentUI getUI(JComponent target)

这是一组接受Locale参数的重载的方法集合。

客户属性

除了UIManager维护一个key/value对设置以外,每一个组件实例还维护一个自己的key/value对集合。这对于维护一个不同于一定观感的组件或者是维护与一个组件关联的数据而不需要新类或是方法来存储这些数据的情况十分有用。

public final void putClientProperty(Object key, Object value)public final Object getClientProperty(Object key)

例如,JTree类具有一个属性通过Metal观感来连接线风格或是显示JTree中的节点。因为设置特定于一个观感,因而向树API中添加一些内容并不合理。相反,我们可以通过下面的代码在一个特定的树实例上设置属性:

tree.putClientProperty("JTree.lineStyle", "None")

然后,当观感是默认的Metal时,树的节点将会用线连接。如果安装了其他的观感,客户属性就会被忽略。图4-2显示具有与不具有线的树。

Swing_4_2

4.1.2 JComopnent属性

我们已经了解了一些不同的JComponent子类所共享的属性。现在是了解JavaBean属性的时候了。表4-2显示了JComponent所定义的完整属性列表,包括由AWT Container为戌Component类所继承的属性。

JComponent属性

属性名数据类型组件访问容器访问JComponent访问
accessibleContextAccessibleContext

只读

N/A

只读

actionMap

ActionMap

N/A

N/A

读写

alignmentX

float

只读

只读

读写

alignmentY

float

只读

只读

读写

ancestorListeners

AncestorListener[]

N/A

N/A

只读

autoscrolls

boolean

N/A

N/A

读写

background

Color

读写绑定

N/A

只写

backgroundSet

boolean

只读

N/A

N/A

border

Border

N/A

N/A

读写绑定

bounds

Rectangle

读写

N/A

N/A

colorModel

ColorModel

只读

N/A

N/A

componentCount

int

N/A

只读

N/A

componentListeners

ComponentListener[]

只读

N/A

N/A

componentOrientation

ComponentOrientation

读写绑定

N/A

N/A

componentPopupMenu

JPopupMenu

N/A

N/A

读写

components

Component[]

N/A

只读

N/A

containerListeners

ContainerListener[]

N/A

只读

N/A

cursor

Cursor

读写

N/A

N/A

cursorSet

boolean

只读

N/A

N/A

debugGraphicsOptions

int

N/A

N/A

读写

displayable

boolean

只读

N/A

N/A

doubleBuffered

boolean

只读

N/A

读写

dropTarget

DropTarget

读写

N/A

N/A

enabled

boolean

读写

N/A

只写绑定

focusable

boolean

读写绑定

N/A

N/A

focusCycleRoot

boolean

N/A

读写绑定

N/A

focusCycleRootAncestor

Container

只读

N/A

N/A

focusListeners

FocusListener[]

只读

N/A

N/A

focusOwner

boolean

只读

N/A

N/A

focusTraversalKeyEnabled

boolean

读写

N/A

N/A

focusTraversalPolicy

FocusTraversalPolicy

N/A

读写绑定

N/A

focusTraversalPolicyProvider

boolean

N/A

读写绑定

N/A

focusTraversalPolicySet

boolean

N/A

只读

N/A

font

Font

读写绑定

只写

只写

fontSet

boolean

只读

N/A

N/A

foreground

Color

读写绑定

N/A

只写

foregroundSet

boolean

只读

N/A

N/A

graphics

Graphics

只读

N/A

只读

graphicsConfiguration

GraphicsConfiguration

只读

N/A

N/A

height

int

只读

N/A

N/A

hierarchyBoundsLIsteners

HierarchyBoundsListener[]

只读

N/A

N/A

hierarchyListeners

HierarchyListener[]

只读

N/A

N/A

ignoreRepaint

boolean

读写

N/A

N/A

inheritsPopupMenu

boolean

N/A

N/A

读写

inputContext

InputContext

只读

N/A

N/A

inputMap

InputMap

N/A

N/A

只读

inputMethodListeners

InputMethodListener[]

只读

N/A

N/A

inputMethodRequests

InputMethodRequests

只读

N/A

N/A

inputVerifiers

InputVerifier

N/A

N/A

读写绑定

insets

Insets

N/A

只读

只读

keyListeners

KeyListener[]

只读

N/A

N/A

layout

LayoutManager

N/A

读写

N/A

lightweight

boolean

只读

N/A

N/A

locale

Locale

读写绑定

N/A

N/A

location

Point

读写

N/A

N/A

locationOnScreen

Point

只读

N/A

N/A

maximumSize

Dimension

读写绑定

只读

读写

maximumSizeSet

boolean

只读

N/A

N/A

minimumSize

Dimension

读写绑定

只读

读写

minimumSizeSet

boolean

只读

N/A

N/A

mouseListeners

MouseListener[]

只读

N/A

N/A

mouseMotionListeners

MouseMotionListener[]

只读

N/A

N/A

mousePosition

Point

只读

N/A

N/A

mousWheelListeners

MouseWheelListener

只读

N/A

N/A

name

String

读写

N/A

N/A

opaque

boolean

只读

N/A

读写绑定

optimizedDrawingEnabled

boolean

N/A

N/A

只读

paintingTile

boolean

N/A

N/A

只读

parent

Container

只读

N/A

N/A

preferredSize

Dimension

读写绑定

只读

读写

preferredSizeSet

boolean

只读

N/A

N/A

propertyChangeListeners

PropertyChangeListener[]

只读

N/A

N/A

registeredKeyStrokes

KeyStroke[]

N/A

N/A

只读

requestFocusEnabled

boolean

N/A

N/A

读写

rootPane

JRootPane

N/A

N/A

只读

showing

boolean

只读

N/A

N/A

size

Dimension

读写

N/A

N/A

toolkit

Toolkit

只读

N/A

N/A

tooltipText

String

N/A

N/A

读写

topLevelAncestor

Container

N/A

N/A

只读

transferHandler

TransferHandler

N/A

N/A

读写绑定

treeLock

Object

只读

N/A

N/A

uiClassID

String

N/A

N/A

只读

valid

boolean

只读

N/A

N/A

validateRoot

boolean

只读

N/A

N/A

verifyInputWhenFocusTarget

boolean

N/A

N/A

只读

vetoableChangeListeners

vetoableChangeListener[]

N/A

N/A

只读

visible

boolean

读写

N/A

只写

visibleRect

Rectangle

N/A

N/A

只读

width

int

只读

N/A

只读

x

int

只读

N/A

只读

y

int

只读

N/A

只读

包括由父结构继承的属性,JComponent共有92个属性。正如这个数字所表明的,JComponent类极为适用于可视化开发。

4.1.3 处理JComponnent事件

所有的JComponent子类共享许多不同类型的事件。大多数的事件类型来自于父类,例如Component与Container。首先我们将会探讨由Container继承而来的PropertyChangeListener。然后我们会了解一下所有的JComponent子类所共享的两种事件处理功能的使用:VetoableChangeListener与AncestorListener。最后,我们来了解一下由Component继承的完全监听器集合。

使用PropertyChangeListener监听组件事件

JComponent类具有一些直接或间接的组件绑定属性。通过将PropertyChangeListener绑定到组件,我们可以监听特定的JComponent属性变化,并进行相应的响应。

public interface PropertyChangeListener extends EventListener {  public void propertyChange(PropertyChangeEvent propertyChangeEvent);
}

为了演示的目的,列表4-1中的PropertyChangeListener演示当监听JButton组件中Action类型属性的变化时我们所需要的行为。属性的变化可以决定执行哪一个if语句块。

package swingstudy.ch04;   import java.beans.PropertyChangeEvent;import java.beans.PropertyChangeListener;   import javax.swing.Action;import javax.swing.Icon;import javax.swing.JButton;   public class ActionChangedListener implements PropertyChangeListener {   private JButton button;   public ActionChangedListener(JButton button) {		this.button = button;
	}   @Override	public void propertyChange(PropertyChangeEvent e) {		// TODO Auto-generated method stub   String propertyName = e.getPropertyName();
		if(e.getPropertyName().equals(Action.NAME)) {
			String text = (String)e.getNewValue();
			button.setText(text);
			button.repaint();
		} 
		else if(propertyName.equals("enabled")) {
			Boolean enabledState = (Boolean)e.getNewValue();
			button.setEnabled(enabledState.booleanValue());
			button.repaint();
		}		else if(propertyName.equals(Action.SMALL_ICON)) {
			Icon icon = (Icon)e.getNewValue();
			button.setIcon(icon);
			button.invalidate();
			button.repaint();
		}
	}   }

使用VetoableChangeListener监听组件事件

VetoableChangeListener是Swing组件所使用的另一个JavaBean监听器。他使用限制属性,而PropertyChangeListener只使用绑定属性。这两个监听器之间的一个关键区别就在于如果监听器不能处理所请求的变化,则public void vetoableChange(PropertyChangeEvent propertyChangeEvent)方法会抛出一个PerpotyVetoException异常。

public interface VetoableChangeListener extends EventListener {  public void vetoableChange(PropertyChangeEvent propertyChangeEvent)    throws PropertyVetoException;
}

使用AncestorListener监听JComponent事件

我们可以使用AncestorListener可以确定组件何时移动,何时可见,以及何时不可见。如果我们允许用户通过在屏幕上移动组件以及由屏幕中移除组件进行屏幕定制,则AncestorListener就十分有用。

public interface  AncestorListener extends EventListener {
  public void ancestorAdded(AncestorEvent ancestorEvent);
  public void ancestorMoved(AncestorEvent ancestorEvent);
  public void ancestorRemoved(AncestorEvent ancestorEvent);
}

为了演示,列表4-2将一个AncestorListener与JFrame的根面板相关联。当程序首次启动时我们会看到Removed, Added以及Move信息。另外,当我们拖动窗体是我们会看Moved消息。

package swingstudy.ch04;   import java.awt.EventQueue;   import javax.swing.JFrame;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;   public class AncestorSample {   /**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub   Runnable runner = new Runnable() {
			public void run() {
				JFrame frame = new JFrame("Ancestor Sample");
				frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);   AncestorListener ancestorListener = new AncestorListener() {
					public void ancestorAdded(AncestorEvent event) {
						System.out.println("Added");
					}
					public void ancestorMoved(AncestorEvent event) {
						System.out.println("Moved");
					}
					public void ancestorRemoved(AncestorEvent event) {
						System.out.println("Removed");
					}
				};   frame.getRootPane().addAncestorListener(ancestorListener);
				frame.setSize(300, 200);
				frame.setVisible(true);
				frame.getRootPane().setVisible(false);
				frame.getRootPane().setVisible(true);
			}
		};   EventQueue.invokeLater(runner);
	}   }

监听JComponent的继承事件

除了监听JComponent的AncestorEvent或是PropertyChangeEvent实际的功能,JComponent由其父类Container与Component继承了监听其他事件的能力。

表4-4列出了十个事件监听器。我们也许我们使用了相当多的JComponent监听器,但是旧版本也可以工作。使用最合适的来解决我们的任务。

JComponent继承的事件监听器


事件监听器

事件对象

Component
ComponentListener

componentHidden(ComponentEvent)
componentMoved(ComponenetEvent)
componentResized(ComponentEvent)
componentShow(ComponentEvent)

Component
FocusListener

focusGained(FocusEvent)
focusLost(FocusEvent)

Component
HierarchyBoundsListener

ancestorMoved(HierarchyEvent)
ancestorResized(HierarchyEvent)

Component
HierarchyListener

hierarchyChanged(HierarchyEvent)

Component
InputMethodListener

carePositionChanged(InputMethodEvent)
inputMethodTextChanged(InputMethodEvent)

Component
KeyListener

keyPressed(KeyEvent)
keyReleased(KeyEvent)
keyTyped(KeyEvent)

Component
MouseListener

mouseClicked(MouseEvent)
mouseEntered(MouseEvent)
mouseExited(MouseEvent)
mousePressed(MouseEvent)
mouseReleased(MouseEvent)

Component
MouseMotionListener

mouseDragged(MouseEvent)
mouseMoved(MouseEvent)

Component
MouseWheelListener

mouseWheelMoved(MouseWheelEvent)

Container
ContainerListener

componentAdd(ContainerEvent)
componentRemoved(ContainerEvent)


文章分类
代码人生
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐