Swing Z-Order in einer JDesktopPane

jf

Bekanntes Mitglied
Hi, ich habe nochmal eine Frage hinsichtlich JDesktopPane:

Ich möchte eine Bildlaufleiste in eine JDesktopPane einfügen. Da die JDesktopPane ein Null-Layout besitzt, muss ich die Bildlaufleiste absolut positionieren. Soweit habe ich auch ein kleines ausführbares Bespiel gebastelt:

Java:
import java.awt.BorderLayout;
import java.awt.Component;

import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JScrollBar;
import javax.swing.UIManager;


@SuppressWarnings("serial")
public class DesktopPaneTest extends JDesktopPane {

	private static void changeUI(Component component) {
		try {
 			UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() );
 			javax.swing.SwingUtilities.updateComponentTreeUI(component);
		} catch(Exception ex) {
 			ex.printStackTrace();
		}
	}
	
	public DesktopPaneTest() {
		super();
	}
	
	public static void main(String[] args) {
		JFrame win           = new JFrame();
		DesktopPaneTest desk = new DesktopPaneTest();
		JInternalFrame fra   = new JInternalFrame();
		JScrollBar scroll    = new JScrollBar(JScrollBar.HORIZONTAL);

		win.setSize(800, 600);
		win.getContentPane().add(desk);
		win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		desk.add(fra);
		desk.add(scroll);
		
		fra.setBounds(100, 0, 200, 200);
		fra.setVisible(true);

		changeUI(win);
		win.setVisible(true);
		scroll.setBounds(0, 0, win.getContentPane().getWidth(), 17);
	}
}

Mein Problem ist, dass sich die JInternalFrames stets vor die Bildlaufleiste legen. - Es müsste aber genau umgedreht sein!

Ich komme hier leider nicht allein weiter... Folgendes habe ich bereits erfolglos ausprobiert:
Code:
desk.moveToFront(scroll);
Code:
desk.setComponentZOrder(scroll, 1);

Könnt ihr mir bitte weiterhelfen? 🙂
 
Leg die DesktopPane in eine Scrollpane oder willst du was bestimmtes mit der Scrollbar machen?
Ich habe bereits mehrere Beispiele aus dem Netz ausprobiert, welche so vorgehen, wie du es vorschlägst.
Hierbei kann man aber die Fenster nur über den rechten und unteren Rand schieben, damit der Ursprung sich stets an gleicher Stelle befindet.
Ich habe auch ein Beispiel vorliegen, welches dieses Manko zu umgehen versucht. Der Codes is allerdings sehr unleserlich und es gibt Fehler: wenn man einen Frame über den rechten Rand schiebt, erscheint die Bildlaufleiste, wenn man nun den Frame über den linken Rand schiebt, wird der Desktop zwar nach links erweitert - aber auf der rechten Seite nicht wieder verkürzt. Dies erreicht man nur, wenn man den Frame zunächst in der Mitte fallen lässt.
Dies ist zwar kein schlimmes Problem, zeigt aber gut, wie schnell hier suboptimales Verhalten entsteht. Das es noch sehr viel mehr solcher Probleme gibt, und ich außerdem noch eine weitere Funktionalität hinzufügen möchte (anstatt der Bildlaufleisten ein automatisches Hereinziehen der Frames), welche sich mit den verschiedenen Viewport-Positionen der JScrollPane nicht verträgt, bin ich dazu übergegangen, die Bildlauf-Funktionalität selbst zu implementieren.
Dies funktioniert auch verdammt gut - und zwar ohne oben geschilderte Macken!

Zunächst habe ich das ganze durch Komposition gelöst: JDesktopPane und Bildlaufleisten über BorderLayouts auf einem JPanel platziert. Allerdings hat es dabei immer das Hintergrundbild ein Stück versetzt, wenn die Bildlaufleisten ein- bzw. ausgeblendet wurden.

Daher habe ich nochmal alles überarbeitet und nutze nun die GlassPane, um die Bildlaufleisten anzuzeigen. Das ist schon fast perfekt. Allerdings finde ich es nicht gut, wenn eine Komponente außerhalb seines eigenen Territorium wütet - wenn jemand die GlassPane für etwas anderes nutzen möchte, könnte es Inkompatibilitäten mit meiner Komponente geben.
Außerdem bekomme ich mit .getBounds() nur die Position relative zur Elternkomponente - in der GlassPane muss ich die Komponenten aber relative zur ContentPane des zugehörigen JFrames positionieren. -> Um dem aus dem Weg zu gehen, habe ich einige Versuche hinsichtlich der absoluten Positionierung in der JDesktopPane gemacht - und bin leider gescheidert...

Daher meine Frage an die Profis hier, ob es nicht doch einen Weg hier gibt. - Dies wäre echt SUPER!!!
 
Dann würde ich mir quasi mein eigenes ScrollPane basteln. Das heißt ein Panel mit Borderlayout, center die desktopPane, rechts und unten die scrollbars.
 
Dann würde ich mir quasi mein eigenes ScrollPane basteln. Das heißt ein Panel mit Borderlayout, center die desktopPane, rechts und unten die scrollbars.
Da war ich auch schon:
"Zunächst habe ich das ganze durch Komposition gelöst: JDesktopPane und Bildlaufleisten über BorderLayouts auf einem JPanel platziert.
Allerdings hat es dabei immer das Hintergrundbild ein Stück versetzt, wenn die Bildlaufleisten ein- bzw. ausgeblendet wurden."


Dieses Vorgehen würde auch meine Vererbungshierarchie konterkarieren. Daher bin ich ja auf der Suche nach einer Alternative.

Mich würde zunächst interessieren, warum die JInternalFrames stets vor anderen Komponenten liegen.
Ändern lässt sich dies wohl leider nicht, wenn ich deine Hinweise richtig deute....

Gibt es aber vlt. einen anderen Weg? - z. B. eine eigene GlassPane, welche ich über die JDesktopPane legen könnte? :bahnhof:
 
Nunja es gibt das OverlayLayout, ich habs noch nie benutzt, es sieht aber nicht sehr kompliziert aus.
Sobald ich ein anderes Layout als das Null-Layout verwende, werden JInternalFrames stets maximiert, als wären sie im Zentrum eines BorderLayouts.

Gibt es die Möglichkeit, Steuerelemente im paintComponent-Ereignis zu zeichnen?
- Also den Geräte-Kontext eines bereits vorhandenen Steuerelementes an eine bestimmte Stelle in den Geräte-Kontextes eines weiteren Steuerelementes zu blitten?
 
Ich meine die Scrollbalken als Overlay über deine Desktoppane in deinem Panel. Das DesktopPane kann sein Layout behalten.
 
Ich meine die Scrollbalken als Overlay über deine Desktoppane in deinem Panel. Das DesktopPane kann sein Layout behalten.
Hmmm. Dann müsste ich wieder zur Komposition zurück... Aktuell erebe ich von JDesktopPane - so habe ich gleich alle Funktionalität und Methoden einer Standard-DesktopPane ohne weiteres Zutun.
Dies war auch einer der zwei Punkte, weshalb ich Die Bildlaufleisten in die JDesktopPane setzen wollte.

Ideal wäre es, wenn auch alle Klassen, welche von JComponent erben, eine GlassPane besitzen würden - leider ist dies nicht der Fall... 🙁

Aber es müsste doch auch die Möglichkeit des direkten Zeichnens geben. - Ich müsste nur wissen, wie ich an die Bitmap der Bildlaufleisten komme.

Könnte man vlt. auch einen undekorierten JInternalFrame als Parent für die Bildlaufleisten verwenden?
Hierfür müsste ich natürlich zumindest die Z-Order der JInternalFrames beeinflussen können.
 
Hab mirs nochmal kurz angeschaut: Was passt dir an der lösung nicht?
Java:
private JPanel contentPane;
	private JPanel panel;
	private JDesktopPane desktopPane;
	private JScrollBar scrollBar;
	private JInternalFrame internalFrame;
	private JInternalFrame internalFrame_1;
	private JScrollBar scrollBar_1;
	

	
	/**
	 * Create the frame.
	 */
	public DesktopPaneOverlay()
	{
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(100, 100, 450, 300);
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		contentPane.setLayout(new BorderLayout(0, 0));
		setContentPane(contentPane);
		
		panel = new JPanel();
		
		contentPane.add(panel, BorderLayout.CENTER);
		panel.setLayout(new MigLayout("", "[grow][]", "[grow][]"));
		
		desktopPane = new JDesktopPane();
		panel.add(desktopPane, "cell 0 0,grow");
		
		internalFrame = new JInternalFrame("New JInternalFrame");
		internalFrame.setBounds(59, 77, 132, 115);
		desktopPane.add(internalFrame);
		
		internalFrame_1 = new JInternalFrame("New JInternalFrame");
		internalFrame_1.setBounds(220, 53, 132, 149);
		desktopPane.add(internalFrame_1);
		
		scrollBar = new JScrollBar();
		panel.add(scrollBar, "cell 1 0,growy");
		
		scrollBar_1 = new JScrollBar();
		scrollBar_1.setOrientation(JScrollBar.HORIZONTAL);
		panel.add(scrollBar_1, "cell 0 1,growx");
		internalFrame_1.setVisible(true);
		internalFrame.setVisible(true);
	}
 
Hab mirs nochmal kurz angeschaut: Was passt dir an der lösung nicht?
Es ist Komposition. Diese Lösung wäre in meinen Augen nicht viel besser, als die GlassPane zu verwenden.

Ich habe jetzt ein wenig mit OverlayLayouts rumprobiert: wenn man dieses selbst implementiert, dann könnte man es mit den Eigenschaften des Null-Layouts versehen.
Allerdings habe ich es bisher nicht hinbekommen, dass eine Nicht-JInternalFrame-Komponente vor einem JInternalFrame lag. Lässt sich dies dennoch erreichen?
 
Also wenn du die Scrollbars einfach zur Desktoppane hinzufügst und ihnen einen Layer von 300 gibst liegen sie über den internal Frames
 
Also wenn du die Scrollbars einfach zur Desktoppane hinzufügst und ihnen einen Layer von 300 gibst liegen sie über den internal Frames
Genau diesen Tipp hätte ich etwas eher gebraucht... 😀

Mir war bewusst, dass die JDesktopPane eine erweitertere JLayeredPane ist. Hatte aber als zweiten Parameter der add()-Methode stets Integer-Werte zwischen 0 und 3 verwendet - als ich dann vorhin nochmal nach JLayeredPane recherchierte, fand ich des Rätels Lösung.
=> Wer hätte gedacht, dass es so einfach ist! :lol:

Um die Bildlaufleisten sehr elegant zu positionieren, habe ich mir dennoch vorgenommen, ein eigenes Layout zu entwickeln: das QuadrantLayout
Java:
  _____________
 |      |      |
 |  II. |  I.  |
 |______|______|
 |      |      |
 | III. |  IV. |
 |______|______|
Es sollte so funktionieren wie das BorderLayout - aber eben nur mit vier Zellen.
Die vertikale Bildlaufleiste würde dann in den I. Quadranten kommen, die horizontale Bildlaufleiste in den III. Quadranten.
Dann habe ich keinen Aufwand, mit der rechten unteren Ecke (IV. Quadrant).

Bevor ich hier sehr viel Zeit investiere, wollte ich fragen, ob es schon etwas in dieser Form gibt.
- Oder ob hier jemand, mit Ahnung von der Materie ein paar hilfreiche Tipps geben kann.
 
Zuletzt bearbeitet:
Ok, ich habe die Sache mit dem QuadrantLayout verworfen und mich für ein einfacheres spezialisiertes ScrollPaneLayout entschieden.
Jetzt funktioniert alles wunderbar. Danke für deine Unterstützung bERt0r!
 
Hallo, ich muss dieses Thema leider nochmal öffnen... ;(

Meine DesktopPane funktioniert eigentlich sehr gut - doch leider gibt es ein kleines Manko:
um die Bildlaufleisten VOR den JInternalFrames darzustellen setze ich das JPanel (transparent) mit den JScrollBars einfach in einen höheren Layer.

Nun musste ich aber feststellen, dass dadurch der Mauszeiger beeinflusst wird:
Wenn man über den Rand enes JInternalFrams positioniert, ändert er sich leider nicht zu einem Resizing-Cursor. Dies passiert erst, wenn man die Maustaste drückt! :autsch:

Ich habe viel probiert und keine andere Lösung gefunden, als die Bildlaufleisten in einen höheren Layer zu legen. Ich möchte auch unbedingt beibehalten, dass die DesktopPane die Klassen-Komponente ist (also die Bildlaufleisten der DesktopPane selbst hinzugefügt werden und beide ein eine gemeinsame andere Komponente).

Kennt ihr evtl. eine Möglichkeit, das Problem zu beseitigen? - Evtl. wenn ich irgendeine andere Holder-Komponente verwende als ein JPanel?
Oder muss ich doch den Mauszeiger selbst beeinflussen? - Und wenn ja, wie mache ich das?
 
Ich positioniere die Bildlaufleisten nun absolut und nicht mehr mittels eines eigenen Layouts, welches eine Holder-Komponente (die das Problem auslöst) benötigt. => so einfach kann es sein! 😀
 

Zurück
Oben