Swing组件提供了对组件周围的边框区域进行定制的功能。为了简单,我们可以使用预定义的八个边框,或者是我们可以创建自己的边框。在本章中,我们将会了解如何最好的使用已存在边框以及如何创建我们自己的边框。
7.1 Some Basics on Woring with Borders
边框是带有标准的setBorder()与getBorder()属性方法的JComponent属性。所以,所有的JComponent子类的Swing组件都具有边框。默认情况下,一个组件并没有与其相关联的自定义边框。(JComponent的getBorder()方法返回null。)相反,组件显示的默认边框是依据当前的观感对于其状态最为合适的边框。例如,对于JButton,对于每一个观感特定不同的边框,边框可以表现为按下,未按下或是禁止。
尽管对于所有的组件初始的边框属性设置为null,我们可以通过调用JComponent的setBorder(Border newValue)方法来修改组件的边框。一旦设置,修改的值就会覆盖当前观感的边框,并且在组件的区域内绘制新边框。如果在稍后的时候,我们希望将边框重新设置为对于状态与观感合适的边框,我们可以将边框属性修改为null,使用setBorder(null)并且调用组件的updateUI()方法。updateUI()方法会通知观感重新设置边框。如果我们没有调用updateUI()方法,则组件将没有边框。
图7-1显示了一个JLabel周围的各种边框设置,通过文本标签来标明边框类型。如何创建不同的边框将会在本章的稍后部分进行讨论。
7.1.1 Exploring the Border Inteface
我们可以在javax.swing.border包中找到Border接口。这个接口构成了所有边框类的基础。这个接口直接由AbstractBorder类实现,这个类是所有预定义的Swing边框类的父类:BevelBorder,CompoundBorder,EmptyBorder,EtchedBorder,LineBorder,MatteBorder,SoftBevelBorder以及TitledBorder。另外一个有趣的类就是BorderFactory类,我们可以在javax.swing包中找到这个类。这个类使用工厂设计模式来创建边框,隐藏了实现细节,并且可以缓存各种选项来优化共同使用。
在这里显示的Border接口由三个方法构成:paintBorder(),getBordernsets()以及isBorderOpaque()。这些方法会在后面的章节中进行描述。
paintBorder()
paintBorder()方法是这个接口的关键方法。其定义如下:
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height)
边框实现的绘制是由这个方法完成的。通常,Border实现首先会询问Insets维度,然后在外层区域的四个边进行绘制,如图7-2所示。如果边框是不透明的,paintBorder()实现必须填充整个内部区域。如果一个边框是不透明的,并没有填充区域,那么这是一个bug并且需要修正。
列表7-1显示了一个简单的paintBorder()实现,这个实现使用比上部与下部略浅的颜色填充左边与右边。
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { Insets insets = getBorderInsets(c); Color color = c.getForeground(); Color brighterColor = color.brighter(); // Translate coordinate space g.translate(x, y); // Top g.setColor(color); g.fillRect(0, 0, width, insets.top); // Left g.setColor(brighterColor); g.fillRect(0, insets.top, insets.left, height-insets.top-insets.bottom); // Bottom g.setColor(color); g.fillRect(0, height-insets.bottom, width, insets.bottom); // Right g.setColor(brighterColor); g.fillRect(width-insets.right, insets.top, insets.right, height-insets.top-insets.bottom); // Translate coordinate space back g.translate(-x, -y); }
当创建我们自己的边框时,我们将会经常发现我们自己在填充相同的非重叠矩形区域。Graphics的translate()方法简化了绘制坐标的指定。无需转换坐标,我们需要通过原始的(x,y)来偏移绘制。
注意:我们不能通过插入g.fillRect(x,y,width,height)来简化,因为这会填充整个组件区域,而不是边框区域。
getBorderInsets()
getBorderInsets()方法会返回在指定的组件c作为Insets对象的周围绘制边框所必须的空间。其定义如下:
public Insets getBorderInsets(Component c)
如图7-2所示,这些内部区域定义了可以绘制边框的合法区域。Component参数可以使得我们使用他的一些属性来决定内部区域的尺寸。
isBorderOpaque()
边框可以是不透明的或是透明的。isBorderOpaque()方法可以返回true或是false来表明边框是哪种形式。其定义如下:
public boolean isBorderOpaque()
当这个方法返回true时,边框需要是非透明的,填充其整个内部区域。当其返回false时,没有绘制的区域将会保持边框所在的组件的背景颜色。
7.1.2 Introducing BorderFactory
现在我们已经基本了解了Border接口是如何工作的,现在我们来了解一下作为简单创建边框方法的BorderFactory类。我们可以在javax.swing包中找到这个类,BorderFactory类提供了一系列的static方法来创建预定义的边框。无需调用不同的边框类的特定构造函数,通过这个工厂类我们几乎可以创建所有的边框。这个工厂类同时可以缓存一些边框的创建从而避免多次重新创建经常使用的边框。这个类的定义如下:
public class BorderFactory { public static Border createBevelBorder( int type); public static Border createBevelBorder( int type, Color highlight, Color shadow); public static Border createBevelBorder( int type, Color highlightOuter, Color highlightInner, Color shadowOuter, Color shadowInner); public static CompoundBorder createCompoundBorder(); public static public static Border createEmptyBorder(); public static Border createEmptyBorder( int top, int left, int bottom, int right); public static Border createEtchedBorder(); public static Border createEtchedBorder(Color highlight, Color shadow); public static Border createEtchedBorder( int type); public static Border createEtchedBorder( int type, Color highlight, Color shadow); public static Border createLineBorder(Color color); public static Border createLineBorder(Color color, int thickness); public static Border createLoweredBevelBorder(); public static MatteBorder createMatteBorder( int top, int left, int bottom, int right, Color color); public static MatteBorder createMatteBorder( int top, int left, int bottom, int right, Icon icon); public static Border createRaisedBevelBorder(); public static TitledBorder createTitledBorder(Border border); public static TitledBorder createTitledBorder(Border border, String title); public static TitledBorder createTitledBorder(Border border, String title, int justification, int position); public static TitledBorder createTitledBorder(Border border, String title, int justification, int position, Font font); public static TitledBorder createTitledBorder(Border border, String title, int justification, int position, Font font, Color color); public static TitledBorder createTitledBorder(String title); }
我们将会在描述特定的边框类型的过程中描述这个类的不同方法。例如,要创建一个具有红线的边框,我们可以使用下面的语句,然后将这个边框关联到一个组件。
Border lineBorder = BorderFactory.createLineBorder(Color.RED);
7.1.3 Starting with AbstractBorder
在我们了解javax.swing.border包中单个的边框之前,一个系统边框需要获得特别的关注:AbstractBorder。正如前面所提到的,AbstractBorder类是其他的预定义边框的父类。
创建AbstractBorder
AbstractBorder有一个构造函数:
public AbstractBorder()
因为AbstractBorder是其他标准边框的父类,这个构造函数实际是为其他边框类自动调用的。
检测AbstractBorder方法
AbstractBorder类提供了Border接口的三个方法实现。
public Insets getBorderInsets(Component c)
AbstractBorder的内部区域是零。每一个预定义的子类要重写getBorderInsets()方法。
public boolean isBorderOpaque()
AbstractBorder的默认非透明属性设置为false。这就意味着如果我们需要绘制类似点划线的边框,组件的背景将会是透明的。许多预定义的子类重写了isBorderOpaque()方法。
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height)
AbstractBorder的绘制边框是空的。所有的子类应该重写这个方法来实际绘制一个边框,也许除了EmptyBorder。
除了提供了Border方法的默认实现以外,AbstractBorder提供了我们可以利用的其他两个功能,或者仅是允许系统使用。首先,还有另外一个需要两个参数Component与Insets的getBorderInsets()方法:
public Insets getBorderInsets(Component c, Insets insets)
在这个方法版本中,并没有创建并返回一个新的Insets对象,所传递的Insets对象首先被修改然后返回。使用这个方法可以避免每次需要查询边框内部区域时创建然后销毁额外的Insets对象。
第二个可用的新方法是getInteriorRectangle(),这个方法有静态与非静态版本。指定了Component,Border,以及四个整数参数,这个方法将会返回一个内部的Rectangle,从而组件可以在边框内部区域内绘制其自身。