I would like to create a new Swing JComponent based on an existing one, but with a different API. In other words, I don\'t want to extend the existing component, because I don\'
Based on this picture of JCommandButtonStrip
:
I think you are looking for JToggleButton as @trashgod suggested, but I'm not sure about buttons group given the current description of your "problem". If you need buttons group then use it.
Anyway my answer points to this line:
This could be based on a pre-configured
JCommandButtonStrip
(some info here) but exposing exactly the same API ofJCheckBox
.
Once again it's not clear if you're trying to do a buttons bar such as JCommandButtonStrip
or you want to do something else. However you can make your own component extending from JComponent and delegate only those methods that are needed from the outside. For example let's say you want to do a buttons bar such as JCommandButtonStrip
. Then you can have:
JComponent
: your buttons bar.Note: There's already a JToolBar component which can perfectly be used without reinvent the wheel. The example below is just to show you that you can control the API offered to the developers.
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.event.ActionListener;
import java.util.HashMap;
import java.util.Map;
import javax.swing.AbstractButton;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JToggleButton;
import javax.swing.event.ChangeListener;
public class MyCommandBar extends JComponent {
private final JPanel content;
private final Map<String, CommandItem> map = new HashMap<>();
public MyCommandBar() {
super();
content = new JPanel(new GridLayout(1, 0));
content.setOpaque(false);
setLayout(new FlowLayout());
add(content);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics graphics = g.create();
graphics.setColor(getBackground());
graphics.fillRect(0, 0, getWidth(), getHeight());
graphics.dispose();
}
public void addCommandItem(String actionCommand, CommandItem commandItem) {
if(map.get(actionCommand) != null) {
removeCommandItem(actionCommand);
}
content.add(commandItem.getComponent());
map.put(actionCommand, commandItem);
}
public void removeCommandItem(String actionCommand) {
CommandItem commandItem = map.get(actionCommand);
if(commandItem != null) {
content.remove(commandItem.getComponent());
content.revalidate();
content.repaint();
map.remove(actionCommand);
}
}
public CommandItem getCommandItem(String actionCommand) {
return map.get(actionCommand);
}
public static class CommandItem {
public static final int TOGGLE_BUTTON_STYLE = 0;
public static final int CHECK_BOX_STYLE = 1;
public static final int DEFAULT_BUTTON_STYLE = 2;
private final AbstractButton component;
public CommandItem(String text, boolean state, Icon icon, int style) {
switch(style) {
case TOGGLE_BUTTON_STYLE : component = new JToggleButton(text, icon, state); break;
case CHECK_BOX_STYLE : component = new JCheckBox(text, icon, state); break;
default: component = new JButton(text, icon);
}
}
protected AbstractButton getComponent() {
return component;
}
public void addActionListener(ActionListener listener) {
component.addActionListener(listener);
}
public void addChangeListener(ChangeListener listener) {
component.addChangeListener(listener);
}
public void setAction(Action action) {
component.setAction(action);
}
}
}
This code snippet shows how MyCommandBar
class should be used:
MyCommandBar commandBar = new MyCommandBar();
commandBar.setBorder(BorderFactory.createLineBorder(Color.black, 1));
commandBar.addCommandItem("BOLD", new MyCommandBar.CommandItem("<html><b>Bold</b></html>", true, null, MyCommandBar.CommandItem.TOGGLE_BUTTON_STYLE));
commandBar.addCommandItem("ITALICS", new MyCommandBar.CommandItem("<html><i>Italics</i></html>", false, null, MyCommandBar.CommandItem.CHECK_BOX_STYLE));
commandBar.addCommandItem("UNDERLINE", new MyCommandBar.CommandItem("<html><u>Underline</u></html>", false, null, MyCommandBar.CommandItem.DEFAULT_BUTTON_STYLE));
And you'll see something like this:
As shown here, you can use two instances of JToggleButton
in a ButtonGroup
to "show two buttons ON / OFF." The ButtonGroup
causes only one button in the group to be selected at a time. The following change is illustrated:
private final JLabel label = new JLabel(" \u2713 ");
You can create a new class which extends JComponent then inside the constructor insert a checkbox into itself.
public class MyCoolCheckbox extends JComponent{
private JCheckBox checkbox;
public MyCoolCheckbox(String label) {
checkbox= new JCheckBox(label);
this.setLayout(new BorderLayout());
this.add(checkbox, BorderLayout.CENTER);
}
}
This is obviously incomplete and you may need to delegate certain methods to the child. It might get messy. IDEs like IntelliJ IDEA will generate all this for you if you hit alt-ins (by default) then delegate, then select the checkbox member and pick the entries you want to delegate. For example:
public void setForeground(Color fg) {
checkbox.setForeground(fg);
}
public void setBackground(Color bg) {
checkbox.setBackground(bg);
}
public Color getForeground() {
return checkbox.getForeground();
}
public Color getBackground() {
return checkbox.getBackground();
}
Keep in mind that because the child is within the Swing component tree, other code will have access to the children even though they are marked private.
((JCheckBox)myCoolCheckbox.getComponents()[0]).setSelected(true);
You can create MyJComponent
subclass of JComponent
with a private field that references a forwarding class for ExistingComponent
.
The interactions with ExistingComponent
are done with the forwarding class through methods of MyJComponent
, and you are free to add more methods to MyJComponent
.
Please see Effective Java item 16, for the delegation pattern used with the forwarding class.