9.4 JColorChooser类
我们可以将JColorChooser认为是一个只可以输入的JOptionPane,其输入域要求我们选择一种颜色。与JOptionPane类似,JColorChooser也仅是位于窗口中的一堆组件,而并不是一准备好用来使用的弹出窗口。图9-18显示在了我们自己的程序窗口中JColorChooser的样子。在顶部是三个可选择的颜色选择面板;在底部是一个预览面板。其中“I Love Swing”并不是选择器的一部分,而包含选择器的程序所有的。
除了可以在我们的程序窗口显示以外,JColorChooser同时也为自动放置在JDialog的组件集合中提供了支持方法。图9-19显示了一个以这种方式自动创建的弹出窗口。
为了支持这种行为,JColorChooser需要位于javax.swing.colorchooser包中的一些支持类的帮助。JColorChooser的数据模型是ColorSelectionModel接口的一种实现。javax.swing.colorchooser包提供了DefaultColorSelectionModel类作为ColorSelectionModel接口的实现。对于用户界面,JColorChooser依赖ColorChooserComponentFactory来创建选择颜色的默认面板。这些面板是AbstractColorChooserPanel类的特殊子类,如果我们不喜欢默认的集合,我们也可以自己创建。
默认情况下,当在一个JColorChooser中有有多个选择器面板时,每一个面板显示在JTabbedPane的一个标签上。然而,ColorChooserUI可以以他要求的任何方式处理多个面板。
9.4.1 创建JColorChooser
如果我们希望创建一个JColorChooser,并且将其放在我们自己的窗口,我们可以使用下列JColorChooser类的三个构造函数中的一个:
public JColorChooser() JColorChooser colorChooser = new JColorChooser(); public JColorChooser(Color initialColor) JColorChooser colorChooser = new JColorChooser(aComponent.getBackground()); public JColorChooser(ColorSelectionModel model) JColorChooser colorChooser = new JColorChooser(aColorSelectionModel);
默认情况下,选择器的初始颜色为白色。如果我们不希望白色作为默认颜色,我们可以使用Color对象或是ColorSelectionModel来提供初始颜色。
9.4.2 使用JColorChooser
一旦我们应用构造函数创建了一个JColorChooser,我们可以将其放在任何容器中,类似于其他的组件。例如,列表9-7所示的源码创建了一个前面图9-18所示的GUI。
package swingstudy.ch09; import java.awt.BorderLayout; import java.awt.EventQueue; import java.awt.Font; import javax.swing.BorderFactory; import javax.swing.JColorChooser; import javax.swing.JFrame; import javax.swing.JLabel; public class ColorSample { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Runnable runner = new Runnable() { public void run() { JFrame frame = new JFrame("JColorChooser Popup"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); final JLabel label = new JLabel("I Love Swing", JLabel.CENTER); label.setFont(new Font("Serif", Font.BOLD | Font.ITALIC, 48)); frame.add(label, BorderLayout.SOUTH); final JColorChooser colorChooser = new JColorChooser(label.getBackground()); colorChooser.setBorder(BorderFactory.createTitledBorder("Pick Foreground Color")); frame.add(colorChooser, BorderLayout.CENTER); frame.pack(); frame.setVisible(true); } }; EventQueue.invokeLater(runner); } }
尽管上面的代码创建了GUI,但是在JColorChooser中选择不同的颜色并不会做任何事情。下面我们看一下使得颜色变化的代码。
监听颜色选择变化
JColorChooser使用ColorSelectionModel作为其数据模型。正如下面的接口定义所示,数据模型只包含一个属性,selectedColor,用来管理颜色选择器的状态。
public interface ColorSelectionModel { // Listeners public void addChangeListener(ChangeListener listener); public void removeChangeListener(ChangeListener listener); // Properties public Color getSelectedColor(); public void setSelectedColor(Color newValue); }
当用户改变了JColorChooser中的颜色,selectedColor属性发生变化,并且JColorChooser生成一个ChangeEvent来通知所注册的ChangeListener对象。
所以,要完成前一节中的ColorSample示例,并且当用户修改JColorChooser中的颜色选择时使得标签的前景色发生变化,我们需要向颜色选择器注册一个ChangeListener。这涉及到创建一个ChangeListener并将其添加到ColorSelectionModel中。将列表9-8中所示的代码添加到前面的9-7代码中的相应位置。
ColorSelectionModel model = colorChooser.getSelectionModel(); ChangeListener changeListener = new ChangeListener() { public void stateChanged(ChangeEvent changeEvent) { Color newForegroundColor = colorChooser.getColor(); label.setForeground(newForegroundColor); } }; model.addChangeListener(changeListener);
一旦添加了这段代码,这个示例就完成了。运行这个程序会出现图9-18所示的颜色选择器,并且选择一个新的地修改标签的前景色。
创建并显示一个JColorChooser弹出窗口
尽管前面的例子对于如果我们仅是希望在我们程序中包含一个JColorChooser的情况来说已经足够了,但是更多的时候,我们希望JColorChooser在一个单独的弹出窗口中显示。这个窗口看起来像是我们在屏幕上选择一个按钮或者是选择一个菜单项目的结果。为了支持这种行为,JColorChooser包含下列的工厂方法:
public static Color showDialog(Component parentComponent, String title, Color initialColor)
当调用这个方法时,showDialog()会使用指定的父组件与标题创建一个模态对话框。在这个对话框中是一个给定了初始颜色值的JColorChooser。正如我们可以在图9-18中所看到的,在底部是三个按钮:OK,Cancel与Rest。当OK按钮被按下时,弹出窗口会消失,而showDialog()方法会返回当前选中的颜色值。当Cancel按钮被按下时,此方法会返回null,而不会返回所选择的颜色值或是初始颜色值。Reset按钮的选择会使得JColorChooser将其所选中的颜色修改为在启动时所提供的初始颜色。
showDialog()方法的通常作用过程是其初始颜色参数是一个对象的某个颜色属性。然后方法调用的返回值变为相同颜色属性的新值。这种模式用法显示在下面的代码行中,其中颜色属性的变化是一个按钮的背景颜色属性值。类似于JOptionPane,null父组件参数会使得弹出窗口位于屏幕的中间,而不是重叠在某个特定的组件之上。
Color initialBackground = button.getBackground(); Color background = JColorChooser.showDialog( null, "Change Button Background", initialBackground); if (background != null) { button.setBackground(background); }
可以将这段代码放在完整的示例程序中,列表9-9显示了这样的一个示例程序,其中提供了一个按钮,当选中时会显示一个JColorChooser。在OK按钮被选中之后颜色选择器中选中的颜色变为按钮的背景色。
package swingstudy.ch09; import java.awt.BorderLayout; import java.awt.Color; import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JColorChooser; import javax.swing.JFrame; public class ColorSamplePopup { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Runnable runner = new Runnable() { public void run() { JFrame frame = new JFrame("JColorChooser Sample Popup"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); final JButton button = new JButton("Pick to Change Background"); ActionListener actionListener = new ActionListener() { public void actionPerformed(ActionEvent event) { Color initialBackground = button.getBackground(); Color background = JColorChooser.showDialog(null, "Change Button Background", initialBackground); if(background != null) { button.setBackground(background); } } }; button.addActionListener(actionListener); frame.add(button, BorderLayout.CENTER); frame.setSize(300, 100); frame.setVisible(true); } }; EventQueue.invokeLater(runner); } }
提供我们自己的OK/Cancel事件监听器
如果showDialog()方法提供了太多了自动行为,我们也许会更喜欢另一种JColorChooser方法 ,这种方法允许我们在显示之前自定义JColorChooser并且定义当选择OK与Cancel按钮时的所发生的事件。
public static JDialog createDialog(Component parentComponent, String title, boolean modal, JColorChooser chooserPane, ActionListener okListener, ActionListener cancelListener)
在createDialog()方法,父组件与标题参数与showDialog()方法相同。modal参数使得弹出窗口可以是非模态的,这与showDialog()不同,后者弹出窗口总是模态的。当弹出窗口是非模态时,用户仍然可以与程序的其他部分交互。弹出窗口中的OK与Cancel按钮有一个相关联的ActionListener,从而在选择之后可以隐藏弹出窗口。如果我们需要选择之后的额外响应,则我们要负责添加我们自己的监听器。
为了演示createDialog()的正确使用,列表9-10中的程序重复了列表9-9中的程序的功能。然而,如果新的背景色与前景色相同,则颜色修改会被拒绝,而不会自动接受新的颜色值。另外,如果用户选择Cancel按钮,按钮的背景色会被设置为红色。
package swingstudy.ch09; import java.awt.BorderLayout; import java.awt.Color; import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JColorChooser; import javax.swing.JDialog; import javax.swing.JFrame; public class CreateColorSamplePopup { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Runnable runner = new Runnable() { public void run() { JFrame frame = new JFrame("JColorChooser Create Popup Sample"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); final JButton button = new JButton("Pick to Change Background"); ActionListener actionListener = new ActionListener() { public void actionPerformed(ActionEvent event) { Color initialBackground = button.getBackground(); final JColorChooser colorChooser = new JColorChooser(initialBackground); // for okay selection, change button background to selected color ActionListener okActionListener = new ActionListener() { public void actionPerformed(ActionEvent event) { Color newColor = colorChooser.getColor(); if(newColor.equals(button.getForeground())) { System.out.println("Color change rejected"); } else { button.setBackground(newColor); } } }; // for cancel selection, change button background to red ActionListener cancelActionListener = new ActionListener() { public void actionPerformed(ActionEvent event) { button.setBackground(Color.RED); } }; final JDialog dialog = JColorChooser.createDialog(null, "Change Button Background", true, colorChooser, okActionListener, cancelActionListener); // wait for current event dispatching to complete before showing Runnable showDialog = new Runnable() { public void run() { dialog.setVisible(true); } }; EventQueue.invokeLater(showDialog); } }; button.addActionListener(actionListener); frame.add(button, BorderLayout.CENTER); frame.setSize(300, 100); frame.setVisible(true); } }; EventQueue.invokeLater(runner); } }
注意,actionPerformed()方法使用EventQueue.invokeLate()方法来显示选择器。当前的事件处理需在显示选择器之前完成。否则,在选择器显示之前,前一个动作的事件处理还没有完成。
9.4.3 JColorChooser属性
表9-8列出了JColorChooser的八个属性的信息,包括color属性的三个数据类型。
color属性比较特殊,因为他有三种设置的方法:
- 直接由Color设置
- 由一个使用0xAARRGGBB分配形式将红绿蓝值组合在一起的int变量来设置,其中A表示alpha值(被忽略,使用255替换)
- 由分别表示红,绿,蓝颜色组件的单独三个int变量设置
如果我们没有使用showDialog(),我们可以在JColorChooser显示之前进行自定义。除了自定义color属性以外,color属性可以在JColorChooser构造函数中设置,我们还可以自定义在预览区域显示的组件以及选择器面板。
修改预览面板
ColorChooserComponentFactory类负责为JColorChooser的预览区域提供默认的组件。对于标准的观感类型,预览面板位于颜色选择器的底部。
如果我们不需要颜色选择器中的预览面板,我们必须将previewPanel属性设置为一个不为null的组件值。当这个属性设置为null时,则会显示观感的默认预览面板。将这个属性设置为一个空的JPanel可以实现不显示预览面板的目的。
colorChooser.setPreviewPanel(new JPanel());
图9-20显示了一个不带预览面板的颜色选择器的样子。因为当JPanel内没有任何内容时,JPanel没有尺寸,这就有效的移除的面板。
如果我们希望显示预览面板,但是并不喜欢默认的外观,我们可以向这个区域添加我们自己的JComponent。配置要求我们将我们的新预览面板放在一个带有标题边框的容器内,并且当用户选择一个新的颜色时预览面板的前景色发生变化。
注意,ColorChooserUI实现类(BasicColorChooserUI)中的bug要求额外的步骤来安装预览面板。除了调用setPreviewPanel(newPanel)之外,我们必须设置面板的尺寸与边框,从而使得用户界面正确的配置新的预览面板。
下面的代码演示了使用JLabel作为自定义预览面板。图9-21演示了使用了这种预览面板后JColorChooser的样子。
final JLabel previewLabel = new JLabel("I Love Swing", JLabel.CENTER); previewLabel.setFont(new Font("Serif", Font.BOLD | Font.ITALIC, 48)); previewLabel.setSize(previewLabel.getPreferredSize()); previewLabel.setBorder(BorderFactory.createEmptyBorder(0,0,1,0)); colorChooser.setPreviewPanel(previewLabel);
注意,因为预览面板的前景色的初始设置为其背景色,所以面板看起来是空的。这也就是默认预览面板使用限制的背景色显示文本的原因。
修改颜色选择器面板
JColorChooser上部的各种标签表示AbstractColorChooserPanel实现。每一个标签都允许用户以一种不同的方式选择颜色。默认情况下,ColorChooserComponentFactory提供具有三个面板的JColorChooser(如图9-22):
- 样本面板允许用户由一个预定义的颜色样本集合中选择颜色,就如同在一个颜料店一样。
- HSB面板允许用户使用色调饱和度明亮度的颜色模式选择颜色。
- RGB面板允许用户使用红绿蓝颜色模式选择颜色。
如果我们不喜欢默认的选择器面板,或者是我们希望添加其他工作方式不同的颜色选择器面板,我们可以通过继承AbstractColorChooserPanel类来创建我们自己的面板。要将新面板添加到已存在集合中,我们可以调用下面的方法:
public void addChooserPanel(AbstractColorChooserPanel panel)
如果稍后我们决定不再使用新面板,我们可以使用下面的方法来移除:
public AbstractColorChooserPanel removeChooserPanel(AbstractColorChooserPanel panel)
要替换已存在的面板集合,可以调用下面的方法:
setChooserPanels(AbstractColorChooserPanel panels[ ])
创建一个新面板要求我们继承AbstractColorChooserPanel,并且为新面板填充颜色选择的细节。下面的代码行显示了这个类的定义,其中包括了五个抽象方法。这五个方法是我们必须重写的。
public abstract class AbstractColorChooserPanel extends JPanel { public AbstractColorChooserPanel(); protected abstract void buildChooser(); protected Color getColorFromModel(); public ColorSelectionModel getColorSelectionModel(); public int getDisplayMnemonicIndex(); public abstract String getDisplayName(); public abstract Icon getLargeDisplayIcon(); public int getMnemonic(); public abstract Icon getSmallDisplayIcon(); public void installChooserPanel(JColorChooser); public void paint(Graphics); public void uninstallChooserPanel(JColorChooser); public abstract void updateChooser(); }
为了演示如何使用颜色选择器面板,下面我们来看一下如何来创建显示Color与SystemColor类中的颜色列表的颜色选择面板。由这个列表中,用户必须选择一个颜色。面板使用JComboBox来表示颜色列表。(JComboBox的使用会在第13章进行详细解释)图9-23显示了完成的面板。面板是使用下面的代码创建并添加的:
SystemColorChooserPanel newChooser = new SystemColorChooserPanel();
AbstractColorChooserPanel chooserPanels[] = {newChooser};
colorChooser.setChooserPanels(chooserPanels);
要定义的第一个方法是public String getDisplayName()。这个方法返回一个当存在多个选择器面板时在Tab上显示的文本标签。如果只有一个选择面板,这个名字不会显示。
public String getDisplayName() { return "SystemColor"; }
由两个Icon方法返回的值与系统的观感类型没有任何关系。我们可以由这两个方法中返回null,或者是返回与这两个方法无关的Icon来检测。自定义的ColorChooserUI需要使用这两个Icon方法,通常用于在选择器面板Tab页上的图标。
public Icon getSmallDisplayIcon() { return new DiamondIcon(Color.BLUE); } public Icon getLargeDisplayIcon() { return new DiamondIcon(Color.GREEN); }
protected void buildChooser()方法是由AbstractColorChooserPanel的installChooserPanel()方法在面板被添加到选择器时调用的。我们使用这个方法来向容器添加必要的组件。在示例SystemColorChooserPanel选择器中,这个涉及到创建JComboBox并将其添加到面板。因为AbstractColorChooserPanel是一个JPanel子类,我们就可以使用add()方法添加组合框。组合框必须使用选项进行填充,并且必须安装一个事件处理器用于用户选择组件时的事件处理。事件处理的代码在下面的代码块之后描述。
protected void buildChooser() { comboBox = new JComboBox(labels); comboBox.addItemListener(this); add(comboBox); }
注意,另外,如果我们选择重写uninstallChooserPanel(JColorChooser enclosingChooser),我们需要最后调用super.uninstallChooserPanel(JColorChooser enclosingChooser),而不是先调用。
当用户修改AbstractColorChooserPanel中的颜色时,面板必须通知颜色变化的ColorSelectionModel。在SystemColorChooserPanel面板中,这等同于用户在JComboxBox中选择一个新的选项。所以,当复选框的值发生变化时,确定与选项等同的Color,然后通知模型相应的变化。
public void itemStateChanged(ItemEvent itemEvent) { int state = itemEvent.getStateChange(); if (state == ItemEvent.SELECTED) { int position = findColorLabel(itemEvent.getItem()); // Last position is bad (not selectable) if ((position != NOT_FOUND) && (position != labels.length-1)) { ColorSelectionModel selectionModel = getColorSelectionModel(); selectionModel.setSelectedColor(colors[position]); } } }
最后要实现的AbstractColorChooserPanel方法是public void updateChooser()。这个方法也是在启动时由installChooserPanel()方法来调用的。另外,当JColorChooser的ColorSelectionModel发生变化时也会调用这个方法。当updateChooser()方法被调用时,选择器面板必须更新其显示来显示当前被选中的模型颜色。并不是所有的面板都显示当前被选中的是哪一个颜色,所以调用也许会不做任何事情。(系统提供的样品面板就是一个不显示当前颜色的面板。)另外,也有可能当前的颜色在面板上不能显示。例如,在SystemColorChooserPanel中,如果当前选择并不是一个SystemColor或是Color常量,我们可以选择不做任何事情或是显示一些内容来表示自定义的颜色。所以,在updateChooser()实现中,我们需要由ColorSelectionModel中获取当前的颜色,并且修改面板的颜色。实际的设置是通过一个为setColor(Color newValue)的助手方法来实现的。
public void updateChooser() { Color color = getColorFromModel(); setColor(color); }
setColor(Color newValue)方法简单使用由findColorPosition(Color newColor)返回的位置在一个查询表中查询颜色。
// Change combo box to match color, if possible private void setColor(Color newColor) { int position = findColorPosition(newColor); comboBox.setSelectedIndex(position); }
findColorLabel(Object label)与findColorPosition(Color newColor)的细节将会在稍后的列表9-11的完整源码中进行显示。
如果我们不使用显示选择器弹出容器的showDialog()方法,一旦选择器面板已经被定义,而且我们已经创建了一个选择器面板,他可以被通过addChooserPanel()方法放入JColorChooser中。
AbstractColorChooserPanel newChooser = new SystemColorChooserPanel(); colorChooser.addChooserPanel(newChooser);
在显示JColorChooser并且选择相应的Tab页之后,我们的新选择器就可以使用,如图9-24所示。
SystemColorChooserPanel的完整源码显示在列表9-11中。这个程序应该使用ComboBoxModel来将示例的labels与colors数组存储在一个数据模型中。然而,使用JComboBox的MVC功能的复杂性将会在第13章讨论。我们可以自由的修改示例从而为JComboBox或者是其他可用的集合API类使用合适的数据模型。
package swingstudy.ch09; import java.awt.Color; import java.awt.SystemColor; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import javax.swing.Icon; import javax.swing.JComboBox; import javax.swing.colorchooser.AbstractColorChooserPanel; import javax.swing.colorchooser.ColorSelectionModel; import swingstudy.ch04.DiamondIcon; public class SystemColorChooserPanel extends AbstractColorChooserPanel implements ItemListener{ private static int NOT_FOUND = -1; JComboBox comboBox; String labels[] = { "BLACK", "BLUE", "CYAN", "DARK_GRAY", "GRAY", "GREEN", "LIGHT_GRAY", "MAGENTA", "ORANGE", "PINK", "RED", "WHITE", "YELLOW", "activeCaption", "activeCaptionBorder", "activeCaptionText", "control", "controlDkShadow", "controlHighlight", "controlLtHighlight", "controlShadow", "controlText", "desktop", "inactiveCaption", "inactiveCaptionBorder", "inactiveCaptionText", "info", "infoText", "menu", "menuText", "scrollbar", "text", "textHighlight", "textHighlightText", "textInactiveText", "textText", "window", "windowBorder", "windowText", "" }; Color colors[] = { Color.BLACK, Color.BLUE, Color.CYAN, Color.DARK_GRAY, Color.GRAY, Color.GREEN, Color.LIGHT_GRAY, Color.MAGENTA, Color.ORANGE, Color.PINK, Color.RED, Color.WHITE, Color.YELLOW, SystemColor.activeCaption, SystemColor.activeCaptionBorder, SystemColor.activeCaptionText, SystemColor.control, SystemColor.controlDkShadow, SystemColor.controlHighlight, SystemColor.controlLtHighlight, SystemColor.controlShadow, SystemColor.controlText, SystemColor.desktop, SystemColor.inactiveCaption, SystemColor.inactiveCaptionBorder, SystemColor.inactiveCaptionText, SystemColor.info, SystemColor.infoText, SystemColor.menu, SystemColor.menuText, SystemColor.scrollbar, SystemColor.text, SystemColor.textHighlight, SystemColor.textHighlightText, SystemColor.textInactiveText, SystemColor.textText, SystemColor.window, SystemColor.windowBorder, SystemColor.windowText, null }; // change combo box to match color, if possible private void setColor(Color newColor) { int position = findColorPosition(newColor); comboBox.setSelectedIndex(position); } // given a label, find the position of the label in the list private int findColorLabel(Object label) { String stringLabel = label.toString(); int position = NOT_FOUND; for(int i=0, n=labels.length; iif(stringLabel.equals(labels[i])) { position = i; break; } } return position; } // given a color, find the position whose color matches // this could result in a position different from original if two are equal // since actual color is same, this is considered to be okay private int findColorPosition(Color color) { int position = colors.length-1; // cannot use equals() to compare Color and SystemColor int colorRGB = color.getRGB(); for(int i=0, n=colors.length; iif((colors[i] != null) && (colorRGB == colors[i].getRGB())) { position = i; break; } } return position; } public void itemStateChanged(ItemEvent event) { int state = event.getStateChange(); if(state == event.SELECTED) { int position = findColorLabel(event.getItem()); // last position is bad(not selectable) if((position != NOT_FOUND) && (position != labels.length-1)) { ColorSelectionModel selectionModel = getColorSelectionModel(); selectionModel.setSelectedColor(colors[position]); } } } public String getDisplayName() { return "SystemColor"; } public Icon getSmallDisplayIcon() { return new DiamondIcon(Color.BLUE); } public Icon getLargeDisplayIcon() { return new DiamondIcon(Color.GREEN); } protected void buildChooser() { comboBox = new JComboBox(labels); comboBox.addItemListener(this); add(comboBox); } public void updateChooser() { Color color = getColorFromModel(); setColor(color); } }
列表9-12演示了新的选择器面板的使用。这只是前面9-10中所显示的CreateColorSamplePopup程序的简单修改版本。我们可以取消setChooserPanels()语句的注释,并且注释掉addChooserPanel()调用就可以实现由添加一个面板(如图9-23所示)到替换所有面板(如图9-24所示)的转变。
package swingstudy.ch09; import java.awt.BorderLayout; import java.awt.Color; import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JColorChooser; import javax.swing.JDialog; import javax.swing.JFrame; public class CustomPanelPopup { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Runnable runner = new Runnable() { public void run() { JFrame frame = new JFrame("JColorChooser Custome Panel Sample"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); final JButton button = new JButton("Pick to Change Background"); ActionListener actionListener = new ActionListener() { public void actionPerformed(ActionEvent event) { Color initialBackground = button.getBackground(); final JColorChooser colorChooser = new JColorChooser(initialBackground); SystemColorChooserPanel newChooser = new SystemColorChooserPanel(); // AbstractColorchooserPanel chooserPanels[] = {newChooser}; // colorChooser.setChooserPanels(chooserPanels); colorChooser.addChooserPanel(newChooser); // for okay button, change button background to selected color ActionListener okActionListener = new ActionListener() { public void actionPerformed(ActionEvent event) { Color newColor = colorChooser.getColor(); if(newColor.equals(button.getForeground())) { System.out.println("Color change rejected"); } else { button.setBackground(newColor); } } }; // for cancel button, change button background to red ActionListener cancelActionlistener = new ActionListener() { public void actionPerformed(ActionEvent event) { button.setBackground(Color.RED); } }; final JDialog dialog = JColorChooser.createDialog(null, "Change Button Background", true, colorChooser, okActionListener, cancelActionlistener); // wait for current event dispatching to complete before showing Runnable showDialog = new Runnable() { public void run() { dialog.setVisible(true); } }; EventQueue.invokeLater(showDialog); } }; button.addActionListener(actionListener); frame.add(button, BorderLayout.CENTER); frame.setSize(300, 100); frame.setVisible(true); } }; EventQueue.invokeLater(runner); } }
使用ColorChooserComponentFactory类
值得关注的一个类就是ColorChooserComponentFactory。通常这个类在幕后工作,而我们也不需要对其进行处理。
然而,如果我们希望移除一个默认的颜色选择器,我们不能使用JColorChooser的public AbstractColorChooserPanel removeChooserPanel(AbstractColorChooserPanel panel)。初始时,JColorChooser的chooserPanels属性为null。当这个属性为null时,默认的ColorChooserUI使用public static AbstractColorChooserPanel[] getDefaultChooserPanels()方法向ColorChooserComponentFactory查询默认面板。所以在我们修改这个属性之前,不会有面板显示。如果我们希望移除一个默认面板,我们必须获取默认数组,将我们希望保持的面板存入一个新数组,然后将选择器的chooserPanel属性修改为新数组。这是额外的工作,但是这可以使得工作完成。
ColorChooserComponentFactory类的另一个方法就是public static JComponent getPreviewPanel()方法,这个方法会在JColorChooser的previewPanel属性为null时获取默认的预览面板。这就是向JColorChooser的setPreviewPanel()方法提供null参数并不会移除预览面板的原因。对于空面板,我们必须提供一个没有尺寸的JComponent。
colorChooser.setPreviewPanel(new JPanel());
9.4.4 自定义JColorChooser观感
JColorChooser的外观几乎与所有已安装的观感类型相同。唯一的区别与每一个观感如何显示内部组件相关,例如JTabbedPane,JLabel,JButton,或是JSlider。修改这些组件的UIResource相关属性可以影响新创建的JColorChooser的外观。另外,表9-9中列出了JColorChooser类用于自定义的39个UIResource相关属性。这些属性中的大多数与显示在各种默认颜色选择面板上的文本标签有关。