gpt4 book ai didi

java - 使用单选框/复选框实现 JTree 节点

转载 作者:行者123 更新时间:2023-12-01 11:53:31 27 4
gpt4 key购买 nike

我正在尝试实现一种优雅的树表示,其中某些类型的节点显示为包含文本、单选按钮和复选框的面板。下面是我当前拥有的图片以及生成它的代码。然而,有一些问题只会让人感觉很脏,而且我不确定解决这些问题的最佳方法。

Example Tree

public class DatasetTree extends JTree {

public DatasetTree(String name) {
super(new DatasetTreeModel(name));
getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
DatasetTreeCellRenderer renderer = new DatasetTreeCellRenderer();
renderer.setOpenIcon(null);
renderer.setClosedIcon(null);
renderer.setLeafIcon(null);
setCellRenderer(renderer);
setEditable(true);
PanelCellEditor editor = new PanelCellEditor(this, renderer);
setCellEditor(editor);
setShowsRootHandles(true);
setRootVisible(false);
}

public DatasetTreeModel getDatasetModel() {
return (DatasetTreeModel) treeModel;
}

public static class DatasetTreeCellRenderer extends DefaultTreeCellRenderer {

public DatasetTreeCellRenderer() {

}

@Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel,
boolean expanded, boolean leaf, int row, boolean hasFocus) {

if ((value != null) && (value instanceof DatasetHandle)) {
DatasetHandle h = (DatasetHandle) value;
DatasetCellPanel line = new DatasetCellPanel(h);
if (sel) {
line.setBackground(getBackgroundSelectionColor());
line.setForeground(getTextSelectionColor());
} else {
line.setBackground(getBackgroundNonSelectionColor());
line.setForeground(getTextNonSelectionColor());
}
return line;
}
return super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
}
}

public static class DatasetCellPanel extends JPanel {

private final JLabel lblName, lblType, lblom, lbldata, lblimages, lblspectra;
private boolean observable;
private boolean orientable;

private JRadioButton omButton;
private JCheckBox dataSelectBox;

/**
* Create the panel.
*/
public DatasetCellPanel(DatasetHandle h) {
super();
setBackground(Color.WHITE);
FileData fd = h.getFileData();
String name = fd.getFileName();
boolean observable = (fd instanceof ObservableData);
boolean orientable = (fd instanceof Orientable);
String typeName = fd.getClass().getSimpleName();
lblName = new JLabel("");
lblType = new JLabel("");
lblom = new JLabel("[om]");
lbldata = new JLabel("[data]");
lblimages = new JLabel("[images]");
lblspectra = new JLabel("[spectra]");

JRadioButton omButton = new JRadioButton("");
JCheckBox dataSelectBox = new JCheckBox("");

setLayout(new BoxLayout(this, BoxLayout.X_AXIS));

lblName.setText(name);
lblName.setMinimumSize(new Dimension(100, 8));
lblName.setPreferredSize(new Dimension(100, 16));
lblName.setMaximumSize(new Dimension(100, 64));
add(lblName);
add(Box.createRigidArea(new Dimension(5, 0)));

lblType.setText(typeName);
lblType.setMinimumSize(new Dimension(100, 8));
lblType.setPreferredSize(new Dimension(100, 16));
lblType.setMaximumSize(new Dimension(100, 64));
add(lblType);
add(Box.createRigidArea(new Dimension(5, 0)));

if (orientable) {
omButton = h.getLatticeButton();
} else {
lblom.setForeground(UIManager.getColor("Label.disabledForeground"));
omButton.setEnabled(false);
}
add(lblom);
add(omButton);
add(Box.createRigidArea(new Dimension(5, 0)));

if (observable) {
dataSelectBox = h.getDataButton();
} else {
lbldata.setForeground(UIManager.getColor("Label.disabledForeground"));
dataSelectBox.setEnabled(false);
}
add(lbldata);
add(dataSelectBox);
add(Box.createRigidArea(new Dimension(5, 0)));

add(lblimages);
add(Box.createRigidArea(new Dimension(5, 0)));
add(lblspectra);

}

public void addListeners(EventListener l) {

}

@Override
public void setForeground(Color fg) {
if (lblName != null) {
lblName.setForeground(fg);
}
if (lblType != null) {
lblType.setForeground(fg);
}
if (observable && (lbldata != null)) {
lbldata.setForeground(fg);
}
if (orientable && (lblom != null)) {
lblom.setForeground(fg);
}
if (lblimages != null) {
lblimages.setForeground(fg);
}
if (lblspectra != null) {
lblspectra.setForeground(fg);
}
super.setForeground(fg);
}

@Override
public void setBackground(Color bg) {
if (omButton != null) {
omButton.setBackground(bg);
}
if (dataSelectBox != null) {
dataSelectBox.setBackground(bg);
}
super.setBackground(bg);
}

}

public static class PanelCellEditor extends AbstractCellEditor implements TreeCellEditor {

Object value;
private JTree tree;
private DefaultTreeCellRenderer renderer;

public PanelCellEditor(JTree tree, DefaultTreeCellRenderer renderer) {
this.tree = tree;
this.renderer = renderer;
}

@Override
public Object getCellEditorValue() {
return value;
}

// FIXME: Redraw all in group when one is edited
@Override
public Component getTreeCellEditorComponent(JTree tree, Object value, boolean sel,
boolean expanded, boolean leaf, int row) {
this.value = value;
if ((value != null) && (value instanceof DatasetHandle)) {
DatasetHandle h = (DatasetHandle) value;
DatasetCellPanel line = new DatasetCellPanel(h);
if (sel) {
line.setBackground(renderer.getBackgroundSelectionColor());
line.setForeground(renderer.getTextSelectionColor());
} else {
line.setBackground(renderer.getBackgroundNonSelectionColor());
line.setForeground(renderer.getTextNonSelectionColor());
}
return line;
}
return renderer.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, false);
}
}

}

(1) 仅在单击节点一次启用编辑后,按钮/框才会响应。在此之前,按钮/框在鼠标悬停时不会发光。

(2) 父级下每组节点的单选按钮位于单个按钮组中。但是,当我选择其中一个时,另一个的视觉表示不会更新以反射(reflect)它已被取消选择,直到我单击其中的某个位置来“编辑”它。

(3) 一般来说,这种标准类型的树(其中节点只是虚拟对象,而不是实际组件)似乎不适合于此,但我想不出更好的替代方案来允许我对这些对象进行分组,选择单个节点(叶节点或父节点),并让每个叶节点包含正常工作的复选框/按钮。

我愿意接受替代解决方案的建议。

编辑:

尝试使用 Outline,这似乎更接近我想要的,但存在技术问题。我按照例子here 。这是我得到的:

Outline attempt 1

如您所见,按钮未正确显示。这是我的 RowModel:

public class DatasetOutlineRowModel implements RowModel {

@Override
public Class getColumnClass(int column) {
switch (column) {
case 0:
return JRadioButton.class;
case 1:
return JCheckBox.class;
case 2:
return String.class;
case 3:
return String.class;
default:
assert false;
}
return null;
}

@Override
public int getColumnCount() {
return 4;
}

@Override
public String getColumnName(int column) {
switch (column) {
case 0:
return "OM";
case 1:
return "Data";
case 2:
return "Images";
case 3:
return "Spectra";
default:
assert false;
}
return null;
}

@Override
public Object getValueFor(Object node, int column) {
if (!(node instanceof DatasetHandle))
return null;
DatasetHandle handle = (DatasetHandle) node;
switch (column) {
case 0:
return handle.getLatticeButton();
case 1:
return handle.getDataButton();
case 2:
return "";
case 3:
return "";
default:
assert false;
}
return null;
}

@Override
public boolean isCellEditable(Object arg0, int arg1) {
return false;
}

@Override
public void setValueFor(Object arg0, int arg1, Object arg2) {
// TODO Auto-generated method stub

}

}

最佳答案

好的,我终于根据 JTable 处理 boolean 单元格的方式弄清楚了如何实现这一点。我创建了一个独占 boolean 选择渲染器来绘制 JR​​adioButton 并设置树节点以确保维持独占选择。如果其中一个单元格被编辑,我还覆盖了 editStopped 来更新列中的所有单元格。可能有一些方法可以改进这一点,但它适合我的需要。感谢您的指导。

enter image description here

这是我的代码:

DatasetOutline 类

public class DatasetOutline extends Outline {

public DatasetOutline(DatasetTreeModel mdl) {
setRenderDataProvider(new DatasetRenderProvider());
setRootVisible(false);
setShowGrid(false);
setIntercellSpacing(new Dimension(0, 0));
setModel(DefaultOutlineModel.createOutlineModel(mdl, new DatasetOutlineRowModel(), true,
"Dataset"));
getColumnModel().getColumn(1).setCellRenderer(new ExclusiveBooleanRenderer());
getColumnModel().getColumn(1).setCellEditor(new ExclusiveBooleanEditor());
// [snip]
getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
}

// Update the entire column of the conditional boolean if one is changed
@Override
public void editingStopped(ChangeEvent e) {
super.editingStopped(e);
if (e.getSource() instanceof ExclusiveBooleanEditor) {
tableChanged(new TableModelEvent(getModel(), 0, getRowCount(), 1, TableModelEvent.UPDATE));
}
}
}

DatasetOutlineRowModel 类

public class DatasetOutlineRowModel implements RowModel {

@Override
public Class getColumnClass(int column) {
switch (column) {
case 0:
return Boolean.class;
case 1:
return Boolean.class;
case 2:
return String.class;
case 3:
return String.class;
default:
assert false;
}
return null;
}

// [snip]

@Override
public Object getValueFor(Object node, int column) {
if (!(node instanceof DatasetHandle))
return null;
DatasetHandle handle = (DatasetHandle) node;
switch (column) {
case 0:
return handle.isLatticeSelected();
case 1:
return handle.isSelected();
case 2:
return "";
case 3:
return "";
default:
assert false;
}
return null;
}

@Override
public boolean isCellEditable(Object node, int column) {
if (column > 1)
return false;
if (node instanceof DatasetHandle)
return true;
return false;
}

@Override
public void setValueFor(Object node, int column, Object value) {
if (!(node instanceof DatasetHandle))
return;
DatasetHandle handle = (DatasetHandle) node;
if (column == 0) {
handle.setLatticeSelected((Boolean) value);
}
if (column == 1) {
handle.setSelected((Boolean) value);
}

}

}

ExclusiveBooleanEditor 类(DefaultCellRenderer 的修改副本)

public class ExclusiveBooleanEditor extends AbstractCellEditor implements TableCellEditor,
TreeCellEditor {

//
// Instance Variables
//

/** The Swing component being edited. */
protected JComponent editorComponent;
/**
* The delegate class which handles all methods sent from the <code>CellEditor</code>.
*/
protected EditorDelegate delegate;
/**
* An integer specifying the number of clicks needed to start editing. Even if
* <code>clickCountToStart</code> is defined as zero, it will not initiate until a click occurs.
*/
protected int clickCountToStart = 1;

//
// Constructors
//

public ExclusiveBooleanEditor() {
this(new JRadioButton());
JRadioButton radioButton = (JRadioButton) getComponent();
radioButton.setHorizontalAlignment(JRadioButton.CENTER);
}

public ExclusiveBooleanEditor(final JRadioButton radioButton) {
editorComponent = radioButton;
delegate = new EditorDelegate() {
// FIXME replace
@Override
public void setValue(Object value) {
boolean selected = false;
if (value instanceof Boolean) {
selected = ((Boolean) value).booleanValue();
} else if (value instanceof String) {
selected = value.equals("true");
}
radioButton.setSelected(selected);
}

@Override
public Object getCellEditorValue() {
return Boolean.valueOf(radioButton.isSelected());
}
};
radioButton.addActionListener(delegate);
radioButton.setRequestFocusEnabled(false);
}

/**
* Returns a reference to the editor component.
*
* @return the editor <code>Component</code>
*/
public Component getComponent() {
return editorComponent;
}

//
// Modifying
//

/**
* Specifies the number of clicks needed to start editing.
*
* @param count an int specifying the number of clicks needed to start editing
* @see #getClickCountToStart
*/
public void setClickCountToStart(int count) {
clickCountToStart = count;
}

/**
* Returns the number of clicks needed to start editing.
*
* @return the number of clicks needed to start editing
*/
public int getClickCountToStart() {
return clickCountToStart;
}

//
// Override the implementations of the superclass, forwarding all methods
// from the CellEditor interface to our delegate.
//

/**
* Forwards the message from the <code>CellEditor</code> to the <code>delegate</code>.
*
* @see EditorDelegate#getCellEditorValue
*/
@Override
public Object getCellEditorValue() {
return delegate.getCellEditorValue();
}

/**
* Forwards the message from the <code>CellEditor</code> to the <code>delegate</code>.
*
* @see EditorDelegate#isCellEditable(EventObject)
*/
@Override
public boolean isCellEditable(EventObject anEvent) {
return delegate.isCellEditable(anEvent);
}

/**
* Forwards the message from the <code>CellEditor</code> to the <code>delegate</code>.
*
* @see EditorDelegate#shouldSelectCell(EventObject)
*/
@Override
public boolean shouldSelectCell(EventObject anEvent) {
return delegate.shouldSelectCell(anEvent);
}

/**
* Forwards the message from the <code>CellEditor</code> to the <code>delegate</code>.
*
* @see EditorDelegate#stopCellEditing
*/
@Override
public boolean stopCellEditing() {
return delegate.stopCellEditing();
}

/**
* Forwards the message from the <code>CellEditor</code> to the <code>delegate</code>.
*
* @see EditorDelegate#cancelCellEditing
*/
@Override
public void cancelCellEditing() {
delegate.cancelCellEditing();
}

//
// Implementing the TreeCellEditor Interface
//

/** Implements the <code>TreeCellEditor</code> interface. */
@Override
public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected,
boolean expanded, boolean leaf, int row) {
String stringValue = tree.convertValueToText(value, isSelected, expanded, leaf, row, false);

delegate.setValue(stringValue);
return editorComponent;
}

//
// Implementing the CellEditor Interface
//
/** Implements the <code>TableCellEditor</code> interface. */
@Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected,
int row, int column) {
delegate.setValue(value);
if ((editorComponent instanceof JCheckBox) || (editorComponent instanceof JRadioButton)) {
// in order to avoid a "flashing" effect when clicking a checkbox
// in a table, it is important for the editor to have as a border
// the same border that the renderer has, and have as the background
// the same color as the renderer has. This is primarily only
// needed for JCheckBox since this editor doesn't fill all the
// visual space of the table cell, unlike a text field.
TableCellRenderer renderer = table.getCellRenderer(row, column);
Component c =
renderer.getTableCellRendererComponent(table, value, isSelected, true, row, column);
if (c != null) {
editorComponent.setOpaque(true);
editorComponent.setBackground(c.getBackground());
if (c instanceof JComponent) {
editorComponent.setBorder(((JComponent) c).getBorder());
}
} else {
editorComponent.setOpaque(false);
}
}
return editorComponent;
}


//
// Protected EditorDelegate class
//

/**
* The protected <code>EditorDelegate</code> class.
*/
protected class EditorDelegate implements ActionListener, ItemListener, Serializable {

/** The value of this cell. */
protected Object value;

/**
* Returns the value of this cell.
*
* @return the value of this cell
*/
public Object getCellEditorValue() {
return value;
}

/**
* Sets the value of this cell.
*
* @param value the new value of this cell
*/
public void setValue(Object value) {
this.value = value;
}

/**
* Returns true if <code>anEvent</code> is <b>not</b> a <code>MouseEvent</code>. Otherwise, it
* returns true if the necessary number of clicks have occurred, and returns false otherwise.
*
* @param anEvent the event
* @return true if cell is ready for editing, false otherwise
* @see #setClickCountToStart
* @see #shouldSelectCell
*/
public boolean isCellEditable(EventObject anEvent) {
if (anEvent instanceof MouseEvent) {
return ((MouseEvent) anEvent).getClickCount() >= clickCountToStart;
}
return true;
}

/**
* Returns true to indicate that the editing cell may be selected.
*
* @param anEvent the event
* @return true
* @see #isCellEditable
*/
public boolean shouldSelectCell(EventObject anEvent) {
return true;
}

/**
* Returns true to indicate that editing has begun.
*
* @param anEvent the event
*/
public boolean startCellEditing(EventObject anEvent) {
return true;
}

/**
* Stops editing and returns true to indicate that editing has stopped. This method calls
* <code>fireEditingStopped</code>.
*
* @return true
*/
public boolean stopCellEditing() {
fireEditingStopped();
return true;
}

/**
* Cancels editing. This method calls <code>fireEditingCanceled</code>.
*/
public void cancelCellEditing() {
fireEditingCanceled();
}

/**
* When an action is performed, editing is ended.
*
* @param e the action event
* @see #stopCellEditing
*/
@Override
public void actionPerformed(ActionEvent e) {
ExclusiveBooleanEditor.this.stopCellEditing();
}

/**
* When an item's state changes, editing is ended.
*
* @param e the action event
* @see #stopCellEditing
*/
@Override
public void itemStateChanged(ItemEvent e) {
ExclusiveBooleanEditor.this.stopCellEditing();
}
}

public static class ExclusiveBooleanRenderer extends JRadioButton implements TableCellRenderer,
UIResource {
private static final Border noFocusBorder = new EmptyBorder(1, 1, 1, 1);
private static final JLabel emptyLabel = new JLabel("");

public ExclusiveBooleanRenderer() {
super();
setHorizontalAlignment(JRadioButton.CENTER);
setBorderPainted(true);
}

@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int column) {

// Don't draw if it is not changeable
if (value == null) {
if (isSelected) {
emptyLabel.setForeground(table.getSelectionForeground());
emptyLabel.setBackground(table.getSelectionBackground());
} else {
emptyLabel.setForeground(table.getForeground());
emptyLabel.setBackground(table.getBackground());
}

return emptyLabel;
}
if (isSelected) {
setForeground(table.getSelectionForeground());
super.setBackground(table.getSelectionBackground());
} else {
setForeground(table.getForeground());
setBackground(table.getBackground());
}
setSelected((value != null && ((Boolean) value).booleanValue()));

if (hasFocus) {
setBorder(UIManager.getBorder("Table.focusCellHighlightBorder"));
} else {
setBorder(noFocusBorder);
}

return this;
}
}

} // End of class JCellEditor

关于java - 使用单选框/复选框实现 JTree 节点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28612110/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com