创建基于另一个但具有不同API的Swing组件

我想基于现有的Swing JComponent创建一个新的Swing JComponent,但是使用不同的API。 换句话说,我不想扩展现有组件,因为我不希望它的API可访问。

这是一个澄清我需求的例子:

更换JCheckBox ,显示两个按钮ON / OFF。 这可以基于预先配置的 JCommandButtonStrip ( 这里有一些信息),但是暴露了与JCheckBox完全相同的API。 不得更改JCommandButtonStrip的配置。

这种问题的最佳方法是什么?

澄清:

有人指出,我写的关于API的内容并不清楚。

当然, JComponent有许多公共字段和方法,可用于每个子类。 然后, JComponent每个子类都可以添加自己的公共字段和方法。 例如, AbstractButton添加了isSelected()方法,而JCommandButtonStrip添加了getButtonCount()方法。

所以,我的意思是:我想创建一个新的JComponent子类MyJComponent ,它基于一个现有的ExistingJComponent 。 我不希望我的类MyJComponent公开ExistingJComponent的公共方法,除了JComponent的公共方法。 然后我想向MyJComponent添加一些公共方法。

请注意,我不是在寻找JCommandButtonStrip / JCheckBox示例的替代方案。 我对这种问题的一般方法感兴趣。

您可以创建一个扩展JComponent的新类,然后在构造函数中插入一个复选框。

 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); } } 

这显然是不完整的,您可能需要将某些方法委托给孩子。 它可能会变得混乱。 像IntelliJ IDEA这样的IDE会为你生成所有这些,如果你点击alt-ins(默认情况下)然后委托,然后选择复选框成员并选择你想委派的条目。 例如:

 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(); } 

请记住,因为子项位于Swing组件树中,所以即使将其标记为私有,其他代码也可以访问子项。

 ((JCheckBox)myCoolCheckbox.getComponents()[0]).setSelected(true); 

如此处所示,您可以在ButtonGroup使用两个JToggleButton实例来“显示两个按钮ON / OFF”ButtonGroup 一次只能选择组中的一个按钮。 以下更改说明如下:

 private final JLabel label = new JLabel(" \u2713 "); 

图片

基于这张JCommandButtonStrip图片:

在此处输入图像描述

我认为你正在寻找JToggleButton作为@trashgod建议,但我不确定按钮组给出你的“问题”的当前描述。 如果您需要按钮组,请使用它。

无论如何,我的答案指向这一行:

这可以基于预先配置的JCommandButtonStrip (这里有一些信息),但是暴露了与JCheckBox完全相同的API。

再一次不清楚你是否想要做一个按钮栏,比如JCommandButtonStrip或者你想做别的事情。 但是,您可以从JComponent扩展自己的组件,并仅委托外部需要的那些方法。 例如,假设你想做一个按钮栏,比如JCommandButtonStrip 。 然后你可以:

  • JComponent扩展的一个类:你的按钮栏。
  • 另一个提供API以向按钮栏添加“命令”。

注意 :已经有一个JToolBar组件可以完美地使用而无需重新发明轮子。 下面的示例只是为了向您展示您可以控制提供给开发人员的API。

MyCommandBar.java

 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 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); } } } 

使用示例

此代码段显示了应如何使用MyCommandBar类:

 MyCommandBar commandBar = new MyCommandBar(); commandBar.setBorder(BorderFactory.createLineBorder(Color.black, 1)); commandBar.addCommandItem("BOLD", new MyCommandBar.CommandItem("Bold", true, null, MyCommandBar.CommandItem.TOGGLE_BUTTON_STYLE)); commandBar.addCommandItem("ITALICS", new MyCommandBar.CommandItem("Italics", false, null, MyCommandBar.CommandItem.CHECK_BOX_STYLE)); commandBar.addCommandItem("UNDERLINE", new MyCommandBar.CommandItem("Underline", false, null, MyCommandBar.CommandItem.DEFAULT_BUTTON_STYLE)); 

你会看到这样的事情:

在此处输入图像描述

您可以使用引用ExistingComponent转发类的私有字段创建JComponent MyJComponent子类。
ExistingComponent的交互是通过MyJComponent方法与转发类完成的,您可以自由地向MyJComponent添加更多方法。
有关转发类使用的委托模式,请参阅Effective Java第 16项。