JTree Horizontal weiterführen wenn Vertical kein platz mehr (JTree horizontal wrap)

Hallo :o),

Ich hab leider ein kleines Problem für das ich im Internet keine Lösung gefunden habe! (Mag auch sein das ich den richtigen Suchbegriff nicht gefunden habe :wink: )

Ich versuche es mal zu beschreiben:

Also ich habe einen JTree. Dieser wird im Verlauf der Benutzung vom User erweitert. Dies geht solange gut bis die letzten Element in dem unsichtbaren Teil meines JScrollpanes verschwinden.

Für die Benutzbarkeit ist es allerdings wichtig immer alle Element des Tree’s im Blick zu haben(Es bleiben bereits alle Nodes automatisch expanded).

Meine Idee war also das der Tree wenn er auf der X-Achse keinen Platz mehr hat praktisch eine neue Spalte aufmacht und darin weitergeführt wird!

Nur wie ich das anstelle ist mir ein Rätsel… :eek:

Hoffe ihr könnt mir hier helfen!! :smiley:

Danke

jo, JScrollPane(-Bar) sollte sich nach links ziehen lassen, setze zuvor die preferredSize, sage nicht einfach, ne ist nicht, ist halt bald weihnachten. Grüßle.

Das ist meines Wissens nach nicht vorgesehen, und wäre mit erheblichen Programmieraufwand verbunden. Deine Lösung ist auch ziemlich ungewöhnlich, normalerweise versucht man eher, den aktuellen Knoten wieder ins Bild zu scrollen (selbst wenn dann Papa-Knoten dadurch verschwinden).

Mal angenommen du kriegst das hin wie du es dir vorstellst, was passiert wenn der Platz noch immer nicht reicht?

Na wenn der Platz immer noch nicht reicht, soll das von der horizontalen Scrollbar kompensiert werden!
Ist aber im Kontext des Programmes unwahrscheinlich…

Gibt es vielleicht eine JTree ähnliche Klasse die diese Funktionalität besitzt oder kennt ihr irgendwelche libaries oder private Klassen oder Projekte?

Danke schonmal für eure Infos!

Mir viele da nur selber schreiben ein… aber das duerfte ziemlich komplex werden. Kannst dir ja mal den code von jtree anschauen, und mal ueberlegen was man da aendern koennte… vielleicht gibt es auch soetwas wie einen jtreelayoutmanager, evtl koennte man da etwas basteln…

Naja, du müsstest den JTree in der 1. Spalte “abschneiden” (ab einem gewissen Punkt keine Knoten mehr anzeigen) und in der 2. Spalte einen neuen JTree anzeigen, der da beginnt, wo der alte Aufhört.

Aber: Wenn die Knoten eh immer aufgeklappt sind, ist es vielleicht einfacher statt einem JTree eine JList zu nehmen, in der die Elemente nur entsprechend eingerückt sind!? Ich weiß jetzt nicht wie komplex dein Baum ist, aber vielleicht ist das eine Idee!?

[QUOTE=Natac]
Aber: Wenn die Knoten eh immer aufgeklappt sind, ist es vielleicht einfacher statt einem JTree eine JList zu nehmen, in der die Elemente nur entsprechend eingerückt sind!? Ich weiß jetzt nicht wie komplex dein Baum ist, aber vielleicht ist das eine Idee!?[/QUOTE]
Das ist ne sehr gute Idee glaube ich :wink: Danke!

Aber wie schneide ich den dann die JList ab und führe sie in der neuen Spalte weiter??

Wir wärs wenn du das ganze einfach in einem Grid Layout anzeigst? Frohe Weihnachten übrigens!

Ich hab Grid Layout noch nie benutzt, aber mir wäre neu wenn es das könnte was ich suche…
Bist du sicher das das damit möglich ist?

EDIT:
Und wenn ja wie?

Naja kommt drauf an wie dein Baum aussieht, wieviele Ebenen, ob er sich verändert, neue Elemente dazukommne usw. GridLayout ist ein Gitter, du kannst dir Panels/Labels machen, die je einen Eintrag in deinem Baum darstellen und sie in diesem Gitter anordnen. Ich hab grad was im Kopf, vielleicht hacke ich da noch was zusammen.

Der Weihnachtsbert war da, das sollte ungefähr das sein was du haben willst:

import java.util.ArrayList;
import java.util.List;

import javax.swing.*;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreeNode;


public class ColumnTreeLayout implements LayoutManager2
{
	static int LEVEL_GAP = 30;	
	static int ENTRIES_PER_COLUMN = 5;
	static int V_GAP = 10;
	
	List<Node> nodes;
	
	class Node
	{
		Object object;
		int level;
		Component component; 
		
		
		Node(Object o, int l)
		{
			this.object=o;
			level=l;
		}

		public Object getObject()
		{
			return object;
		}

		public int getLevel()
		{
			return level;
		}
		
		public String toString()
		{
			return "Node: {object="+object.toString()+", level="+level+"}
";
		}

		public Component getComponent()
		{
			return component;
		}

		public void setComponent(Component component)
		{
			this.component = component;
		}
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args)
	{
		JFrame frame = new JFrame();
		
		JTree tree = new JTree();
		JScrollPane scroll = new JScrollPane(tree);
		frame.add(scroll,BorderLayout.NORTH);
		
		JPanel p = new JPanel();
		p.setLayout(new ColumnTreeLayout(tree.getModel(),p));
		
		frame.add(p,BorderLayout.CENTER);
		
		frame.pack();
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setVisible(true);
	}
	
	public ColumnTreeLayout(TreeModel model,Container parent)
	{
		nodes = createList(model);
		for(Node n:nodes)
		{
			JLabel label = new JLabel(n.getObject().toString());
			parent.add(label);
			n.setComponent(label);
		}
	}	
	
	private List<Node> createList(TreeModel model)
	{
		ArrayList<Node> list = new ArrayList<Node>();
		nodeToList((TreeNode)model.getRoot(),list,0);
		return list;
	}
	
	private void nodeToList(TreeNode root, List<Node> list, int level)
	{
		Node node = new Node(root,level);
		list.add(node);
		
		if(!root.isLeaf())
		{
			for(int i=0;i<root.getChildCount();i++)
			{
				nodeToList(root.getChildAt(i),list,level+1);
			}
		}
	}

	@Override
	public void addLayoutComponent(String name, Component comp)
	{
		
	}

	@Override
	public void removeLayoutComponent(Component comp)
	{
		// TODO Auto-generated method stub
		
	}

	@Override
	public Dimension preferredLayoutSize(Container parent)
	{
		int size = nodes.size();
		int sqrt = (int) Math.sqrt(size);
				
		Dimension d = new Dimension(sqrt*(100+LEVEL_GAP),sqrt*(15+V_GAP));
		return d;
	}

	@Override
	public Dimension minimumLayoutSize(Container parent)
	{
		int size = nodes.size();
		int sqrt = (int) Math.sqrt(size);
				
		Dimension d = new Dimension(sqrt*LEVEL_GAP,sqrt*(15+V_GAP));
		return d;
	}

	@Override
	public void layoutContainer(Container parent)
	{
		int x = parent.getInsets().left;
		int y = parent.getInsets().top;

		int height = parent.getHeight();
		int columnWidth = 0;
		
		for(int i=0;i<nodes.size();i++)
		{
			Node n = nodes.get(i);
			Component c = n.getComponent();
			Dimension prefs = c.getPreferredSize();
			
			if(y+prefs.height > height)
			{
				y = parent.getInsets().top;
				x = x + columnWidth + LEVEL_GAP; 
			}
			
			int gap = LEVEL_GAP*n.level;
			
			if(columnWidth < prefs.width + gap)
			{
				columnWidth = prefs.width + gap;
			}
			
			c.setBounds(x+gap,y, prefs.width, prefs.height);
			y += prefs.height + V_GAP;
		}
	}

	@Override
	public void addLayoutComponent(Component comp, Object constraints)
	{
		// TODO Auto-generated method stub
		
	}

	@Override
	public Dimension maximumLayoutSize(Container parent)
	{
		int size = nodes.size();
		int sqrt = (int) Math.sqrt(size);
				
		Dimension d = new Dimension(sqrt*LEVEL_GAP,sqrt*(15+V_GAP));
		return d;
	}

	@Override
	public float getLayoutAlignmentX(Container target)
	{
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public float getLayoutAlignmentY(Container target)
	{
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public void invalidateLayout(Container target)
	{
		// TODO Auto-generated method stub
		
	}
	
}

Ist noch ein bisschen dirty, vor allem die Schätzung der preferred Size.

Habs kurz überarbeitet. Falls dir das nicht schön genug ist, kommst du wohl nicht um das Selberzeichnen des Baums herum.


import java.awt.*;
import java.util.ArrayList;
import java.util.List;
 
import javax.swing.*;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreeNode;
 
 
public class ColumnTreeLayout implements LayoutManager2
{
    int levelGap = 20; 
    int verticalGap = 5;
   
    List<Node> nodes;
   
    class Node
    {
        Object object;
        int level;
        Component component;
       
       
        Node(Object o, int l)
        {
            this.object=o;
            level=l;
        }
 
        public Object getObject()
        {
            return object;
        }
 
        public int getLevel()
        {
            return level;
        }
       
        public String toString()
        {
            return "Node: {object="+object.toString()+", level="+level+"}
";
        }
 
        public Component getComponent()
        {
            return component;
        }
 
        public void setComponent(Component component)
        {
            this.component = component;
        }
    }
   
    public static void main(String[] args)
    {
        JFrame frame = new JFrame();
       
        JTree tree = new JTree();
        JScrollPane scroll = new JScrollPane(tree);
        frame.add(scroll,BorderLayout.NORTH);
       
        JPanel p = new JPanel();
        p.setLayout(new ColumnTreeLayout(tree.getModel(),p));
       
        frame.add(p,BorderLayout.CENTER);
       
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
   
    public ColumnTreeLayout(TreeModel model,Container parent)
    {
        nodes = createList(model);
        for(Node n:nodes)
        {
            JLabel label = new JLabel(n.getObject().toString());
            parent.add(label);
            n.setComponent(label);
        }
    }  
   
    private List<Node> createList(TreeModel model)
    {
        ArrayList<Node> list = new ArrayList<Node>();
        nodeToList((TreeNode)model.getRoot(),list,0);
        return list;
    }
   
    private void nodeToList(TreeNode root, List<Node> list, int level)
    {
        Node node = new Node(root,level);
        list.add(node);
       
        if(!root.isLeaf())
        {
            for(int i=0;i<root.getChildCount();i++)
            {
                nodeToList(root.getChildAt(i),list,level+1);
            }
        }
    }
    

    private Dimension calculatePreferredSize()
    {       
        int width =0, height =0;        
        int maxHeight = 0;
        
        for(int i=0;i<nodes.size();i++)
        {
            Node n = nodes.get(i);
            Component c = n.getComponent();
            Dimension prefs = c.getPreferredSize();
            maxHeight += prefs.height + verticalGap;
        }
        
        double heightPerEntry = maxHeight / nodes.size();
        
        int sqrt = (int) Math.ceil(Math.sqrt(nodes.size()));
        int optimalColHeight = (int) (heightPerEntry * sqrt);
                        
        int colHeight=0, colWidth =0;
        for(Node n:nodes)
        {
        	Dimension prefs = n.getComponent().getPreferredSize();
        	
        	if(colHeight + prefs.height + verticalGap>optimalColHeight)
        	{
        		height = Math.max(height, colHeight);
        		width += colWidth + levelGap;
        		colWidth = 0;
        		colHeight = 0;
        	}
        	
    		colHeight += prefs.height + verticalGap;
    		colWidth = Math.max(colWidth, prefs.width+levelGap*n.level);

        }
        
        height = Math.max(height,  colHeight);
        width +=colWidth + levelGap;
        
        Dimension d = new Dimension(width,height);
        return d;
        
    }
 
    @Override
    public void addLayoutComponent(String name, Component comp)
    {
       
    }
 
    @Override
    public void removeLayoutComponent(Component comp)
    {
        // TODO Auto-generated method stub
       
    }
 
    @Override
    public Dimension preferredLayoutSize(Container parent)
    {
    	return calculatePreferredSize();
    }
 
    @Override
    public Dimension minimumLayoutSize(Container parent)
    {
        return calculatePreferredSize();
    }
 
    @Override
    public void layoutContainer(Container parent)
    {
        int x = parent.getInsets().left;
        int y = parent.getInsets().top;
 
        int height = parent.getHeight();
        int columnWidth = 0;
       
        for(int i=0;i<nodes.size();i++)
        {
            Node n = nodes.get(i);
            Component c = n.getComponent();
            Dimension prefs = c.getPreferredSize();
           
            if(y+prefs.height > height)
            {
                y = parent.getInsets().top;
                x = x + columnWidth + levelGap;
            }
           
            int gap = levelGap*n.level;
           
            if(columnWidth < prefs.width + gap)
            {
                columnWidth = prefs.width + gap;
            }
           
            c.setBounds(x+gap,y, prefs.width, prefs.height);
            y += prefs.height + verticalGap;
        }
    }
 
    @Override
    public void addLayoutComponent(Component comp, Object constraints)
    {
        // TODO Auto-generated method stub
       
    }
 
    @Override
    public Dimension maximumLayoutSize(Container parent)
    {
    	int maxHeight = 0;
        int maxWidth = 0;
        
        for(int i=0;i<nodes.size();i++)
        {
            Node n = nodes.get(i);
            Component c = n.getComponent();
            Dimension prefs = c.getPreferredSize();
            maxHeight += prefs.height + verticalGap;
            maxWidth = Math.max(maxWidth, prefs.width+levelGap*n.level);
        }
               
        Dimension d = new Dimension(maxWidth,maxHeight);
        return d;
    }
 
    @Override
    public float getLayoutAlignmentX(Container target)
    {
        // TODO Auto-generated method stub
        return 0;
    }
 
    @Override
    public float getLayoutAlignmentY(Container target)
    {
        // TODO Auto-generated method stub
        return 0;
    }
 
    @Override
    public void invalidateLayout(Container target)
    {
        // TODO Auto-generated method stub
       
    }

	public int getLevelGap() {
		return levelGap;
	}

	public void setLevelGap(int l) {
		levelGap = l;
	}

	public int getVerticalGap() {
		return verticalGap;
	}

	public void setVerticalGap(int vGap) {
		verticalGap = vGap;
	}
    
    
   
}

@bERt0r

Jo, danke!!! :slight_smile:

Ich hab mir noch einen Hover und Click Listener reingeschrieben und benutze es jetzt im Programm! Hat tatsächlich noch einige andere Dinge einfacher gemacht!!

Danke

Falls du noch Interesse dran hast, ich habs jetzt auch so hingekriegt dass es sich wie ein JTree verhält. Aber da wäre ein HoverListener schon wieder schwerer einzubauen.