# Flackerndes Window beim DnD



## Sunchezz (13. Nov 2011)

Guten Morgen.


Ich habe ein JPanel in dem ich dynamisch Componenten hin und her schieben will...
Joa, mit dem Null-Layout klappt das soweit auch super einfach, mit nem MouseListener und nem einfachen setLocation()

Nun wollte ich das aber auch auf andere Layouts übertragen.
Meine Krieterien:
1. Es sollte beim verschieben angezeigt werden was verschoben wird (is ja eigentlich bekannt)
2. Es sollte während des Drags (Maus gedrückt halten [auch bekannt]) die nächste verankerung angezeigt werden wo die Komponente hinverschoben wird, sollte man nun loslassen.

Meine Lösungen kollidieren da allerdings mit diesen beiden Kriterien.

1. Lösungsidee:
  Verschiebungsvorschau: Komponente mit setLocation() verschieben
  Verankerungsanzeige: Temproräres sehr kurzes JPanel mit dicker grüner Border
  Problem: jPanel. validate() lässt die verschobene Komponente an der Maus zucken und flackern, ohne validate() wird aber die Verankerungsvorschau nicht bei jeder Mausbewegung aktualisiert. 

2. Lösungsidee:
  Verschiebungsvorschau: Komponente mit getGraphics() und customCorsor als Vorschau anbieten
  Verankerungsanzeige: Temproräres sehr kurzes JPanel mit dicker grüner Border
  Problem: Es sieht einfach nur lächerlich aus, und verursacht Augenkrebs -> verständlich bei 32x32 Pixel

3. Lösungsidee:
  Verschiebungsvorschau: Komponente auf GlassPane verschieben
  Verankerungsanzeige: das selbe wie schon zuvor
  Problem: Komponente wird im GlassPane garnicht erst angezeigt

4. Lösungsidee:
  Verschiebungsvorschau: Komponente in ein Window packen und mitverschieben
  Verankerungsanzeige: wie auch zuvor
  Problem: wie im Threadtitel, das Window flackert enorm, selbst wenn ich die Maus nicht bewege
  ich schätze das Liegt am validate des Panels.

Hat irgendjemand noch andere Ideen?

Ich hatte auch ganz kurz probiert die Verankerungsanzeige nicht mit einer Border, sondern über drawLine zu realisieren, doch das hat in der Dragbewegung ebenso geflackert.

Ich hoffe keiner will den Code sehn, denn das ist pures verzweiflungsschreiben mit wilden Ideen gewesen. Ich kann ja schonmal anfangen aufzuräumen!


----------



## Marco13 (13. Nov 2011)

Erstmal vorneweg: Du willst sowas wie ein Docking-Framework (wie Docking Frames ) mal kurz aus dem Ärmel schütteln?


----------



## Sunchezz (13. Nov 2011)

hmm... naja, mal eben aus dem Ärmel schütteln wollte ich das nicht, es sollte ja auch nicht so komplex werden!
Ist aber auch ziemlich verwirrend dieses Docking Frames!

Ich muss mal sehen ob ich das benutzen kann, aber im grunde wollte ich ja eigentlich nur nen paar Komponenten hin und her schieben,
Genau genommen funktioniert das ja auch schon sehr gut!
Nur diese Flackergeschichten müssen ja irgendwie umgangen werden können.
Hab bei meiner Recherche ja auch schon einige gefunden die das selbe gemacht haben, und die habens auch irgendwie gelöst, nur stehen nirgendwo (jedenfalls das was ich gefunden habe) konkrete Hinweise.


----------



## Marco13 (13. Nov 2011)

Es gibt auch noch andere Docking-Frameworks, das sollte nur ein Beispiel sein. Vielleicht ist das, was du vorhast, auch SO viel "weniger" als das, was durch solche Frameworks erlaubt wird, dass sich der Einsatz eines Frameworks nicht lohnt und man es besser per Hand macht. Aber diese "Vorschau" des Verschobenen und der "Einrastpositionen" klang schon nach etwas, wo man mit einigen GlassPane-getrickse usw. drangehen müßte. Kann man dazu schon irgendwelchen compilier- und testbaren Code posten? Dann kann man vielleicht eine einfache Lösung für das konkrete Problem (ohne Framework) finden...


----------



## Sunchezz (13. Nov 2011)

Joa, dann werde ich hier mal den MouseInputAdapter hinhauen...
Der wird jedem Objekt was auf dem Panel liegt beim adden hinzugefügt, sowohl als mouseListener als auch MMotionL.

Wie bereits erwähnt, wegen dem vielen rumprobieren bin ich noch zu keiner vernünftigen übersicht gekommen und habe bis jetzt auch auf die Doku verzichtet.
Fragt! 


```
package tests.gui;

IMPORTS....

public class ComponentMover extends MouseInputAdapter {
	private JComponent selectedComponent; // the actual selected Component to move
	private MobileContainerPanel container; // the parentContainer of selectedComponent
	private Point offset; // Point when selected a Component
	private int actualPos; // ZOrderPosition of the selectedComponent
	private JPanel anchor; // Visual Anchor for Possible new Position
	private int newZOrderPosition = -1; // the possible new ZOrderPosition
	private boolean anchorIsVisible;
	private Border anchorBorder;
	private Border editBorder;
	private Border selectionBorder;
	private boolean dragging;
	private boolean movingAllowed;

	public ComponentMover(MobileContainerPanel parent, Border edit) {
		dragging = false;
		container = parent;
		anchorBorder = BorderFactory.createLineBorder(Color.GREEN, 1);
		selectionBorder = BorderFactory.createLineBorder(Color.GREEN, 2);
		editBorder = edit;
	}

	public void mousePressed(MouseEvent e) {
		selectedComponent = (JComponent) e.getSource();
		offset = e.getPoint();
		dragging = true;
		selectedComponent.setBorder(selectionBorder);
		if (container.getLayout() != null) {
			actualPos = container.getComponentZOrder(selectedComponent);
			//container.validate();
		}
		container.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
		super.mousePressed(e);
	}

	public void mouseEntered(MouseEvent e) {
	}

	public void mouseMoved(MouseEvent e) {
		if (!e.getSource().equals(Cursor.DEFAULT_CURSOR)) {
			movingAllowed = false;
		} else {
			movingAllowed = true;
		}
	}

	public void mouseReleased(MouseEvent e) {
		dragging = false;
		if (container.getLayout() instanceof FlowLayout) {			
			if (newZOrderPosition != -1) {
				container.add(selectedComponent);
				container.setComponentZOrder(selectedComponent,
						newZOrderPosition);
			}
			if (anchorIsVisible) {
				container.remove(anchor);
				anchorIsVisible = false;
			}
		}
		selectedComponent.setBorder(editBorder);
		setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
		container.validate();
		actualPos = -1;
		newZOrderPosition = -1;
		super.mouseReleased(e);
	}

	public void mouseDragged(MouseEvent e) {
		if (dragging) {
			if (container.getLayout() == null) {
				selectedComponent.setBounds(calculateRect(e.getPoint()));
			}
			if (container.getLayout() != null) {
				if (anchorIsVisible) {
					removeAnchor();
					container.validate();
				}
				int newPosition = this.getPossibleNewPosition(e);
				if (newPosition != -1 && newPosition != actualPos
						&& newPosition != actualPos + 1) {
					showAnchorAt(newPosition);
					container.validate();
				}
			}
		}
		super.mouseDragged(e);
	}

	private Rectangle calculateRect(Point p) {
		Rectangle r = selectedComponent.getBounds();
		r.x += p.getX() - offset.x;
		r.y += p.getY() - offset.y;
		return r;
	}

	private void showAnchorAt(int position) {
		if (!anchorIsVisible) {
			anchor = new JPanel();
			anchor.setBorder(anchorBorder);
			anchor.setName("Anchor");
			anchor.setPreferredSize(new Dimension(2, 20));
			container.add(anchor);
			container.setComponentZOrder((Component) anchor, position);
			anchorIsVisible = true;
		}
	}

	private void removeAnchor() {
		container.remove(anchor);
		anchorIsVisible = false;
	}

	private int getPossibleNewPosition(MouseEvent e) {
		Point p = e.getPoint();
		Point newP = SwingUtilities.convertPoint((Component) e.getSource(), p,
				container);
		Component underLayingComponent = container.getComponentAt(newP);
		newZOrderPosition = actualPos;
		if (underLayingComponent != this.anchor) {
			if (underLayingComponent != container) {
				if (underLayingComponent != null) {
					int xposition = isOverXHalfOfComponent(newP,
							underLayingComponent);
					int actualZOrderPos = container
							.getComponentZOrder(underLayingComponent);
					switch (xposition) {
					case 1: // onLeftSideOverHalf = 1;
						if (actualZOrderPos == 0) {

						}
						newZOrderPosition = actualZOrderPos;
						break;
					case 2: // onRightSideOverHalf = 2;
						newZOrderPosition = actualZOrderPos + 1;
						break;
					default:
						break;
					}
				}
			}
		}
		return newZOrderPosition;
	}

	private int isOverXHalfOfComponent(Point p, Component c) {
		int onLeftSideOverHalf, onRightSideOverHalf;
		onLeftSideOverHalf = 1;
		onRightSideOverHalf = 2;

		try {
			int C_x;
			C_x = c.getX();
			int c_x_length = c.getWidth();
			int halfPointX = (c_x_length / 2) + C_x;
			if (p.getX() <= halfPointX) {
				return onLeftSideOverHalf;
			} else {
				return onRightSideOverHalf;
			}
		} catch (Exception e) {
			e.printStackTrace();
			return -1;
		}
	}

	private void setCursor(Cursor c) {
		try {
			container.setCursor(Cursor
					.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
		} catch (HeadlessException e1) {
			e1.printStackTrace();
		}
	}
}
```


Ich hab jetzt irgendwie schon ziemlich viel mit dem "GlassPane-Getrickse" gehört, und hab das auch selbst versucht, aber wie oben beschrieben, bei mir führt das nur zu ekligen Zuckungen der Komponenten -.-
Vielleicht hab ich ja was falsch gemacht, bzw. nich nur vielleicht, bei anderen scheints ja zu gehen, aber ich weiß halt nich was.
Und der Entwurf mit dem GlassPane ist auch schon einige Versionen alt^^

Wär trotzdem Nett wenn vielleicht jemand nen Ansatz für mich hat!


----------



## Marco13 (13. Nov 2011)

Ja, so viel zu "compilier- und testbar"  Aber nochmal zum Eingangsbeitrag bzw. dem Glasspane: Ich würde spontan sagen, dass der Ansatz 3 (mit dem Glasspane) eben am vielversprechendsten ist. Wenn die verschobene Component dort nicht angezeigt wird, würde ich mal versuchen, herauszufinden, warum das so ist. Das GlassPane hätte ja ein null-Layout, hast du da vielleicht die Bounds nicht richtig gesetzt oder so...? (Und kann man DAvon ein testbares KSKB posten?)


----------



## Sunchezz (13. Nov 2011)

arg... ein testbares ja? ^^
hmm naja, das ist der komplette MouseInputAdapter oben, der is schon soweit aufgeräumt und gekürzt wie ich Zeit hatte!

ähm, mit dem GlassPane bin ich mir sehr sich das es nullLayout hatte und die bounds richtig gesetzt waren, aber ich probiere es gerne noch mal!

werde wohl gleich zurück sein^^


----------



## Sunchezz (13. Nov 2011)

Gut:

Hier der Neue Adapter:
- hinzugekommen ist die Methode createGlassPane(JComponent)
- ein aufruf in mouseDragged() zum setzen der Bounds
- und ein übergeben der selectedComponent an das GlassPane in mousePressed()


```
package tests.gui;

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.HeadlessException;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;

import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
import javax.swing.event.MouseInputAdapter;

public class ComponentMover extends MouseInputAdapter {
	private JComponent selectedComponent; // the actual selected Component to
											// move
	private JPanel container; // the parentContainer of
											// selectedComponent
	private Point offset; // Point when selected a Component
	private int actualPos; // ZOrderPosition of the selectedComponent
	private JPanel anchor; // Visual Anchor for Possible new Position
	private int newZOrderPosition = -1; // the possible new ZOrderPosition
	private boolean anchorIsVisible;
	private Border anchorBorder;
	private Border editBorder;
	private Border selectionBorder;
	private boolean dragging;
	private boolean movingAllowed;
	private JPanel glassPane;

	public ComponentMover(JPanel parent, Border edit) {
		dragging = false;
		container = parent;
		anchorBorder = BorderFactory.createLineBorder(Color.GREEN, 1);
		selectionBorder = BorderFactory.createLineBorder(Color.GREEN, 2);
		editBorder = edit;
	}

	public void mousePressed(MouseEvent e) {
		selectedComponent = (JComponent) e.getSource();
		offset = e.getPoint();
		dragging = true;
		selectedComponent.setBorder(selectionBorder);
		createGlassPane(selectedComponent);
		if (container.getLayout() != null) {
			actualPos = container.getComponentZOrder(selectedComponent);
			//container.validate();
		}
		container.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
		super.mousePressed(e);
	}
	
	private void createGlassPane(JComponent c) {
		((JFrame)JFrame.getFrames()[0]).setGlassPane(null);
		JPanel p = new JPanel();
		p.setBackground(new Color(255, 255, 255, 200));
		p.setVisible(true);
		p.setLayout(null);
		p.add(c);
		((JFrame)JFrame.getFrames()[0]).setGlassPane(p);
	}

	public void mouseEntered(MouseEvent e) {
	}

	public void mouseMoved(MouseEvent e) {
		if (!e.getSource().equals(Cursor.DEFAULT_CURSOR)) {
			movingAllowed = false;
		} else {
			movingAllowed = true;
		}
	}

	public void mouseReleased(MouseEvent e) {
		dragging = false;
		if (container.getLayout() instanceof FlowLayout) {			
			if (newZOrderPosition != -1) {
				container.add(selectedComponent);
				container.setComponentZOrder(selectedComponent,
						newZOrderPosition);
			}
			if (anchorIsVisible) {
				container.remove(anchor);
				anchorIsVisible = false;
			}
		}
		selectedComponent.setBorder(editBorder);
		setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
		container.validate();
		actualPos = -1;
		newZOrderPosition = -1;
		super.mouseReleased(e);
	}

	public void mouseDragged(MouseEvent e) {
		if (dragging) {
			if (container.getLayout() == null) {
				selectedComponent.setBounds(calculateRect(e.getPoint()));
			}
			if (container.getLayout() != null) {
				Rectangle r = calculateRect(e.getPoint());
				selectedComponent.setBounds(r);
				if (anchorIsVisible) {
					removeAnchor();
					container.validate();
				}
				int newPosition = this.getPossibleNewPosition(e);
				if (newPosition != -1 && newPosition != actualPos
						&& newPosition != actualPos + 1) {
					showAnchorAt(newPosition);
					container.validate();
				}
			}
		}
		super.mouseDragged(e);
	}

	private Rectangle calculateRect(Point p) {
		Rectangle r = selectedComponent.getBounds();
		r.x += p.getX() - offset.x;
		r.y += p.getY() - offset.y;
		return r;
	}

	private void showAnchorAt(int position) {
		if (!anchorIsVisible) {
			anchor = new JPanel();
			anchor.setBorder(anchorBorder);
			anchor.setName("Anchor");
			anchor.setPreferredSize(new Dimension(2, 20));
			container.add(anchor);
			container.setComponentZOrder((Component) anchor, position);
			anchorIsVisible = true;
		}
	}

	private void removeAnchor() {
		container.remove(anchor);
		anchorIsVisible = false;
	}

	private int getPossibleNewPosition(MouseEvent e) {
		Point p = e.getPoint();
		Point newP = SwingUtilities.convertPoint((Component) e.getSource(), p,
				container);
		Component underLayingComponent = container.getComponentAt(newP);
		newZOrderPosition = actualPos;
		if (underLayingComponent != this.anchor) {
			if (underLayingComponent != container) {
				if (underLayingComponent != null) {
					int xposition = isOverXHalfOfComponent(newP,
							underLayingComponent);
					int actualZOrderPos = container
							.getComponentZOrder(underLayingComponent);
					switch (xposition) {
					case 1: // onLeftSideOverHalf = 1;
						if (actualZOrderPos == 0) {

						}
						newZOrderPosition = actualZOrderPos;
						break;
					case 2: // onRightSideOverHalf = 2;
						newZOrderPosition = actualZOrderPos + 1;
						break;
					default:
						break;
					}
				}
			}
		}
		return newZOrderPosition;
	}

	private int isOverXHalfOfComponent(Point p, Component c) {
		int onLeftSideOverHalf, onRightSideOverHalf;
		onLeftSideOverHalf = 1;
		onRightSideOverHalf = 2;

		try {
			int C_x;
			C_x = c.getX();
			int c_x_length = c.getWidth();
			int halfPointX = (c_x_length / 2) + C_x;
			if (p.getX() <= halfPointX) {
				return onLeftSideOverHalf;
			} else {
				return onRightSideOverHalf;
			}
		} catch (Exception e) {
			e.printStackTrace();
			return -1;
		}
	}

	private void setCursor(Cursor c) {
		try {
			container.setCursor(Cursor
					.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
		} catch (HeadlessException e1) {
			e1.printStackTrace();
		}
	}
}
```

TestKlasse:

```
package tests.gui;

import java.awt.BorderLayout;

public class Test extends JFrame {

	private JPanel contentPane;

	public static void main(String[] args) {
		Test frame = new Test();
		frame.setVisible(true);			
	}


	public Test() {
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(100, 100, 360, 119);
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		contentPane.setLayout(new BorderLayout(0, 0));
		setContentPane(contentPane);
		
		JPanel panel = new JPanel();
		contentPane.add(panel, BorderLayout.CENTER);
		
		ComponentMover listener = new ComponentMover(panel, BorderFactory.createLineBorder(
				Color.RED, 3));
		
		JLabel lblNewLabel = new JLabel("LABEL1");
		lblNewLabel.addMouseListener(listener);
		lblNewLabel.addMouseMotionListener(listener);
		panel.add(lblNewLabel);
		
		JLabel lblNewLabel_1 = new JLabel("LABEL2");
		lblNewLabel_1.addMouseListener(listener);
		lblNewLabel_1.addMouseMotionListener(listener);
		panel.add(lblNewLabel_1);
		
		JLabel lblNewLabel_2 = new JLabel("LABEL3");
		lblNewLabel_2.addMouseListener(listener);
		lblNewLabel_2.addMouseMotionListener(listener);
		panel.add(lblNewLabel_2);
	}

}
```


also mir ist gut bewusst das der kleine Test noch grausam aussieht, ich habe eigentlich noch ein abgeleitetes Panel auf dem die Komponenten normaler weise liegen, das tut aber für das eigentlich Problem hier nichts zur sache, bitte ignoriere auch die Borders, die haben eben genau was mit dem ParentPanel zu tun 

jedenfalls bewegt sich da nicht viel auf GlassPane!


Ich hoffe ich hab auf die schnelle nichts vergessen, sieht aber vom verhalten so ungefähr aus wie vorhin!


----------



## Marco13 (14. Nov 2011)

Ja, hab's nur mal kurz getestet - und der Test IST "grausam", ich glaube kaum, dass man ohne eine systematischere, strukturiertere Herangehensweise da weit kommt. Scheint als würden da etliche Sachen gemacht, ohne genau zu wissen, was sie bewirken sollen. Sowas wie

```
public void mouseMoved(MouseEvent e) {
        if (!e.getSource().equals(Cursor.DEFAULT_CURSOR)) {
```
hat ja schon fast einen gewissen Unterhaltungswert, aber ... was soll die Prüfung auf FlowLayout? Und was soll das Z-Order-Gehacke? Was auch immer da bisher zu funktionieren scheint, ist vermutlich nicht viel mehr als Zufall. Ich gehe davon aus, dass es SEHR schwierig ist, das "vernünftig" zu machen, und wenn man das macht, kommt eben etwas komplexeres raus...

Mit einer festen GlassPane würde es wohl eher funktionieren, aber .... man sollte dann (vermutlich! ist auch nur geraten) die aktuelle Component tatsächlich auf der GlassPane herumschieben. Wie man dann die Einfügeposition berechnet (die für jedes Layout verschieden sein muss, und deren Berechnung schon für's FlowLayout wahnsinnig kompliziert wäre) müßte man sich noch überlegen. Vom Anzeigen dieser Einfügeposition mal ganz abgesehen.

Wie viele Personenmonate willst du da rein investieren?


----------



## Sunchezz (14. Nov 2011)

Also ich darf behaupten das ich alles ziemlich gut verstanden habe!
Wüsste jetzt auch nicht was das so aussehen lässt!



Marco13 hat gesagt.:


> Sowas wie
> ...
> public void mouseMoved(MouseEvent e) {
> if (!e.getSource().equals(Cursor.DEFAULT_CURSOR)) {




Ich hatte vorher gesagt das ich viel einfach nur rumprobiert habe...
Und beim aufräumen vielleicht was übersehen habe.
Im übrigen is die obere Prüfung dafür gut gewesen, da ich noch ein anderen Listener habe der dann noch für das einstellen der größe für die Komponenten zuständig ist!
und wenn ein resize-cursor drauf is, soll er nich anfangen beim gedrückt halten die komponente zu verschieben, sondern die größe ändern -.-

sorry, das ichs übersehen habe

Ich nehem gerne Tips an für blödsinnigen Quellcode und wie mans besser machen kann.

Aber funktionieren tuts jetzt bei mir so wie ich das wollte, bis auf eine kurze Positionenberechnung wofür ich noch keine Zeit hatte!

Wenns dich interessiert kann ich dir gerne eine PM für die Jar schicken!


EDIT:
wenn das Null-Layout an ist, brauch ich keine Komponenten auf ein glasspane schieben, auch keine Verankerungsanzeige hinhauen... 
Also wenn ich nicht auf FlowLayout prüfe, wie soll ichs dann machen?

Was meinst du bitte mit dem "Z-Order gehacke"?
"ZOrder" is der von Sun geprägte begriff für die position im FlowLayout. 

```
setComponentZOrder(Component comp, int pos)
```
wie soll ich denn die komponente sonst an eine  andere stelle verschieben, wenn nich über


----------



## Marco13 (14. Nov 2011)

> wenn das Null-Layout an ist, brauch ich keine Komponenten auf ein glasspane schieben, auch keine Verankerungsanzeige hinhauen...
> Also wenn ich nicht auf FlowLayout prüfe, wie soll ichs dann machen?
> 
> Was meinst du bitte mit dem "Z-Order gehacke"?
> ...



Ja... es sah eben recht unstrukturiert aus... ob der Vergleich einer Event-Quelle mit einem Cursor nun eine Altlast oder unverstanden zusammenkopierte Zeilen sind, kann man eben nicht unterscheiden - und ob die Verwendung der ComponentZOrder eine Altlast oder unverstanden zusammenkopierte Zeilen sind, auch nicht - zumindest erkenne ich keinen Zusammenhang zwischen FlowLayout und Z-Order (die Z-Order ist die Reihenfolge in Z-Richtung - sagt also AFAIK nichts darüber aus, ob die Component im FlowLayout links oder rechts landet). Das IST ja das "wahnsinnig komplizierte" was ich oben meinte: Wenn man von den drei Labels das mittlere verschiebt, sollen die beiden Äußeren sich dann zusammenbewegen, als wäre das mittlere nicht da? Wenn man das Mittlere dann nach links bewegt, sollen die anderen beiden sich dann nach rechts bewegen? Wie soll man das machen, außer alle Components zu entfernen, das bewegte als erstes (und damit ganz links) und die anderen beiden dann hinterher wieder einzufügen? Aber dem Layout kann man die Positionierung nicht überlassen, man hält das Label ja noch mit der Maus fest, also muss dort ein "Dummy" eingefügt werden, und erst wenn man losläßt das echte Label. Bei einem BorderLayout hätte man da mehr Freiheiten, aber obwohl das Platzieren dort konzeptuell "ganz anders" ist, stellen sich ähnliche Fragen.
Ich stelle es mir jedenfalls so kompliziert vor, dass man es nicht an einem Tag mit 200 Zeilen schreibt. Aber wenn es jetzt zu deiner Zufriedenheit funktioniert, ist ja alles in Ordnung, vielleicht hattes du gar nicht vor, das so "ausgearbeitet" zu machen, wie ich vermutet habe.


----------



## Sunchezz (14. Nov 2011)

Doch, habe schon noch vor das ein wenig auszuarbeiten...
Sitze jetzt aber auch schon drei Tage dran!

Ähm, ja, hatte ich oben gesagt das alles nen bischen unstrukturiert is.
Aber ich denke schon das ich alles verstanden hab was ich geschrieben hab, und kopiert war da absolut nichts.

Dann habe ich den Sinn von ZOrder wohl missverstanden, aber erfüllen tuts beim FlowLayout ja seinen Zweck oder nicht?
Hab dir die Version ja geschickt und da reicht ja ZOrder aus.

Bis auf die Point-Konvertierung läufts ja erstmal fast Bugfrei.


----------



## Marco13 (15. Nov 2011)

Ja, hab's gesehen... wie und warum das mit der Z-Order funktionieren sollte, weiß ich nicht, aber wie gesagt: Wenn's so für dich passt, und es nicht wichtig ist, ist es ja OK


----------



## Sunchezz (15. Nov 2011)

API:
"Returns the z-order index of the component inside the container. The higher a component is in the z-order hierarchy, the lower its index. The component with the lowest z-order index is painted last, above all other child components."

War für mich der anlass das zu nehemen^^
Was passenderes hab ich garnich erst gefunden, vor allem is das aus Container, und wenn ich Glück habe, dürfte das sogar für alle anderen Layouts auch funktionieren 

Du scheinst damit aber nich zufrieden zu sein, hättest du insgesamt Verbesserungsvorschläge?
Es sind noch einige Erweiterungen geplant, und mein Ziel ist eigentlich das vielleicht doch noch als eigenes Framework zusammen zu hacken 

Aber ich bedanke mich jetzt hier trotzdem schonmal für deine Unterstützung!
Hat mir hier sehr geholfen!


----------



## Marco13 (15. Nov 2011)

Nochmal: Das Layout und die Z-Order haben nichts miteinander zu tun. Wenn man ein Flow-Layout mit 3 Labels hat, ist egal, ob die Zeichenreihenfolge "Links-Mitte-Rechts" oder "Links-Rechts-Mitte" ist: Was Links ist, ist Links, was Rechts ist, ist Rechts, und was tautologisch ist, ist taustologisch. Ich sehe keine Möglichkeit, die Reihenfolge der Components in einem Flow-Layout zu ändern, außer, alle Components zu entfernen und in der neuen Reihenfolge wieder einzufügen. Kannst ja mal einen Frame mit 3 Labels erstellen, und mal die Z-Order der 3 Labels ändern, und schauen, ob das auswirkungen auf's Layout hat...

Vielleicht schaut Beni mal hier rein, der hat DockingFrames nämlich geschrieben, und nachdem er damit fertig ist, sich über die bisherigen Aussagen (deine und meine) kauputt-zu :lol: en, kann er da vielleicht ein paar Tipps geben  Ingesamt gehe ich davon aus, dass es SEHR aufwändig ist, wenn man das "gut" machen will....


----------



## Sunchezz (15. Nov 2011)

also ich bin ja immer gerne selbstkritisch, aber ich finde ich habs schon halbwegs "gut" gemacht 

Klar, besser geht immer alles, aber für drei tage arbeit und ohne Vorkenntnisse, bin ich ganz zufrieden.


hmm... interessant:

```
package tests.gui;

import java.awt.BorderLayout;

public class Test extends JFrame {

	private JPanel contentPane;
	private JTextField textField;
	private JPanel panel;

	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					Test frame = new Test();
					frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	public Test() {
		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 FlowLayout(FlowLayout.CENTER, 5, 5));
		
		JLabel lblNewLabel = new JLabel("New label");
		panel.add(lblNewLabel);
		
		JLabel lblLabel = new JLabel("Label2");
		panel.add(lblLabel);
		
		textField = new JTextField();
		panel.add(textField);
		textField.setColumns(10);
		
		JPanel panel_1 = new JPanel();
		contentPane.add(panel_1, BorderLayout.NORTH);
		
		JButton btnChange = new JButton("Change");
		btnChange.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				panel.setComponentZOrder(textField, 1);
				panel.validate();
			}
		});
		panel_1.add(btnChange);
	}

}
```

also bei mir gehts einwandfrei...
oder anders gesagt: Genau das was ich von Anfang an erwartet hatte.

Also mag ja sein das ichs nich komplett gerafft habe, aber ohne dir nahe treten zu wollen, vielleicht hast dus auch missverstanden? (Bitte nich falsch verstehen :noe
Das is zumindest grade meine einzige erklärung^^


Dann hoff ich mal das Beni vorbei kommt, hätte gerne noch ein paar tips


----------



## Marco13 (15. Nov 2011)

Ja, ich hab' mal geschaut: Die ComponentZOrder verändert gerade die Reihenfolge der Components im Container (d.h. die Reihenfolge in der sie gezeichnet werden). Dass das auch gerade die Reihenfolge der Components beim FlowLayout ist, ist "Zufall" und nirgendwo spezifiziert. Auf jeden Fall gibt es keine Entsprechung dazu in anderen LayoutManagern.


----------



## Sunchezz (15. Nov 2011)

Ok, stimmt wohl so 

Hab das mal näher überprüft...
Layouts bei denen das zufällig zu gehen scheint:
GridLayout, FlowLayout, BoxLayout

Joa, jetzt sind wa schlauer xD

um die Positionen auf dem "normalen" weg neu zu setzen müsste ich dann mit den Mangern kommunizieren, und Positionen wie "CENTER" und so angeben oder?


----------



## Marco13 (15. Nov 2011)

Sunchezz hat gesagt.:


> Positionen wie "CENTER" und so angeben oder?



Genau das meinte ich: Beim FlowLayout geht das nicht. Man könnte sich mal anschauen, ob es die Layout-Constraints irgendwie berücksichtigt, aber meines Wissens nicht. Zumindest kann man nicht davon ausgehen, dass es eine _allgemeine_ Möglichkeit gibt, für ein _Beliebiges_ Layout die Einfügeposition anzugeben (... und noch viel Spaß beim GridBagLayout  ). Die einzige Möglichkeit, die ich sehen würde, wäre, das für jedes Layout spezifisch auszuprogrammieren - aber wenn man die Einfügeposition (einschließlich der Abmessungen der Component NACH dem Einfügen, also quasi eine komplette "Vorschau") anzeigen wollte, würde es richtig kompliziert werden...


----------



## Sunchezz (15. Nov 2011)

Ja, es für jedes Layout spezifisch einzuhacken war bis jetzt sowieso mein Plan...
zumal die Berechnung für jede neue Position ja sowieso Layoutabhängig ist!

"Constraints" war das Wort was mir gefehlt hat^^
Darauf wirds wohl hinauslaufen...

Naja, ich glaube wenn dann lass ich das GridBag aus...
oder füge es mal noch hinzu wenn ich nen Jahr lang langeweile habe und arbeitslos bin 

Das mit den Abmessungen der Komponente für die neue Position hatte ich schonmal drin, war jetzt nicht so kompliziert. 
Dem Anchor die gleichen abmasse geben, das validate hat den rest der aktualisierung abgenommen, jedenfalls funktioniert das mit der aktuellen methode. Sollte ja aber auch kein Ding sein (Gesetz dem fall das mit den Constraints funktioniert) die selektierte Komponente zu entfernen, über Constraints den Anchor mit den Maßen zu setzen und dann wieder zu validaten.

Aber diesen Ansatz habe ich verworfen, da das ein umso Größeres hinund hergespringe der Komponenten nachsich zieht.

Für bessere Komponentenplatzierung war meine Idee eigentlich, die Möglichkeit "unsichtbare" platzhalter einzufügen, die in Ihrer größe verstellbar sind. Das sollte jedenfalls für die "unkomplexen" Layouts ausreichen.

So wie das momentan aussieht, hab ich noch einen heiden spaß vor mir 
Werden wohl noch ein paar threads dazu folgen


----------



## Marco13 (15. Nov 2011)

Sunchezz hat gesagt.:


> Das mit den Abmessungen der Komponente für die neue Position hatte ich schonmal drin, war jetzt nicht so kompliziert.
> ...
> Für bessere Komponentenplatzierung war meine Idee eigentlich, die Möglichkeit "unsichtbare" platzhalter einzufügen, die in Ihrer größe verstellbar sind.



Wieder: Im einfachsten Fall, ja. Aber die Frage, ob die bei getMinimumSize, getPreferredSize und getMaximumSize zurückgelieferten Werte vom LayoutManager berücksichtigt werden (und wie) hängt auch wieder vom LayoutManager ab. D.h. selbst wenn man an die Zielposition eine "Dummy-Component" einfügt, um eine Vorschau zu haben, kann es sein, dass die Component eine andere Größe bekommt, wenn sie denn tatächlich dort losgelassen wird. Nahe liegend wäre dann, die besagten Methoden der Dummy-Component an die aktuell bewegte Component weiterzudelegieren, aber das kann auch hakelig sein. Abgesehen davon...


> Aber diesen Ansatz habe ich verworfen, da das ein umso Größeres hinund hergespringe der Komponenten nachsich zieht.


... könnte das natürlich beliebig häßlich werden. Man kann ja versuchen, sich bei sowas wie Eclipse und seiner Docking-Funktionalität was abzuschauen, aber auch nur konzeptuell - die Implementierung dürfte da ein paar Größenordnungen aufwändiger sein. 
Naja. Mal schauen was da noch draus wird...


----------



## Beni (15. Nov 2011)

Ich weiss ja nicht genau wo du jetzt stehst, und was für eine Hilfe du gerade brauchst/erwartest, aber meine Meinung ist folgende:

Mit den Standard-LayoutManagern wirst du immer das Problem haben, dass jedesmal wenn du die Component umplazierst das ganze Layout springt. Wenn du unbedingt die Standard-LayoutManager benutzen willst, dann würde ich das etwa so lösen:

1. Ausrechnen wo die verschobene Componente etwa hinkommt, ohne sie tatsächlich zu verschieben. D.h. Methoden wie "validate" werden nie aufgerufen. Das kann man entweder machen, indem man sich den LayoutManager genau anschaut und den Code analysiert (z.B. beim GridLayout wäre das möglich). Oder indem man sich eine unsichtbare Kopie der Oberfläche zusammenbaut, und dort eine Kopie der Component verschiebt.

2. Je nachdem wie gut die Berechnung ist, kann man in einem GlassPanel ein paar Markierungen (z.B. ein Rechteck) zeichnen, dort wo die Component etwa hinkommen würde.


Noch eine Stufe härter:

3. Den LayoutManager temporär entfernen und das "null" Layout verwenden. Dann berechnen wo die Componenten landen könnten, wenn der Benutzer seine Component verschieben würde. Da erhälst du mehr als ein Resultat, da der Benutzer die Component an mehr als eine Stelle schieben könnte. Aber damit kannst du eine Art Animation bauen: der Benutzer hat die Component etwas nach rechts geschoben? Also 25% der "rechten" Lösung + 75% der "linken" Lösung nehmen, und die Componenten entsprechend verschieben. Dann machst du statt einem riesigen Sprung viele kleine, und das sieht besser aus. Wenn der Benutzer die Maus los lässt, kannst du den LayoutManager wieder installieren.


Und ums sich das Leben einfacher zu machen:

4. Verzichte auf die Standard-LayoutManager. Implementier selber LayoutManager, die dürfen sich aber wie ihre Vorbilder benehmen. Diesen LayoutManagern kannst du Methoden einbauen um verschiedene Vorschauen zu berechnen. Da du sie selbst schreibst, ist das wesentlich einfacher als wenn du die existierenden LayoutManager entschlüsseln musst.


----------



## Beni (16. Nov 2011)

Danke für das Beispiel.

Ich finde es ein bisschen komisch, dass die Maus immer oben links von der Component ist, und nicht dort wo man die Componente ergriffen hat.

Und das Resize das Höhen-Breiten-Verhältnis beibehält finde ich nicht intuitiv.

Dass da noch was rumspringt hast du ja selbst bemerkt. Ich bleibe bei meiner Behauptung, dass du mit den Standard-LayoutManagern nie glücklich wirst...



Den Code selbst finde ich so lala (ist nicht böse gemeint). Du bist ganz klar kein Anfänger mehr, aber auch noch nicht der Überprofi ;-)  Ich denke so wie das Programm jetzt ist, kann man es als Prototyp bezeichnen. Vielleicht lohnt es sich noch ein paar Tage in den Prototyp zu investieren, bis alles halbwegs brauchbar funktioniert, und dann erst mit dem "echten" Programm beginnen. Beim zweiten Start hast du soviel Vorwissen, dass das Projekt automatisch 100 mal besser wird. Für mein Framework hatte ich ebenfalls einen Prototypen, und im Rückblick war das eine gute Idee.

Ein bisschen mehr Abstraktion, und Möglichkeiten alles zu konfigurieren wären sicherlich nett. Persönlich gehe ich immer davon aus, dass ein Interface zuviel besser als eines zuwenig ist. Also habe keine Angst viele Interfaces und Klassen zu machen. z.B. der ComponentMover ist bereits ein bisschen komplex, er sollte nicht gleichzeitig ein Border setzen, Mouse überwachen, und GlassPane verwalten. Ich sehe hier Potential für mindestens 3 Klassen (GlassPane, Border-Configuration, ComponentMover, ...).


Falls ich dir etwas anders als Allgemeinplätze liefern solle, musst du noch ein passende Frage stellen ;-)


----------



## Sunchezz (17. Nov 2011)

Beni hat gesagt.:


> Ich finde es ein bisschen komisch, dass die Maus immer oben links von der Component ist, und nicht dort wo man die Componente ergriffen hat.


Das hat nen gewissen Grund:
Wenn das zu bearbeitende Panel nicht allzugroß ist, oder man eine Große Komponente (und der Rest nur kleine), dann ists mit der übersicht nich so dolle, wenn man die zuverschiebene Komponente irgendwo greift. Hauptsächlich deswegen, weil es dann schnell passieren kann das man den Anker nicht mehr sehen kann!




Beni hat gesagt.:


> Und das Resize das Höhen-Breiten-Verhältnis beibehält finde ich nicht intuitiv.


Das hab ich heute schon behoben, hat mich auch ein wenig genervt.


Mit dem nicht mehr Anfänger, seh ich jetzt mal definitiv als Kompliment 
Aber ich geb dir Recht, das war von Anfang an auch nur als Prototyp geplant, vielleicht ist das auch am Packagenamen aufgefallen: "gui.tests" xD
Angefangen hats als Schnapsidee 
Ich wollte einfach erstmal die Funktionalität erproben, denn ich seh mich noch nicht als "nicht-Anfänger" und wusste nich obs überhaupt so einfach machbar is.
Irgendwie muss ich mich hier ja für diesen unstrukturierten Quelltext rechtfertigen 




Beni hat gesagt.:


> Ein bisschen mehr Abstraktion, und Möglichkeiten alles zu konfigurieren wären sicherlich nett. Persönlich gehe ich immer davon aus, dass ein Interface zuviel besser als eines zuwenig ist. Also habe keine Angst viele Interfaces und Klassen zu machen. z.B. der ComponentMover ist bereits ein bisschen komplex, er sollte nicht gleichzeitig ein Border setzen, Mouse überwachen, und GlassPane verwalten. Ich sehe hier Potential für mindestens 3 Klassen (GlassPane, Border-Configuration, ComponentMover, ...).


Steht alles noch auf der ToDoList 
Geplant sind ne Menge Einstellungsmöglichkeiten...
Ich will eigentlich auch ne Magnetfunktion einbauen. Damit zwei Komponenten als eine gehandelt wird. (Darüber hab ich mir bisher aber noch keine Gedanken gemacht, war bisher nur Ideenspielrei)

Komplette Neustrukturierung erfolgt jetzt demnächst wohl noch, wenn ich alle Funktionen durchgeplant habe!


Also eine konkrete Frage habe ich schon noch:
Es funktioniert jetzt zwar über meine GlassPane methode, aber eigentlich ist mir die irgendwie unsympatisch.
Also wenn ich jetzt alles richtig verstanden habe, meinst du ich soll das Verschiebungsproblem (Anker und SourceComponent gleichzeitig aktualisieren) über ein LayoutManager lösen?
Wenn du das so meinst, dann versteh ichs nich so ganz. Ich müsste ja dann in nem Geerbten Layout "layoutContainer(Container target)" überschreiben oder? aber was ändert das an dem ursprünglichen Problem?


Wenn nein, was genau bringen mir dann eigene Layouts, und gibts dann noch ne andere Lösung (bzgl. der aktualisierung)?


----------



## Beni (17. Nov 2011)

Mit dieser Lösung wirst du "layoutContainer(Container target)" und andere Methoden komplett neu schreiben müssen. Das ist durchaus ein nicht zu unterschätzender Aufwand. Um nicht zu sagen, du wirst elend viel Zeit dafür benötigen. ;-)  Vielleicht ist es auch nicht die beste Lösung und du findest was viel besseres und einfacheres.

Wenn du selbst einen LayoutManager schreibst, kannst du ihn intelligenter als ein Standard-LayoutManager machen. z.B.:

- Du kannst deinen LayoutManager fragen, wo eine Component hinkommt wenn man sie an einer bestimmten Stelle einfügt. Damit kannst du einer Vorschau zeichnen, ohne wirklich Components hin und her zu schieben.
- Du kannst deinen LayoutManager fragen, wo überall eine Component auftauchen könnte. Damit kannst du einfach rausfinden welche Position am nächsten zu der Maus ist. Damit hast du mehr Möglichkeiten als nur den "componentZ" - Wert. Wiederum ist das alles möglich ohne Components hin und her zu schieben.
- Du kannst soweit gehen, dass deine LayoutManager Animationen ausführen. Statt das alles rumspringt gleiten die Componenten elegant in ihre neue Position.

Mit diesem Ansatz brauchst du am Ende diese Anker-component nicht mehr. Du kannst dann die Vorschaut direkt auf dem GlassPane zeichnen (und sparst damit "validate" Aufrufe, in grossen Mengen sind die nämlich nicht so toll).


----------



## Sunchezz (17. Nov 2011)

Ok, das klingt bis auf den Zeitfaktor ziemlich gut!
Werde mich damit auf jeden Fall mal etwas näher beschäftigen.

Auch wenn mir die layoutMethode des Managers mir grad nen bischen Angst macht auf den ersten Blick^^
Wird wohl aber nur der erste Blick sein, hoffe ich 


Aber anscheinend muss ich wohl auf jeden Fall über das GlassPane gehen oder?
Gibts echt keinen Weg das auszulassen?
Das was ich aufs Glasspane zeichne, könnte ich doch auch direkt aufs Panel zeichnen oder?


----------



## Beni (17. Nov 2011)

Das GlassPane garantiert dir halt einfach, dass alles was du zeichnest niemals verdeckt sein wird. Wenn du direkt auf das Panel zeichnest, dann ist deine Zeichnung unter den anderen Componenten versteckt. Das ist ok wenn du auf einer leeren Fläche zeichnest, aber in deinem Fall willst du das vielleicht nicht immer.


----------



## Sunchezz (17. Nov 2011)

Gibt es eine Möglichkeit so etwas ähnliches wie das Glasspane zu erstellen?
Sozusagen ein JPanel was unsichtbar über dem anderen liegt, nur das es nicht als glassPane gesetzt wird?

Hab die Angst das es zu einem unwarscheinlichen Fall kommt, wo das GlassPane zu anderen Zwecken gebraucht wird...
Für das jetzige Projekt kann ich das zwar auschlißen, aber ich möchte mich ja doch so flexibel halten, das ich es eventuell irgendwann anders mal anders benutzen kann^^


----------



## Beni (17. Nov 2011)

Sunchezz hat gesagt.:


> Gibt es eine Möglichkeit so etwas ähnliches wie das Glasspane zu erstellen?
> Sozusagen ein JPanel was unsichtbar über dem anderen liegt, nur das es nicht als glassPane gesetzt wird?



Das kannst du problemlos machen. Solange dein panel mit den Componenten und das unsichtbare JPanel denselben Container als Elter haben, wird das funktionieren. Musst nur noch dafuer sorgen, dass das unsichtbare JPanel auch wirklich ueber dem anderen panel gezeichnet wird (dazu musst du mit LayoutManagern rumspielen, und das component-z beachten).


----------



## Sunchezz (17. Nov 2011)

Mit anderen Worten ich müsste das ParentLayout temporär auf null setzen, das "invisiblePane" auf die selbe location setzen, und dann die paint reihenfolge ändern...

Werds morgen mal auf dem Weg probieren!
Danke!


----------



## Sunchezz (18. Nov 2011)

Ok, habs auf diesem Weg Porbiert...
Es funktioniert auch, nur bin ich jetzt beim alten Problem das die PrewiewComponent flackert^^

Habe jetzt die eine Klasse erstellt die von JPanel erbt (PrewiewPanel), die wird einmal im Konstruktor des Listeners initialisiert.
1. mousePressed() ruft showPreview auf und übergibt die Komponente, wie ihr seht wird hier auch das previewPanel  auf das Parent gesetzt.
2. mouseReleased() ruft hidePreview auf.
3. mouseDragged () ruft getPreviewComponent() auf und setzt die neuen Bounds.


```
private void prepareParent() {
		this.parent = containerToOverlay.getParent();
		this.previousParentLayout = parent.getLayout();
		Point originalPosition = containerToOverlay.getLocation();
		Dimension originalSize = containerToOverlay.getSize();
		parent.setLayout(null);		
		this.setLocation(originalPosition);
		this.setSize(originalSize);
		parent.add(this);
		parent.validate();
		parent.repaint();
		this.validate();
	}

	private void showPrewiew(JComponent c, Border b) {
		this.prepareParent();
		this.setPrewiewComponent(c);
		prewiewComponent.setBorder(b);
		prewiewComponent.getParent().remove(getPrewiewComponent());
		this.setVisible(true);
		this.add(prewiewComponent);
	}

	public void hidePrewiew() {
		this.setVisible(false);
		this.remove(getPrewiewComponent());
		this.validate();
	}
```

Ist die Ursache für das Flackern irgendwo hier zu finden?


----------



## Beni (19. Nov 2011)

Wie soll dir blos jemand helfen, wenn du nichtmal deine ganze Preview-Klasse zeigst. Ohne Code kann man nur rumraten...

Vielleicht stimmt die Position deiner Component nicht? Springt die Position die ganze Zeit hin und her? Allenfalls beachtest du bei der Position nicht, dass die Component sich bewegt und deshalb der Punkt 0/0 an einer anderen Stelle ist.


----------



## Sunchezz (19. Nov 2011)

Also genau genommen hat sich nicht viel geändert, ich habe nur angefangen im Code aufzuräumen, und habe die GlassPane vorschau extrahiert...
Da hat noch alles funktioniert!
Dann hab ich die GlassPane Prewiew kopiert und modifiziert, um das mit dem überlappenden JPanel zu versuchen.
Und nein, wenn ich das richtig sehe, Springt es nicht hin und her, es flackert nur bei der Mausbewegung. Bleibt die Maus unbewegt, aber die Taste noch gehalten, verschwindet es komplett, und taucht nur zur nächsten Bewegung wieder auf!



Dann hier mal beide Klassen und der Listener:
(Zur besseren Übersicht wurden überall selbsterklärende Methoden wie getter usw. entfernt!)


Die GlassPane Vorschau:

```
package tests.gui;

import java.awt.Color;

public class PrewiewPanel extends JPanel{
	private JFrame frame = (JFrame) JFrame.getFrames()[0];
	private JComponent prewiewComponent;
	private JPanel oldGlassPane;
	private Border prewiewBorder;
	
	public PrewiewPanel() {
		setBackground(new Color(255, 255, 255, 0));
		setOpaque(false);
		setVisible(true);
		setLayout(null);
	}
	
	public void showPrewiew(JComponent c) {
		this.showPrewiew(c, prewiewBorder);
	}
	
	public void showPrewiew(JComponent c, Border b) {
		this.oldGlassPane = (JPanel)frame.getGlassPane();
		this.setPrewiewComponent(c);
		getPrewiewComponent().setBorder(b);
		getPrewiewComponent().getParent().remove(getPrewiewComponent());
		setVisible(true);
		add(getPrewiewComponent());

		frame.setGlassPane(this);
		frame.getGlassPane().setVisible(true);
	}

	public void hidePrewiew() {
		setVisible(false);
		remove(getPrewiewComponent());
		frame.setGlassPane(oldGlassPane);
		repaint();
		validate();
	}
}
```

Die JPanel Vorschau:

```
package tests.gui;

public class OverlayerPanel extends JPanel {
	private JFrame frame = (JFrame) JFrame.getFrames()[0];
	private JComponent prewiewComponent;
	private Container parent;
	private LayoutManager previousParentLayout;
	private JPanel containerToOverlay;
	private Border prewiewBorder;

	public OverlayerPanel(JPanel original) {
		this.parent = original.getParent();
		this.containerToOverlay = original;
		this.setBackground(new Color(55, 255, 255, 0));
		this.setBackground(Color.RED);
		this.setOpaque(true);
		this.setVisible(true);
		this.setLayout(null);
	}

	private void prepareParent() {
		this.parent = containerToOverlay.getParent();
		this.previousParentLayout = parent.getLayout();
		Point originalPosition = containerToOverlay.getLocation();
		Dimension originalSize = containerToOverlay.getSize();
		parent.setLayout(null);		
		this.setLocation(originalPosition);
		this.setSize(originalSize);				
		//((JPanel)parent).setBorder(prewiewBorder);
		this.setBorder(prewiewBorder);
		parent.add(this);
		parent.validate();
		parent.repaint();
		this.validate();
	}

	public void showPrewiew(JComponent c, Border b) {
		this.prewiew(c, b);
	}

	public void showPrewiew(JComponent c) {
		this.prewiew(c, prewiewBorder);
	}

	private void prewiew(JComponent c, Border b) {
		this.prepareParent();
		this.setPrewiewComponent(c);
		this.prewiewComponent.setOpaque(true);
		prewiewComponent.setBorder(b);
		prewiewComponent.getParent().remove(getPrewiewComponent());
		this.setVisible(true);
		this.add(prewiewComponent);
	}

	public void hidePrewiew() {
		try {
			parent.setLayout(previousParentLayout);
		} catch (NullPointerException e) {
			parent.setLayout(null);
		}
		parent.validate();
		this.setVisible(false);		
		this.remove(getPrewiewComponent());
		this.validate();
	}
}
```

Der Listener:

```
package tests.gui;

public class ComponentMover extends MouseInputAdapter {
	private JComponent selectedComponent; // the actual selected Component to
											// move
	private JPanel container; // the parentContainer of
								// selectedComponent
	private int actualPos; // ZOrderPosition of the selectedComponent
	private JPanel anchor; // Visual Anchor for Possible new Position
	private int newZOrderPosition = -1; // the possible new ZOrderPosition
	private int lastAnchorPosition;
	private boolean anchorIsVisible;
	private Border anchorBorder;
	private Border editBorder;
	private Border selectionBorder;
	private boolean dragging;
	private boolean movingAllowed;
	private Component layeredComponent;
	private Point convertedPoint;
	private OverlayerPanel viewer;

	public ComponentMover(JPanel container, Border edit) {
		dragging = false;
		this.container = container;
		anchorBorder = BorderFactory.createLineBorder(Color.RED, 4);
		selectionBorder = BorderFactory.createLineBorder(Color.RED, 2);
		editBorder = edit;
		viewer = new OverlayerPanel(container);
		viewer.setPrewiewBorder(selectionBorder);
	}	

	public void mousePressed(MouseEvent e) {
		selectedComponent = (JComponent) e.getSource();
		convertPoint(e);
		if (selectedComponent != null) {
			//offset = e.getPoint();
			dragging = true;
			if (container.getLayout() != null) {
				actualPos = container.getComponentZOrder(selectedComponent);
				lastAnchorPosition = container
						.getComponentZOrder(selectedComponent);
				
				selectedComponent.setBounds(calculateRect(convertedPoint));
				viewer.showPrewiew(selectedComponent);
				container.validate();
			}
			viewer.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
		}		
		super.mousePressed(e);
	}

	public void mouseReleased(MouseEvent e) {
		dragging = false;
		if (container.getLayout() instanceof FlowLayout) {
			if (newZOrderPosition != -1) {
				container.add(selectedComponent);
				container.setComponentZOrder(selectedComponent,
						newZOrderPosition);
			}
			if (anchorIsVisible) {
				container.remove(anchor);
				anchorIsVisible = false;
			}
			viewer.hidePrewiew();
		}
		container.add(selectedComponent,
				newZOrderPosition == -1 ? lastAnchorPosition
						: newZOrderPosition);
		selectedComponent.setBorder(editBorder);
		viewer.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
		container.validate();
		actualPos = -1;
		newZOrderPosition = -1;
		super.mouseReleased(e);
	}

	public void mouseDragged(MouseEvent e) {
		convertPoint(e);
		if (dragging) {
			if (container.getLayout() == null) {
				selectedComponent.setBounds(calculateRect(e.getPoint()));
			}
			if (container.getLayout() != null) {
				Rectangle r = calculateRect(e.getPoint());
				viewer.getPrewiewComponent().setBounds(r);
				if (anchorIsVisible) {
					removeAnchor();
				}
				int newPosition = this.getNewAnchorPosition(e);
				if (newPosition != -1) {
					showAnchorAt(newPosition);
				}
			}
		}
		super.mouseDragged(e);
	}
	
	private void convertPoint(MouseEvent e) {
		convertedPoint = SwingUtilities.convertPoint(container,
				e.getPoint(), viewer);
	}

	private Rectangle calculateRect(Point p) {
		Rectangle r = selectedComponent.getBounds();
		r.x += p.getX() + 5;
		r.y += p.getY() + 5;
		return r;
	}
}
```


Wenn ich jetzt den Objekttyp von "viewer" wieder auf das alte PrewiewPanel umstelle, funktionierts wieder blendend (GlassPane).
Aber irgendwas scheint der an meinem neuen Panel nicht zu mögen.

Hoffe ich hab jetzt nichts vergessen!

Grüße und ein schönes WE
Sunchezz


----------



## Beni (20. Nov 2011)

Wenn du auf deinem neuen Glass-Pane einfach irgendwas zeichnest, z.B. ein paar Rechtecke, flackern die auch? Wenn ja, dann ist vielleicht das Glass-Pane selbst falsch auf seinem Vater (falsche z-Order?). Wenn nein, dann ist irgendwas mit der Vorschau-Component falsch.


----------



## Sunchezz (20. Nov 2011)

Also mal nebenbei, habe mich mit Graphics bisher eigentlich noch überhaupt nicht näher beschäftigt.

Allerdings glaube ich das innheralb der methode showPrewiew():

```
this.getGraphics().drawRect(einRect);
```
ja eigentlich ausreichen sollte um was zu zeichnen oder?
Aber auf jeden Fall passiert da auch nichts!
wird nichts angezeigt...

Wär dankbar für ne Idee, probiere aber auch selbst noch nen bischen weiter rum!


----------



## Beni (20. Nov 2011)

Ne, du musst "paintComponent" überschreiben. Vergiss, dass die Methode "getGraphics" existiert, benutze sie niemals, es ist immer falsch.


----------



## Sunchezz (20. Nov 2011)

also paintComponent hat mich jetzt auch ich soviel weiter gebracht.
das Rechteckt wurde gezeichnet, aber sobald sich nach dem Drag die Maus bewegt verschwindet es wieder!

ich habe die Vermutung das irgendwo nen unangebrachtes repaint oder sowas zuviel ist.
aber ich komm absolut nicht dahinter.
Habe jetzt in prepareParent nen bischen mit der Z-Order der beiden panels die sich überlagern rumgespielt (und auch noch mit einigen anderen sachen), und habe es zumindest geschafft das flackern zu minimieren.

Wo sich nun aber der rest versteckt ist mir absolut nen Rätsel.
Zumal ich die minimierung mehr durch rumprobieren als durch wissen geschafft habe.

Das interessante ist, die Bewegungsschnelligkeit der Maus spielt irgendeine Rolle.
Wird sie nur enorm langsam bewegt, flackert es überhaupt nicht!
Ich könnte deshalb vermuten, das zwischen den Bewegungen zuviele Berechnungen angestellt werden, bis es zum Repaint kommt, allerdings müsste ja dann eher die Komponente "springen", und es würde auf dem Originalglasspane genauso aussehen, tut es aber nicht.


Ich hoffe ich nerve nicht langsam -.-


----------



## Sunchezz (21. Nov 2011)

Soo... hab das jetzt anders gelöst 
Zauberformel lautet JLayeredPane!

Jetzt also das Vorschaupanel bei bedarf in den Vordergrund geschoben.
Das hat das Flackern im Prinzip eigentlich gelöst, dachte ich jedenfalls -.-

Jetzt kommt ein neuer Flackerfaktor.
Da Container auf dem eigentlich Panel ebenfalls verschoben werden können sollen, musste ich ja diverse Subkomponenten deaktivieren. Da man zum Beispiel eine JTable in einem ScrollPane immer noch anwählen kann, obwohl die ScrollPane deaktiviert ist, daher musste man die ScrollPane direkt greifen, oder irgendwo am Rand. Das ist natürlich nicht wünschenswert.
Meine erste Idee war, rekursiv zu prüfen ob die jeweilge Komponente ein Container is, bis mir schnell aufgefallen is, das ja alle JComponenten von Container erben -.-. Nächster ansatz war, bei speziellen Containern (JPanel, JScrollPane etc.) weiter rekursiv die subKomponenten zu deaktivieren. Hat aber auch irgendwann gehakt, da er versuch hat irgendwelche defaulteditor in einer JTable oder so zu deaktivieren. Da ich nich jede eventuelle Möglichkeit abfragen will, hab ich mir gedacht, man kann ja zur bearbeitungszeit aus der Komponente nen Bild machen, und das dann Verschieben.
Sieht klasse aus, und funktioniert weitestgehend bis auf folgenden Fehler:
Wird das Bild nicht angefasst, sondern eine andere Komponente und schwebt man nun beim ziehen über das Bild, fangen beide Komponenten an sich abwechselnd zu überlappen (Flackert böse, bleibt man jedoch stehen, ist die zu ziehende komponente mal unter, mal über dem Bild).
Is mir irgendwie nen Rätsel, vielleicht war das ja von anfang an das Flackerproblem???

Hier mal die Klasse für ein Bild (ich benutze es als normales JComponent)


```
public class Picture extends JComponent {

	private static final long serialVersionUID = 1992007953073696720L;
	private Image img;
	private int oldComponentPosition = -1;
	
	private String identifier;
	
	public Picture(Component c) {
		this.img = getComponentPicture(c);
		oldComponentPosition = c.getParent().getComponentZOrder(c);
	}
	
	public Picture(Image img) {
		this.img = img;
		int w = img.getWidth(this);
		int h = img.getHeight(this);
		setPreferredSize(new Dimension(w, h));
	}	

	protected void paintComponent(Graphics g) {
		g.drawImage(img, 0, 0, this);
	}
	
	protected Image getComponentPicture(Component c) {
		BufferedImage img = new BufferedImage(c.getWidth(),
				c.getHeight(), BufferedImage.TYPE_INT_RGB);
		img.flush();
		c.paint(img.getGraphics());
		return img;
	}
	
	public Image getImg() {
		return img;
	}

	public void setImg(Image img) {
		this.img = img;
	}
}
```

Gibts da was was ich auf anhieb nich kapiere?
Eigentlich sollte es sich doch wie ein normales JComponent verhalten oder?
BTW: mit Bildern, paintComponent, Graphics etc. hab ich mich noch nie stark auseinander Gesetzt, vielleicht liegts daran


----------

