Составной узел JTree, позволяющий событиям проходить к объектам под ними
Я пытаюсь создать JTree, в котором некоторые узлы являются составными объектами, содержащими JLabel и JButton. Узел представляет сервер и порт, показанный JLabel, JButton будет использовать Desktop API для открытия браузера по умолчанию и перехода к URL-адресу.
Я уже прочитал следующее и внимательно следил за ними. Узел отображается так, как я этого хочу (в основном - я смогу позже сделать его лучше), но когда я пытаюсь нажать на кнопку, JTree реагирует на события, а не на кнопку.
Java Swing: добавить пользовательскую графическую кнопку к элементу JTree
http://www.java2s.com/Code/Java/Swing-JFC/TreeCellRenderer.htm
Мне нужно знать, как позволить событиям проходить через JTree, чтобы они обрабатывались объектом (ами) под ним - JButton или JLabel.
Вот мой TreeCellEditor:
public class UrlValidationCellEditor extends DefaultTreeCellEditor
{
public UrlValidationCellEditor(JTree tree, DefaultTreeCellRenderer renderer)
{
super(tree, renderer);
}
@Override
public Component getTreeCellEditorComponent(JTree tree, Object value,
boolean isSelected, boolean expanded, boolean leaf, int row)
{
return renderer.getTreeCellRendererComponent(tree, value, true, expanded, leaf, row, true);
}
@Override
public boolean isCellEditable(EventObject anEvent)
{
return true; // Or make this conditional depending on the node
}
}
Вот TreeCellRenderer:
public class UrlValidationRenderer extends DefaultTreeCellRenderer implements TreeCellRenderer
{
JLabel titleLabel;
UrlGoButton goButton;
JPanel renderer;
DefaultTreeCellRenderer defaultRenderer = new DefaultTreeCellRenderer();
public UrlValidationRenderer()
{
renderer = new JPanel(new GridLayout(1, 2));
titleLabel = new JLabel(" ");
titleLabel.setForeground(Color.blue);
renderer.add(titleLabel);
goButton = new UrlGoButton();
renderer.add(goButton);
renderer.setBorder(BorderFactory.createLineBorder(Color.black));
backgroundSelectionColor = defaultRenderer
.getBackgroundSelectionColor();
backgroundNonSelectionColor = defaultRenderer
.getBackgroundNonSelectionColor();
}
@Override
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean selected, boolean expanded, boolean leaf, int row,
boolean hasFocus)
{
Component returnValue = null;
if ((value != null) && (value instanceof DefaultMutableTreeNode))
{
Object userObject = ((DefaultMutableTreeNode) value)
.getUserObject();
if (userObject instanceof UrlValidation)
{
UrlValidation validationResult = (UrlValidation) userObject;
titleLabel.setText(validationResult.getServer()+":"+validationResult.getPort());
goButton.setUrl(validationResult.getUrl());
if (selected) {
renderer.setBackground(backgroundSelectionColor);
} else {
renderer.setBackground(backgroundNonSelectionColor);
}
renderer.setEnabled(tree.isEnabled());
returnValue = renderer;
}
}
if (returnValue == null)
{
returnValue = defaultRenderer.getTreeCellRendererComponent(tree,
value, selected, expanded, leaf, row, hasFocus);
}
return returnValue;
}
}
Буду признателен за любые идеи или предложения. Спасибо!
2 ответа
Рендереры не работают таким образом. Они используются в качестве резиновых штампов, что означает, что на самом деле существует только один экземпляр средства визуализации, который окрашивается повсеместно, как окрашивается JList. Таким образом, он не может обрабатывать ввод с помощью мыши, поскольку объекты на самом деле отсутствуют - они просто нарисованы.
Чтобы передавать события мыши нижележащим объектам, вам необходимо реализовать редактор ячеек. Иногда редактор выглядит иначе, чем средство визуализации (средства визуализации строк - это метки, редакторы - это, например, текстовые поля). Следуя этой логике, редактор должен быть реализован с использованием другого экземпляра компонента.
Теперь вы собираетесь визуализировать кнопки и использовать их для манипулирования (т.е. редактирования). Редактор тогда должен быть другим экземпляром JButton, отличным от визуализатора. Реализовать кнопку как средство визуализации легко, но сложнее всего реализовать ее в виде редактора. Вам необходимо расширить AbstractCellEditor и реализовать TreeCellEditor и ActionListener. Кнопка является полем класса редактора. В конструкторе класса редактора вы инициализируете кнопку и добавляете ее в качестве нового прослушивателя действий для кнопки. В методе getTreeCellEditorComponent вы просто возвращаете кнопку. В действии actionPerformed вы вызываете любой код, который вам нужно сделать при нажатии кнопки, а затем вызываете stopCellEditing ().
Таким образом, это работает для меня.
Я сделал SSCCE, который демонстрирует использование на String Tree
public class Start
{
public static class ButtonCellEditor extends AbstractCellEditor implements TreeCellEditor, ActionListener, MouseListener
{
private JButton button;
private JLabel label;
private JPanel panel;
private Object value;
public ButtonCellEditor(){
panel = new JPanel(new BorderLayout());
button = new JButton("Press me!");
button.addActionListener(this);
label = new JLabel();
label.addMouseListener(this);
panel.add(button, BorderLayout.EAST);
panel.add(label);
}
@Override public Object getCellEditorValue(){
return value.toString();
}
@Override public void actionPerformed(ActionEvent e){
String val = value.toString();
System.out.println("Pressed: " + val);
stopCellEditing();
}
@Override public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row){
this.value = value;
label.setText(value.toString());
return panel;
}
@Override public void mouseClicked(MouseEvent e){
}
@Override public void mousePressed(MouseEvent e){
String val = value.toString();
System.out.println("Clicked: " + val);
stopCellEditing();
}
@Override public void mouseReleased(MouseEvent e){
}
@Override public void mouseEntered(MouseEvent e){
}
@Override public void mouseExited(MouseEvent e){
}
}
public static class ButtonCellRenderer extends JPanel implements TreeCellRenderer
{
JButton button;
JLabel label;
ButtonCellRenderer(){
super(new BorderLayout());
button = new JButton("Press me!");
label = new JLabel();
add(button, BorderLayout.EAST);
add(label);
}
@Override public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus){
label.setText(value.toString());
return this;
}
}
public static void main(String[] args){
JTree tree = new JTree();
tree.setEditable(true);
tree.setCellRenderer(new ButtonCellRenderer());
tree.setCellEditor(new ButtonCellEditor());
JFrame test = new JFrame();
test.add(new JScrollPane(tree));
test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
test.setSize(500, 500);
test.setLocationRelativeTo(null);
test.setVisible(true);
}
}
узел должен состоять из 2 частей, метки и кнопки. Когда пользователь щелкает метку, некоторая подробная информация об узле должна появляться в другой части графического интерфейса. Когда пользователь нажимает кнопку, это должно привести к открытию окна браузера...
Не делай так. Вместо этого имейте только метку в дереве. Добавьте кнопку в тот же графический интерфейс, который отображает "подробную информацию об узле".