在 JToggleButton 中保存选择
Saving selection in JToggleButton
对于我一直从事的项目,我必须创建一个 JTree
,每行有一个 JToggleButton
,实际上每行两个。因此,为了使这项工作正常进行,正如您将在 MCVE 中看到的那样,我编写了一个自定义 DefaultTreeCellRenderer
和一个自定义 AbstractCellEditor
.
但是,无论 JToggleBox
是否被 select 编辑,都存在问题。最初,当您 select 更改第一个值时,它工作正常。如果您停留在 JTree
的那一行并且不点击除 JToggleButton
以外的其他任何地方,它就可以正常工作。如果您单击别处,您设置的值将丢失。
JToggleButton
的设置值如何在点击其他地方时保留?
MCVE
import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.util.EventObject;
import javax.swing.AbstractCellEditor;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JToggleButton;
import javax.swing.JTree;
import javax.swing.event.ChangeEvent;
import javax.swing.plaf.metal.MetalToggleButtonUI;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreeCellEditor;
import javax.swing.tree.TreePath;
@SuppressWarnings("serial")
public class DirectoryExplorer extends JFrame {
private DirectoryExplorer() {
super("Directory Explorer");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new GridLayout(1, 1));
createPanel();
setSize(800,600);
setVisible(true);
}
private void createPanel() {
DefaultMutableTreeNode colors = new DefaultMutableTreeNode("Colours");
colors.add(new DefaultMutableTreeNode("Red"));
colors.add(new DefaultMutableTreeNode("Green"));
colors.add(new DefaultMutableTreeNode("Blue"));
DefaultMutableTreeNode falvors = new DefaultMutableTreeNode("Flavours");
falvors.add(new DefaultMutableTreeNode("Toffee"));
falvors.add(new DefaultMutableTreeNode("Fudge"));
falvors.add(new DefaultMutableTreeNode("Chocolate"));
DefaultMutableTreeNode root = new DefaultMutableTreeNode("root");
root.add(colors);
root.add(falvors);
root.add(new DefaultMutableTreeNode("Leafy"));
JPanel panel = new JPanel(new GridLayout(1, 1));
JTree tree = new JTree(root);
FileNameRenderer fileRender = new FileNameRenderer();
tree.setCellRenderer(fileRender);
tree.setCellEditor(new CheckBoxNodeEditor(tree));
tree.setEditable(true);
tree.setShowsRootHandles(true);
panel.add(new JScrollPane(tree));
getContentPane().add(panel);
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> new DirectoryExplorer());
}
private class FileNameRenderer extends DefaultTreeCellRenderer {
@Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
JComponent c = (JComponent) super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
return new Holder((JLabel) c);
}
}
private class CheckBoxNodeEditor extends AbstractCellEditor implements TreeCellEditor {
FileNameRenderer renderer = new FileNameRenderer();
ChangeEvent changeEvent = null;
JTree t;
TreePath path;
public CheckBoxNodeEditor(JTree tree) {
t = tree;
}
public Object getCellEditorValue() {
Holder checkBoxNode = new Holder(new JLabel((((DefaultMutableTreeNode) path.getLastPathComponent()).getUserObject().toString())));
return checkBoxNode;
}
public boolean isCellEditable(EventObject event) {
if(event instanceof MouseEvent) {
MouseEvent mouseEvent = (MouseEvent) event;
path = t.getPathForLocation(mouseEvent.getX(), mouseEvent.getY());
if (path != null) {
Object node = path.getLastPathComponent();
if ((node != null) && (node instanceof DefaultMutableTreeNode)) {
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) node;
Object userObject = treeNode.getUserObject();
return (userObject instanceof String);
}
}
}
return false;
}
public Component getTreeCellEditorComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row) {
Component editor = renderer.getTreeCellRendererComponent(tree, value, true, expanded, leaf, row, true);
ItemListener itemListener = new ItemListener() {
public void itemStateChanged(ItemEvent itemEvent) {
if (stopCellEditing())
fireEditingStopped();
}
};
if (editor instanceof JToggleButton) {
((JToggleButton) editor).addItemListener(itemListener);
}
return editor;
}
}
private class Holder extends JPanel {
public Holder(Component c) {
setLayout(new GridBagLayout());
setBackground(Color.BLACK);
setOpaque(false);
addComponents(c);
}
private void addComponents(Component c) {
GridBagConstraints gBC = new GridBagConstraints();
gBC.insets = new Insets(0, 0, 0, 5);
add(c, gBC);
gBC.insets = new Insets(0, 0, 0, 0);
add(new DefaultChkBx(), gBC);
add(new FavouriteChkBx(), gBC);
}
}
private class DefaultChkBx extends JToggleButton {
public DefaultChkBx() {
setUI(new MetalToggleButtonUI() {
@Override
protected Color getSelectColor() {
return new Color(242, 0, 255);
}
});
setBorder(null);
setForeground(Color.GRAY);
setText("Default");
setFocusPainted(false);
}
}
private class FavouriteChkBx extends JToggleButton {
public FavouriteChkBx() {
setUI(new MetalToggleButtonUI() {
@Override
protected Color getSelectColor() {
return Color.RED;
}
});
setBorder(null);
setForeground(Color.GRAY);
setText("Favourite");
setFocusPainted(false);
}
}
}
在我看来,您的问题出在 "model" 上:您正在为节点使用普通的 DefaultMutableTreeNode 对象,class 无法保存最重要的数据 --你的按钮的状态。
而是考虑
- 使用从 DefaultMutableTreeNode 扩展的 class,它有两个布尔值,渲染器将使用这些值来告诉按钮显示什么状态。
- 或者将 DefaultMutableTreeNode 用作 "wrapper",方法是将一个包含文本的对象以及两个布尔字段(JToggleButton 用来显示其状态的字段)传入 DefaultMutableTreeNode。
对于我一直从事的项目,我必须创建一个 JTree
,每行有一个 JToggleButton
,实际上每行两个。因此,为了使这项工作正常进行,正如您将在 MCVE 中看到的那样,我编写了一个自定义 DefaultTreeCellRenderer
和一个自定义 AbstractCellEditor
.
但是,无论 JToggleBox
是否被 select 编辑,都存在问题。最初,当您 select 更改第一个值时,它工作正常。如果您停留在 JTree
的那一行并且不点击除 JToggleButton
以外的其他任何地方,它就可以正常工作。如果您单击别处,您设置的值将丢失。
JToggleButton
的设置值如何在点击其他地方时保留?
MCVE
import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.util.EventObject;
import javax.swing.AbstractCellEditor;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JToggleButton;
import javax.swing.JTree;
import javax.swing.event.ChangeEvent;
import javax.swing.plaf.metal.MetalToggleButtonUI;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreeCellEditor;
import javax.swing.tree.TreePath;
@SuppressWarnings("serial")
public class DirectoryExplorer extends JFrame {
private DirectoryExplorer() {
super("Directory Explorer");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new GridLayout(1, 1));
createPanel();
setSize(800,600);
setVisible(true);
}
private void createPanel() {
DefaultMutableTreeNode colors = new DefaultMutableTreeNode("Colours");
colors.add(new DefaultMutableTreeNode("Red"));
colors.add(new DefaultMutableTreeNode("Green"));
colors.add(new DefaultMutableTreeNode("Blue"));
DefaultMutableTreeNode falvors = new DefaultMutableTreeNode("Flavours");
falvors.add(new DefaultMutableTreeNode("Toffee"));
falvors.add(new DefaultMutableTreeNode("Fudge"));
falvors.add(new DefaultMutableTreeNode("Chocolate"));
DefaultMutableTreeNode root = new DefaultMutableTreeNode("root");
root.add(colors);
root.add(falvors);
root.add(new DefaultMutableTreeNode("Leafy"));
JPanel panel = new JPanel(new GridLayout(1, 1));
JTree tree = new JTree(root);
FileNameRenderer fileRender = new FileNameRenderer();
tree.setCellRenderer(fileRender);
tree.setCellEditor(new CheckBoxNodeEditor(tree));
tree.setEditable(true);
tree.setShowsRootHandles(true);
panel.add(new JScrollPane(tree));
getContentPane().add(panel);
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> new DirectoryExplorer());
}
private class FileNameRenderer extends DefaultTreeCellRenderer {
@Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
JComponent c = (JComponent) super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
return new Holder((JLabel) c);
}
}
private class CheckBoxNodeEditor extends AbstractCellEditor implements TreeCellEditor {
FileNameRenderer renderer = new FileNameRenderer();
ChangeEvent changeEvent = null;
JTree t;
TreePath path;
public CheckBoxNodeEditor(JTree tree) {
t = tree;
}
public Object getCellEditorValue() {
Holder checkBoxNode = new Holder(new JLabel((((DefaultMutableTreeNode) path.getLastPathComponent()).getUserObject().toString())));
return checkBoxNode;
}
public boolean isCellEditable(EventObject event) {
if(event instanceof MouseEvent) {
MouseEvent mouseEvent = (MouseEvent) event;
path = t.getPathForLocation(mouseEvent.getX(), mouseEvent.getY());
if (path != null) {
Object node = path.getLastPathComponent();
if ((node != null) && (node instanceof DefaultMutableTreeNode)) {
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) node;
Object userObject = treeNode.getUserObject();
return (userObject instanceof String);
}
}
}
return false;
}
public Component getTreeCellEditorComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row) {
Component editor = renderer.getTreeCellRendererComponent(tree, value, true, expanded, leaf, row, true);
ItemListener itemListener = new ItemListener() {
public void itemStateChanged(ItemEvent itemEvent) {
if (stopCellEditing())
fireEditingStopped();
}
};
if (editor instanceof JToggleButton) {
((JToggleButton) editor).addItemListener(itemListener);
}
return editor;
}
}
private class Holder extends JPanel {
public Holder(Component c) {
setLayout(new GridBagLayout());
setBackground(Color.BLACK);
setOpaque(false);
addComponents(c);
}
private void addComponents(Component c) {
GridBagConstraints gBC = new GridBagConstraints();
gBC.insets = new Insets(0, 0, 0, 5);
add(c, gBC);
gBC.insets = new Insets(0, 0, 0, 0);
add(new DefaultChkBx(), gBC);
add(new FavouriteChkBx(), gBC);
}
}
private class DefaultChkBx extends JToggleButton {
public DefaultChkBx() {
setUI(new MetalToggleButtonUI() {
@Override
protected Color getSelectColor() {
return new Color(242, 0, 255);
}
});
setBorder(null);
setForeground(Color.GRAY);
setText("Default");
setFocusPainted(false);
}
}
private class FavouriteChkBx extends JToggleButton {
public FavouriteChkBx() {
setUI(new MetalToggleButtonUI() {
@Override
protected Color getSelectColor() {
return Color.RED;
}
});
setBorder(null);
setForeground(Color.GRAY);
setText("Favourite");
setFocusPainted(false);
}
}
}
在我看来,您的问题出在 "model" 上:您正在为节点使用普通的 DefaultMutableTreeNode 对象,class 无法保存最重要的数据 --你的按钮的状态。
而是考虑
- 使用从 DefaultMutableTreeNode 扩展的 class,它有两个布尔值,渲染器将使用这些值来告诉按钮显示什么状态。
- 或者将 DefaultMutableTreeNode 用作 "wrapper",方法是将一个包含文本的对象以及两个布尔字段(JToggleButton 用来显示其状态的字段)传入 DefaultMutableTreeNode。