JTree PlusZeichen wenn Kinder vorhanden entfernen

Hallo,

ich habe ein Problem mit JTree.
In meinem Baum kann es sein, dass ein Ast sichtbare und/oder unsichtbare Kinder hat. Wenn ein Ast Kinder kat wird ja ein Pluszeichen vor dem Knoten angezeigt. Wenn jetzt aber mein Ast nur unsichtbare Kinder hat möche ich nicht, dass ein Pluszeichen angezeigt wird. Dann will ich lieber ein Minuszeichen d.h. Ast ist aufgeklappt haben.

Wie kann man das erreichen. Ich will also selbst entscheiden bzw. vorgeben können wann ein Plus- bzw. Minuszeichen für einen Ast angezeigt wird.

Zur Zeit ist das Verhalten wi folgt:
Wenn man einen Doppelklick auf das Pluzeichen macht und der Ast nur unsichtbare Kinder hat wird es zu einem Minuszeichen. Wenn ich aber einen Doppelklick auf den Knotennamen mache bleibt das Pluszeichen. Warum ???

Also wie kann man das Plus- Minuszeichen individuel für jeden Knoten setzen ?

Danke
Albert

Hallo Albert,

Im JTree wird die Darstellung eines einzelnen Elementes durch den
TreeCellRenderer(Inteface) erledigt. Die Default-Klasse ist der
DefaultTreeCellRender, der von JLabel abgeleitet wird.
Du kannst entweder den DefaultTreeCellRender ableiten und die Methode
‚getTreeCellRendererComponent()‘ überschreiben und dann deine Logik
einpflanzen oder du leitest die Klasse JLabel ab und implementierst
den TreeCellRenderer und schreibst dann diese Methode neu.
Die Default-Icons bekommst Du vom UIManager über den Aufruf:
getIcon( „foo“ )wobei foo ein String ist:

  • „Tree.openIcon“ für den Offenen Knoten (-)
  • „Tree.closedIcon“ für den geschlossenen Knoten (+)
  • „Tree.leafIcon“ für ein Dokument.
    Zusätzlich kann man hier auch ein wenig zaubern. Die Component die
    zurückgeliefert wird muß lediglich bei der Abfrage eine korrekte
    Größe zurückliefern wenn im Parameter value (Type:open_mouth:bject) eine null
    übergeben wird. Deshalb könnte man auch komplexe Strukturen als
    Componenten erzeugen, die z.B. mehrere Icons und andere Objecte
    anzeigen.
    In dem UIManager liegen noch andere Werte verborgen, deren Nennung
    den Rahmen sprengen würden. Habe ein kleines QUICK-AND-DIRTY-TOOL
    geschrieben, dass ich jedem gerne zumaile (samt Source) ders braucht.
    Dir Albert ohne Anforderung, da ich denke Du kannst es direkt
    brauchen.

Gruß Peter

Danke für den Tip.
Da hätte ich aber nochmals eine Frage:
Gilt das was ich im UIManager setze nicht immer für den gesammten Baum ?

Ich hatte besiher auch schon einen CellRenderer. In diesem habe ich dann auch bereits versucht die Icon zu ändern. Mein Renderer ist von DefaultTreeCellRenderer abgeleitet. In ihm habe ich vergeblich versucht mit den Methoden
this.setIcon(icon);
this.setLeafIcon(icon);
this.setOpenIcon(icon);
this.setClosedIcon(icon);
das Plus Icon zu ändern.

Ich schaum mir mal Dein Programm an.

Danke
Albert

[Bei dieser Antwort wurde das Vollzitat nachträglich automatisiert entfernt]

Hallo Albert.

Gilt das was ich im UIManager setze nicht immer für den
gesammten Baum ?

Doch.

Mein
Renderer ist von DefaultTreeCellRenderer abgeleitet. In ihm
habe ich vergeblich versucht mit den Methoden
this.setIcon(icon);
this.setLeafIcon(icon);
this.setOpenIcon(icon);
this.setClosedIcon(icon);
das Plus Icon zu ändern.

Das funktioniert auch nur global mit den Eigenschaften

// set customized plus and minus tree symbol:
UIManager.put("Tree.expandedIcon" , iconMinus);
UIManager.put("Tree.collapsedIcon", iconPlus);

Gruß,
-Andreas.

Danke für den Tip.
Da hätte ich aber nochmals eine Frage:
Gilt das was ich im UIManager setze nicht immer für den
gesammten Baum ?

Klar, die Properties, die Du im UIManager setzt gelten global.

Ich hatte besiher auch schon einen CellRenderer. In diesem
habe ich dann auch bereits versucht die Icon zu ändern. Mein
Renderer ist von DefaultTreeCellRenderer abgeleitet. In ihm
habe ich vergeblich versucht mit den Methoden
this.setIcon(icon);
this.setLeafIcon(icon);
this.setOpenIcon(icon);
this.setClosedIcon(icon);
das Plus Icon zu ändern.

Das liegt einerseits daran, dass du dem DefaultTreeCellRenderer
mit dem Setzen der Icons nicht in die Logik reingreifst, die er
anwendet um zu entscheiden, wann das jeweilige Icon zu nutzen ist.
Auf unterschiedlichen VM habe ich auch schon erlebt, dass diese
set… überhaubt keine Wirkung zeigen.
Wenn Du schon abgeleitet hast solltest Du die Funktion
‚getTreeCellRendererComponent(JTree tree, Object value, boolean
selected, boolean expanded, boolean leaf, int row, boolean hasFocus)‘
überschreiben. Die Geschichte mit den UIManager Icons macht dann
Sinn, wenn Du die Icons einsetzen willt die dem aktuellen Look & Feel
entsprechen.

Gruß Peter

Hallo Peter,

wie hast Du denn nun das Ausgangs-Problem gelöst,
das Expansionssymbol (Plus- oder Minuszeichen)
für jeden Knoten individuell zu setzen?

So weit ich weiß, bringt Dich ein eigener TreeCellRenderer
nicht unbedingt weiter, da Du dort nur die Darstellung
(standardmäßig Knoten-Icon & Knoten-Label) rechts neben
dem Expansionssymbol ändern kannst, aber nicht das
Expansionssymbol selbst …

Nach einem schnellen Blick unter die Motorhaube von Swing
würde ich mal raten, das Du

javax.swing.plaf.basic.BasicTreeUI.paintExpandControl(
 Graphics g, Rectangle clipBounds, Insets insets, Rectangle bounds, 
 TreePath path, int row, boolean isExpanded, boolean hasBeenExpanded,
 boolean isLeaf)

überschrieben hast, oder?

Fragende Grüße,
-Andreas.

Hallo Andreas,

Die eigentliche Problemlösung zeige ich Dir mal an
diesem generischen Beispiel:

public class CellRenderer extends
DefaultTreeCellRenderer {

/** plus : */
@SuppressWarnings(„nls“)
static Icon plus = (Icon) UIManager.get
(„Tree.openIcon“);

/** minus : */
@SuppressWarnings(„nls“)
static Icon minus = (Icon) UIManager.get
(„Tree.closedIcon“);

/** leaf : */
@SuppressWarnings(„nls“)
static Icon leaf = (Icon) UIManager.get
(„Tree.leafIcon“);

/**
* New intance of CellRenderer 0 ) {
// TESTE HIER, OB DU NUR INVISIBLE
HAST
if( true ) { // HIER DANN DAS
TESTERGEBNISS
com.setIcon(minus);
}else {
com.setIcon(plus);
}
}
}else {
this.setText("");
}
return com;
}

}

Solltest Du rauskopieren können und in Deiner IDE
mal anschauen.

Gruß Peter

Hallo Peter!

Nun, wie ich schon geschrieben habe, änderst Du nur
das Icon des Labels, welches für den Knoten selbst zuständig
ist, nicht aber das Expansionssymbol links von dem Knoten-Icon.

Aber der Ansatz über den TreeCellRenderer ist schon gut.
Um das Expansionssymbol zu setzen muss man von dort an die
Darstellungsklasse des Baums heran:

 BasicTreeUI treeUI = (BasicTreeUI)tree.getUI();
 Icon icoMinus = (Icon)UIManager.get("Tree.expandedIcon");
 treeUI.setCollapsedIcon(icoMinus);
 treeUI.setExpandedIcon (icoMinus);

Hier noch ein Demo-Programm dafür:

import java.awt.Component;
import java.awt.Container;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.util.Vector;

import javax.swing.Icon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.UIManager;
import javax.swing.plaf.basic.BasicTreeUI;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;

/\*\*
 \* CustomTreeCellRenderer, der 
 \* 
 \* @author Andreas Brand
 \*/
public class CustomTreeCellRenderer extends DefaultTreeCellRenderer {
 /\*\*
 \* Erstelle einen neuen CustomTreeCellRenderer
 \*/
 public CustomTreeCellRenderer() {
 super();
 }

 /\*\*
 \* Erzeugt die visuelle Darstellung des übergebenen Baumknotens.
 \* 
 \* @see javax.swing.tree.TreeCellRenderer#getTreeCellRendererComponent(javax.swing.JTree, java.lang.Object, boolean, boolean, boolean, int, boolean)
 \*/
 public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
 // initialisiere den Renderer:
 JLabel com = (JLabel)super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);

 // fürs Debugging, um zu sehen, was alles zum Knoten-Renderer gehört: 
 //com.setBorder(BorderFactory.createLineBorder(Color.RED));

 BasicTreeUI treeUI = (BasicTreeUI)tree.getUI();
 Icon icoPlus = (Icon)UIManager.get("Tree.collapsedIcon");
 Icon icoMinus = (Icon)UIManager.get("Tree.expandedIcon");

 // TODO: Prüfung, ob der aktuelle Knoten nur unsichtbare Kinder besitzt
 // und folgendes Flag korrekt setzen: 
 boolean blnNodeHasOnlyInvisibleChildren = true;

 if( value != null ) {
 DefaultMutableTreeNode node = (DefaultMutableTreeNode)value;
 if( node.getChildCount()\>0 && blnNodeHasOnlyInvisibleChildren) {
 // für unsichtbare Kinder immer das Minus-Icon anzeigen:
 treeUI.setCollapsedIcon(icoMinus);
 treeUI.setExpandedIcon (icoMinus);
 }
 }

 return this;
 }

 /\*\*
 \* Test-Programm für den Renderer.
 \*/
 public static void main(String[] args) {
 JTree tree = new JTree();
 tree.setCellRenderer(new CustomTreeCellRenderer());

 JFrame frame = new JFrame("Tree Renderer Test");
 Container contentPane = frame.getContentPane();

 Vector vecIconTypes = new Vector();
 vecIconTypes.add("Tree.openIcon");
 vecIconTypes.add("Tree.closedIcon");
 vecIconTypes.add("Tree.leafIcon");
 vecIconTypes.add("Tree.expandedIcon");
 vecIconTypes.add("Tree.collapsedIcon");

 contentPane.setLayout(new GridBagLayout());
 int iXPos = 0;
 int iYPos = 0;
 GridBagConstraints constraints = new GridBagConstraints();
 constraints.gridx = iXPos;
 constraints.gridy = iYPos;
 constraints.gridwidth = vecIconTypes.size();
 constraints.gridheight = 1;
 constraints.weightx = 1;
 constraints.weighty = 1;
 constraints.ipadx = 2;
 constraints.ipady = 2;
 constraints.insets = new Insets(5, 5, 5, 5);
 constraints.anchor = GridBagConstraints.WEST;
 constraints.fill = GridBagConstraints.BOTH;
 contentPane.add(new JScrollPane(tree), constraints); 

 iYPos++;
 constraints = new GridBagConstraints();
 constraints.gridx = iXPos;
 constraints.gridy = iYPos;
 constraints.gridwidth = vecIconTypes.size();
 constraints.gridheight = 1;
 constraints.weightx = 1;
 constraints.weighty = 0;
 constraints.ipadx = 2;
 constraints.ipady = 2;
 constraints.insets = new Insets(5, 5, 5, 5);
 constraints.anchor = GridBagConstraints.WEST;
 constraints.fill = GridBagConstraints.HORIZONTAL;
 contentPane.add(new JLabel("Die UI-Klasse '" + tree.getUI().getClass().getName() + "' des Baums verwendet die folgenden Standard-Icons:"), constraints); 

 iYPos++;
 for (int i=0; i

Damit wäre das Ursprungsproblem nun hoffentlich gelöst :wink:

Gruß,
-Andreas.