Я пытаюсь достичь элегантного древовидного представления, в котором определенные типы узлов отображаются в виде панелей, содержащих текст, переключатель и флажок. Ниже приведено изображение того, что у меня есть, и код, который его генерирует. Однако есть несколько проблем, которые только заставляют его чувствовать себя грязным, и я не уверен, что лучший способ обойти их.Реализация узлов JTree с радио/флажками
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) Переключатели для каждой группы узлов под родителем находятся в одной группе кнопок. Но когда я выбираю один, визуальное представление другого не обновляется, чтобы отразить, что оно было отменено, пока я не щелкнул где-нибудь в нем, чтобы «отредактировать» его.
(3) Как правило, этот стандартный тип дерева, где узлы являются просто пустыми объектами, а не фактическими компонентами, кажется для этого неприемлемым, но я не могу придумать лучшую альтернативу, которая позволяет мне группировать эти объекты, выберите отдельные узлы (листья или родители), и каждый лист содержит флажки/кнопки, которые работают правильно.
Я открыт для предложений об альтернативных решениях.
EDIT:
Пробовал с помощью Outline, который, кажется, ближе к тому, что я хочу, но возникли технические проблемы. Я следовал примеру here. Это то, что я получаю:
Как вы можете видеть, кнопки не отображаются должным образом. Вот мой 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
}
}
Для примера [http://codereview.stackexchange.com/a/4447/6692]. – trashgod
@trashgod - Это похоже на гораздо лучшее решение! Спасибо! Я попробую. – ipetrik
@trashgod - Мне очень нравится, как выглядит ваш контур. :-) У меня с некоторыми техническими проблемами (см. Править выше). Мне также интересно, как вы избавились от уродливых линий сетки. – ipetrik