# Scrollen per mouseDragged (JScrollPane)



## Guest (29. Sep 2008)

Servus aus Coburg, 

gibt es in java Swing eine Möglichkeit per Drag & Drop das Scrollen in einem JScrollPane nachzubilden. Angenommen es wird ein langes Dokument in einem Scrollpane angezeigt, dann soll das vertikale und horizontale Scrollen eben nicht über den herkömmlichen Weg möglich sein, sondern auch per Maus, dh. ich befinde mich mit der Maus an einer Stelle im Dokument und kann bei gedrückter Maustaste dann genauso "scrollen". Ein paar Tips wären hilfreich. Danke im voraus!


----------



## 0001001 (29. Sep 2008)

Ja das sollte funktionieren.

Du brauchst einen MouseListener, der dir die aktuelle Position der Maus liefert. Dann prüfst du die Größe des Fensters. Falls die Maus gedrückt ist und sich nahe am Rand des Fensters befindet, setzt du die entspechende(n) Scrollbars auf einen bestimmten Wert, so dass das Dokument wieder mehr in die Mitte rückt.


----------



## Wildcard (29. Sep 2008)

http://java.sun.com/javase/6/docs/api/javax/swing/JComponent.html#setAutoscrolls(boolean)


----------



## Gelöschtes Mitglied 9001 (14. Okt 2008)

Ich vermute, dem Fragesteller ging es nicht darum, daß der Inhalt automatisch scrollt, wenn man mit der Maus an den Rand kommt. Ich glaube, gesucht ist eine Lösung dafür, wie man den Inhalt mit der Maus anfassen und dann verschieben kann. Ich selbst suche auch gerade danach.

Idealerweise sollte die scrollbare Komponente von der Realisierung unbehelligt bleiben. Das Problem ist nur: wo setze ich einen MouseListener richtig an, der zunächst alle Mausereignisse abfängt und prüft, ob diese das Verschieben des Inhaltes initiieren könnten. Falls sie das tun, so dürften sie nicht auch noch von der Komponente bearbeitet werden. Falls sie das nicht tun, müßten sie an die Komponente weitergereicht werden.

Bislang habe ich MouseListener bei der JScrollPane, beim JViewport und bei der Komponente angemeldet, bei JViewport und JScrollPane auch mal processMouseEvents überschrieben. Hat bis lang alles noch nicht zum Ziel geführt: in jedem Fall reagierte die Komponente auf alle Mausereignisse. Und nur, wenn ich den MouseListener bei der Komponente direkt angemeldet habe, konnte ich auch wirklich an alle Mausereignisse kommen.

Wer hat dazu noch Ideen?


----------



## Gelöschtes Mitglied 9001 (10. Feb 2009)

Hat keiner eine Idee?


----------



## Ebenius (10. Feb 2009)

Hier meine JXScrollPane. Unterstützt view dragging, standardmäßig auf dem rechten mouse button (kann man ändern mit setViewDragModifierMask(int)). Sicher kannst Du darauf aufbauen. Die MouseEvents werden nur verarbeitet, wenn keines der Kinder den Event konsumiert. Diesen Weg halte ich für sinnvoller, als den Kindern den Event zu verstecken, wenn ihn sich die JScrollPane krallt.

ebenius.widget.JXScrollPane

Ebenius


----------



## Gelöschtes Mitglied 9001 (10. Feb 2009)

Danke Ebenius!
Ich habe Deinen Code probiert, funktionierte bei mir leider nicht. Die Mouse-Events kamen gar nicht erst bei processMouseEvents an. Hinzu kommt, daß meine Anwendung für einen Touchscreen bestimmt ist. Dort kann ich nicht zwischen rechter und linker Maustaste unterscheiden. Trotzdem gibt es in der Komponente, die gescrollt werden soll, Elemente, die angeklickt und auch per Drag&Drop verschoben werden können müssen. Ich habe, noch bevor ich Deinen Post las, mich heute Vormittag hingesetzt und weitergebastelt. Folgender Code ist dabei herausgekommen.
Die durchaus kontrovers diskutierbare Strategie ist, dass über den Viewport eine Pane gelegt wird, die zunächst alle MouseEvents abfängt. Drückt der Benutzer die Maustaste und beginnt innerhalb einer zehntel Sekunde den Dragvorgang, interpretiert die Pane dies als Anweisung zum Scrollen. In allen anderen Fällen werden die MouseEvents einfach weitergereicht.


```
/**
 * ScrollPane, bei welcher der Inhalt durch Anfassen mit der Maus verschoben werden kann.
 *
 */
public class DraggableScrollPane extends JScrollPane {
	
	/**
	 * Diese Komponente wird ueber den Viewport gelegt und faengt die Mausereignisse zunaechst ab.
	 * Koennen die Mausereignisse interpretiert werden als Anweisung, den Inhalt zu verschieben, wird
	 * selbiges getan. Ansonsten werden die Ereignisse weiter an den Inhalt gereicht.
	 *
	 */
	class MyGlassPane extends JComponent implements MouseListener, MouseMotionListener, MouseWheelListener {
		
		final static int MOUSEPRESSDELAY = 100; // ein Mausdruck wird nach soviel Millisekunden an den Inhalt weitergereicht, 
												// sofern nichts anderes passiert 
		
		protected javax.swing.Timer MousePressTimer;
		protected Point pressedPoint;           // nach jedem mousePressed stehen hier die Koordinaten drin
		protected Point pressedViewPos;         // und hier, bei welcher Position der Inhalt stand.
		protected boolean dragging = false;     // ist true, wenn der Benutzer den Inhalt gerade verschiebt 
		
		public MyGlassPane() {
			super();
			addMouseListener(this);
			addMouseMotionListener(this);
			addMouseWheelListener(this);
			setFocusable(false);
		}

		boolean MouseLocked = false;
		@Override
		/**
		 * Wenn MouseLocked true ist, dann wird hier false zurueckgegeben und so sichergestellt, 
		 * dass die GlassPane bei der Zuordnung von MouseEvents nicht beruecksichtigt wird
		 */
		public boolean contains(int x, int y) {
			return !MouseLocked && super.contains(x,y);
		}

		/**
		 * Uebergibt ein MouseWheelEvent an die JScrollPane.
		 */
		protected void delegateMouseEvent(MouseWheelEvent e) {
			getParent().dispatchEvent(e);
		}
		
		/**
		 * Uebergibt ein MouseEvent an darunterliegende Komponenten
		 */
		protected void delegateMouseEvent(MouseEvent e) {				
			 Container container = getParent();
			 Point containerPoint = SwingUtilities.convertPoint(this, e.getPoint(), container);
			 
			 MouseLocked = true;  // damit nicht die GlassPane gefunden wird
			 Component C = container.findComponentAt(containerPoint.x, containerPoint.y); // jetzt die Komponente suchen, die den Punkt beinhaltet
			 MouseLocked = false; // jetzt wieder anschalten
			 
			 if (C != null && C != this) {   // C != this zur Sicherheit
				 Point destP = SwingUtilities.convertPoint(this, containerPoint, C);  // Punkt konvertieren in Koordinaten der Komponente
				 MouseEvent newe = new MouseEvent(C, e.getID(), e.getWhen(), e.getModifiers()+e.getModifiersEx(),      
	                     destP.x, destP.y, e.getClickCount(), e.isPopupTrigger(), e.getButton());  // neues MouseEvent erzeugen
				 
	             C.dispatchEvent(newe);   // und der Komponente uebergeben
			 }
		}

		public void startTimer(final MouseEvent EventToDelegate) {
			MousePressTimer = new Timer(MOUSEPRESSDELAY, new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					delegateMouseEvent(EventToDelegate);
				}
			});
			MousePressTimer.setRepeats(false);
			MousePressTimer.start();
		}
		
		public void stopTimer() {
			if (MousePressTimer != null && MousePressTimer.isRunning()) {
				MousePressTimer.stop();
			}
		}
		
		public boolean isTimerRunning() {
			return MousePressTimer != null && MousePressTimer.isRunning();
		}
		
		public void scrollTo(Point p) {
			JViewport v = ((JScrollPane) getParent()).getViewport();
			Dimension ext = v.getExtentSize();
			Dimension D = v.getView().getSize();
			
			if (p.x < 0) p.x = 0;
			if (p.y < 0) p.y = 0;
			if (p.x > D.width-ext.width) p.x = D.width-ext.width;
			if (p.y > D.height-ext.height) p.y = D.height-ext.height;
			
			v.setViewPosition(p);
		}
		
		public Point getViewPos() {
			return ((JScrollPane) getParent()).getViewport().getViewPosition();
		}

		
		public void mouseClicked(MouseEvent e) {
			delegateMouseEvent(e);
		}

		public void mouseEntered(MouseEvent e) {
			delegateMouseEvent(e);
			
		}

		public void mouseExited(MouseEvent e) {
			delegateMouseEvent(e);
			
		}

		public void mousePressed(MouseEvent e) {
			pressedPoint = e.getPoint();
			pressedViewPos = getViewPos();
			
			startTimer(e);
		}
		
		public void mouseReleased(MouseEvent e) {
			if (dragging) {					// lief gerade ein Dragging-Vorgang
				dragging = false;           // dann beenden
			} else {
				delegateMouseEvent(e);      // ansonsten das Event weiterreichen
			} 
		}

		public void mouseDragged(MouseEvent e) {
			if (isTimerRunning()) {         // ist die Zeit noch nicht abgelaufen
				stopTimer();                // dann Timer abbrechen
				dragging = true;            // und denn Dragging-Vorgang starten
			}
			
			if (dragging) {                // wenn Dragging-Vorgang laeuft
				Point sp = new Point(pressedPoint.x-e.getX()+pressedViewPos.x,pressedPoint.y-e.getY()+pressedViewPos.y);
				scrollTo(sp);              // an entsprechende Stelle scrollen
			} else {
				delegateMouseEvent(e);     // ansonsten Event weiterreichen
			}
		}

		public void mouseMoved(MouseEvent e) {
			delegateMouseEvent(e);
		}

		public void mouseWheelMoved(MouseWheelEvent e) {
			delegateMouseEvent(e);
		}		
	}
	
	/**
	 * Dieses Layout erweitert das normale ScrollPaneLayout und sorgt dafuer,
	 * dass meine GlassPane ueber den Viewport gelegt wird.
	 */
	class MyScrollPaneLayout extends ScrollPaneLayout {
		MyGlassPane GP;
		final static String GLASSPANE = "GlassPane";
		
		@Override
		public void addLayoutComponent(String s, Component c) {
			if (s.equals(GLASSPANE)) {
				GP = (MyGlassPane) addSingletonComponent(GP, c);
			} else {
				super.addLayoutComponent(s, c);
			}
		}

		@Override
		public void layoutContainer(Container parent) {
			super.layoutContainer(parent);
			JViewport v = getViewport();
			if (v != null && GP != null) {
				GP.setBounds(v.getBounds());            // passgenaue Position und Groesse setzen
				parent.setComponentZOrder(GP, 0);       // und sicherstellen, dass die GlassPane obendrauf liegt
			}
		}
	}
	
	/********** DraggableScrollPane-Konstruktoren ********************/
	public DraggableScrollPane() {
		super();
		addmythings();
	}

	public DraggableScrollPane(Component view) {
		super(view);
		addmythings();
	}

	public DraggableScrollPane(int vsbPolicy, int hsbPolicy) {
		super(vsbPolicy, hsbPolicy);
		addmythings();
	}

	public DraggableScrollPane(Component view, int vsbPolicy, int hsbPolicy) {
		super(view, vsbPolicy, hsbPolicy);
		addmythings();
	}
	
	/**
	 * Setzt mein eigenes Layout ein und fuegt meine GlassPane hinzu
	 */
	protected void addmythings() {
		setLayout(new MyScrollPaneLayout());
		add(new MyGlassPane(), MyScrollPaneLayout.GLASSPANE);
		doLayout();
	}
}
```


----------



## Ebenius (10. Feb 2009)

Dann hab ich mal meine JXScrollPane erweitert. Jetzt werden auch die Events der Kinder genommen die die Events schlucken wollen.  Ohne GlassPane. Vielleicht gefällt Dir ja der Ansatz...

ebenius.widget.JXScrollPane

Viel Spaß! Ebenius


----------



## minggi (18. Jan 2012)

Der Link verweist auf nichts...


----------

