Subpages: 1. Dialogs and choosers overview
2. Adding an "About" dialog
3. JOptionPane message dialogs
4. Customizing JColorChooser
5. Customizing JFileChooser
14.4 Customizing JColorChooser
In chapter 12 we developed a custom menu item allowing quick and easy selection of a color for the background and foreground of a JTextArea. In section 14.1 we built off of this example to add a simple "About" dialog. In this section we'll build off of it further, and construct a customized JColorChooser allowing a much wider range of color selection. Our implementation includes a preview component, PreviewPanel, that illustrates how text will appear with chosen background and foreground colors. Note that we have to return both background and foreground selection values when the user dismisses the color chooser in order to update the text component properly.

Figure 14.12 JColorChooser with custom PreviewPanel component capable of returning two Color selections.
<<file figure14-12.gif>>
UI Guideline : Preview Improves Usability
In this example, the User may have a goal of "Select suitable colours for a banner headline." By allowing the User to view a WYSIWYG preview, usability is improved. The user doesn't have to experiment with selection, which involves opening and closing the dialog several times. Instead, she can achieve the goal on a single visit to the color chooser dialog.
The Code: BasicTextEditor.java
see \Chapter14\3
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class BasicTextEditor extends JFrame
{
// Unchanged code from section 14.2
protected JColorChooser m_colorChooser;
protected PreviewPanel m_previewPanel;
protected JDialog m_colorDialog;
public BasicTextEditor() {
super("BasicTextEditor with JColorChooser");
setSize(450, 350);
ImageIcon icon = new ImageIcon("smallIcon.gif");
setIconImage(icon.getImage());
m_colorChooser = new JColorChooser();
m_previewPanel = new PreviewPanel(m_colorChooser);
m_colorChooser.setPreviewPanel(m_previewPanel);
// Unchanged code from section 14.2
}
protected JMenuBar createMenuBar() {
// Unchanged code from section 14.2
Action actionChooser = new AbstractAction("Color Chooser") {
public void actionPerformed(ActionEvent e) {
BasicTextEditor.this.repaint();
if (m_colorDialog == null)
m_colorDialog = JColorChooser.createDialog(
BasicTextEditor.this,
"Select Background and Foreground Color",
true, m_colorChooser, m_previewPanel, null);
m_previewPanel.setTextForeground(
m_monitor.getForeground());
m_previewPanel.setTextBackground(
m_monitor.getBackground());
m_colorDialog.show();
if (m_previewPanel.isSelected()) {
m_monitor.setBackground(
m_previewPanel.getTextBackground());
m_monitor.setForeground(
m_previewPanel.getTextForeground());
}
}
};
mOpt.addSeparator();
item = mOpt.add(actionChooser);
item.setMnemonic('c');
menuBar.add(mOpt);
// Unchanged code from section 14.2
}
}
// Unchanged code from section 14.2
class PreviewPanel extends JPanel
implements ChangeListener, ActionListener
{
protected JColorChooser m_chooser;
protected JLabel m_preview;
protected JToggleButton m_btBack;
protected JToggleButton m_btFore;
protected boolean m_isSelected = false;
public PreviewPanel(JColorChooser chooser) {
this(chooser, Color.white, Color.black);
}
public PreviewPanel(JColorChooser chooser,
Color background, Color foreground) {
m_chooser = chooser;
chooser.getSelectionModel().addChangeListener(this);
setLayout(new BorderLayout());
JPanel p = new JPanel(new GridLayout(2, 1, 0, 0));
ButtonGroup group = new ButtonGroup();
m_btBack = new JToggleButton("Background");
m_btBack.setSelected(true);
m_btBack.addActionListener(this);
group.add(m_btBack);
p.add(m_btBack);
m_btFore = new JToggleButton("Foreground");
m_btFore.addActionListener(this);
group.add(m_btFore);
p.add(m_btFore);
add(p, BorderLayout.WEST);
p = new JPanel(new BorderLayout());
Border b1 = new EmptyBorder(5, 10, 5, 10);
Border b2 = new BevelBorder(BevelBorder.RAISED);
Border b3 = new EmptyBorder(2, 2, 2, 2);
Border cb1 = new CompoundBorder(b1, b2);
Border cb2 = new CompoundBorder(cb1, b3);
p.setBorder(cb2);
m_preview = new JLabel("Text colors preview",
JLabel.CENTER);
m_preview.setBackground(background);
m_preview.setForeground(foreground);
m_preview.setFont(new Font("Arial",Font.BOLD, 24));
m_preview.setOpaque(true);
p.add(m_preview, BorderLayout.CENTER);
add(p, BorderLayout.CENTER);
m_chooser.setColor(background);
}
protected boolean isSelected() {
return m_isSelected;
}
public void setTextBackground(Color c) {
m_preview.setBackground(c);
}
public Color getTextBackground() {
return m_preview.getBackground();
}
public void setTextForeground(Color c) {
m_preview.setForeground(c);
}
public Color getTextForeground() {
return m_preview.getForeground();
}
public void stateChanged(ChangeEvent evt) {
Color c = m_chooser.getColor();
if (c != null) {
if (m_btBack.isSelected())
m_preview.setBackground(c);
else
m_preview.setForeground(c);
}
}
public void actionPerformed(ActionEvent evt) {
if (evt.getSource() == m_btBack)
m_chooser.setColor(getTextBackground());
else if (evt.getSource() == m_btFore)
m_chooser.setColor(getTextForeground());
else
m_isSelected = true;
}
}
Understanding the Code
Class BasicTextEditor
New instance variables:
JColorChooser m_colorChooser: stored JColorChooser to avoid unnecessary instantiation.
PreviewPanel m_previewPanel: instance of our custom color previewing component.
JDialog m_colorDialog: stored JDialog acting as the parent of m_colorChooser.
The constructor instantiates m_colorChooser and m_previewPanel, assigning m_previewPanel as m_colorChooser's preview component using the setPreviewPanel() method.
The menu bar receives a new menu item titled "Color Chooser" set up in the createMenuBar() method as an Action implementation. When selected, this item first repaints our application frame to ensure that the area covered by the popup menu is refreshed properly. Then it checks to see if our m_colorDialog has been instantiated yet. If not we call JColorChooser's static createDialog() method to wrap m_colorChooser in a dialog, and use m_previewPanel as an ActionListener for the "OK" button (see 14.1.3). Note that this instantiation only occurs once.
We then assign the current colors of m_monitor to m_previewPanel (recall that m_monitor is the JTextArea central to this application). The reason we do this is because the foreground and background can also be assigned by our custom menu color choosers. If this occurs m_previewPanel is not notified, so we update the selected colors each time the dialog is invoked.
The dialog is then shown and the main application thread waits for it to be dismissed. When the dialog is dismissed m_previewPanel is checked for whether or not new colors have been selected, using its isSelected() method (see below). If new colors have been chosen they are assigned to m_monitor.
Note: We have purposely avoided updating the selected colors in our custom color menu components. The reason we did this is because in a more professional implementation we would most likely not offer both methods for choosing text component colors. If we did want to support both methods we would need to determine the closest color in our custom color menus that matches the corresponding color selected with JColorChooser (because JColorChooser offers a much wider range of choices).
Class PreviewPanel
This class represents our custom color preview component designed for use with JColorChooser. It extends JPanel and implements two listener interfaces, ChangeListener and ActionListener. It displays selected foreground and background colors in a label, and includes two JToggleButtons used to switch between background color and foreground color selection modes. Instance variables:
JColorChooser m_chooser: a reference to the hosting color chooser.
JLabel m_preview: label to preview background and foreground colors.
JToggleButton m_btBack: toggle button to switch to background color selection.
JToggleButton m_btFore: toggle button to switch to foreground color selection.
boolean m_isSelected: flag indicating a selection has taken place.
The first PreviewPanel constructor takes a JColorChooser as parameter and delegates its work to the second constructor, passing it the JColorChooser as well as white and black Colors for the initial background and foreground colors respectively. As we discussed in the beginning of this chapter, JColorChooser's ColorSelectionModel fires ChangeEvent's when the selected Color changes. So we start by registering this component as a ChangeListener with the given color chooser's model.
A BorderLayout is used to manage this container and two toggle buttons are placed in a 2x1 GridLayout, which is added to the WEST region. Both buttons receive a this reference as an ActionListener. A label with a large font is then placed in the CENTER region. This label is surrounded by a decorative, doubly-compounded border consisting of an EmptyBorder, BevelBorder, and another EmptyBorder. The foreground and background colors of this label are assigned as those values passed to the constructor.
Several methods are used to set and get the selected colors and do not require any special explanation. The stateChanged() method will be called when the color chooser model fires ChangeEvents. Depending on which toggle button is selected, this method updates the background or foreground color of the preview label.
The actionPerformed() method will be called when one of the toggle buttons is pressed. It assignes the stored background or foreground, depending which button is pressed, as the color of the hosting JColorChooser. This method is also called when the "OK" button is pressed, in which case the m_isSelected flag is set to true.
Running the Code
Select the "Color Chooser" menu item to bring up our customized JColorChooser shown in figure 14.12. Select a background and foreground color using any of the available color panes. Verify that the preview label is updated accordingly to reflect the current color selection, and the currently selected toggle button. Press the "OK" button to dismiss the dialog and note that both the selected foreground and background colors are assigned to our application's text area. Also note that pressing the "Cancel" button dismisses the dialog without making any color changes.



RSS feed Java FAQ News