Swing/ZoomAndPanPanel のバックアップ差分(No.2)
- バックアップ一覧
- 現在との差分 を表示
- 現在との差分 - Visual を表示
- ソース を表示
- バックアップ を表示
- Swing/ZoomAndPanPanel へ行く。
- 1 (2015-06-23 (火) 20:29:24)
- 2 (2015-06-24 (水) 17:11:50)
- 3 (2015-06-30 (火) 18:28:02)
- 4 (2016-01-13 (水) 14:58:14)
- 5 (2016-01-27 (水) 15:19:42)
- 6 (2016-05-24 (火) 19:52:57)
- 7 (2016-11-18 (金) 14:47:43)
- 8 (2017-04-04 (火) 14:13:45)
- 9 (2017-06-20 (火) 13:56:43)
- 10 (2018-02-24 (土) 19:51:30)
- 11 (2018-06-05 (火) 13:41:15)
- 12 (2018-12-07 (金) 15:22:07)
- 13 (2018-12-21 (金) 14:12:04)
- 14 (2020-11-17 (火) 12:52:52)
- 15 (2022-12-02 (金) 12:08:38)
- 追加された行はこの色です。
- 削除された行はこの色です。
--- title: JScrollPane内に配置したJPanelをマウスで拡大、縮小、移動する tags: [JScrollPane, JPanel, AffineTransform, Image, WheelListener] author: aterai pubdate: 2015-06-22T10:03:20+09:00 description: JScrollPane内に配置したJPanelを、マウスホイールを使った拡大縮小と、スクロールバーを使った表示領域の移動が可能になるように設定します。 --- * 概要 [#yfd80c64] `JScrollPane`内に配置した`JPanel`を、マウスホイールを使った拡大縮小と、スクロールバーを使った表示領域の移動が可能になるように設定します。 #download(https://lh3.googleusercontent.com/-Um9j8O0t3Kg/VYdMPIUOfwI/AAAAAAAAN7A/LAJ5KRiDdp0/s800/ZoomAndPanPanel.png) * サンプルコード [#cf006c68] #code(link){{ class ZoomAndPanePanel extends JPanel { private final AffineTransform coordTransform = new AffineTransform(); private final transient Image img; private final Rectangle imgrect; private transient ZoomAndPanHandler handler; private transient ZoomHandler handler; private transient DragScrollListener listener; public ZoomAndPanePanel(Image img) { super(); this.img = img; this.imgrect = new Rectangle(img.getWidth(null), img.getHeight(null)); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2 = (Graphics2D) g.create(); g2.setPaint(new Color(0x55FF0000, true)); Rectangle r = new Rectangle(500, 140, 150, 150); //AffineTransform at = g2.getTransform(); //at.concatenate(coordTransform); //g2.setTransform(at); //g2.drawImage(img, 0, 0, this); //g2.fill(new Rectangle(500, 140, 150, 150)); //g2.fill(r); //g2.drawRenderedImage((java.awt.image.RenderedImage) img, coordTransform); g2.drawImage(img, coordTransform, this); g2.fill(coordTransform.createTransformedShape(new Rectangle(500, 140, 150, 150))); g2.fill(coordTransform.createTransformedShape(r)); //XXX //g2.setTransform(coordTransform); //g2.drawImage(img, 0, 0, this); g2.dispose(); } @Override public Dimension getPreferredSize() { Rectangle r = coordTransform.createTransformedShape(imgrect).getBounds(); return new Dimension(r.width, r.height); } @Override public void updateUI() { removeMouseListener(handler); removeMouseMotionListener(handler); removeMouseListener(listener); removeMouseMotionListener(listener); removeMouseWheelListener(handler); super.updateUI(); handler = new ZoomAndPanHandler(); addMouseListener(handler); addMouseMotionListener(handler); listener = new DragScrollListener(); addMouseListener(listener); addMouseMotionListener(listener); handler = new ZoomHandler(); addMouseWheelListener(handler); } protected class ZoomAndPanHandler extends MouseAdapter { protected class ZoomHandler extends MouseAdapter { private static final int MIN_ZOOM = -9; private static final int MAX_ZOOM = 16; private static final int EXTENT = 1; private final BoundedRangeModel zoomRange = new DefaultBoundedRangeModel( 0, EXTENT, MIN_ZOOM, MAX_ZOOM + EXTENT); private final Point dragStartPoint = new Point(); @Override public void mousePressed(MouseEvent e) { dragStartPoint.setLocation(e.getPoint()); } @Override public void mouseDragged(MouseEvent e) { Point dragEndPoint = e.getPoint(); Point dragStart = inversedPoint(dragStartPoint); Point dragEnd = inversedPoint(dragEndPoint); coordTransform.translate(dragEnd.x - dragStart.x, dragEnd.y - dragStart.y); dragStartPoint.setLocation(dragEndPoint); e.getComponent().repaint(); } @Override public void mouseWheelMoved(MouseWheelEvent e) { int dir = e.getWheelRotation(); int z = zoomRange.getValue(); zoomRange.setValue(z + EXTENT * (dir > 0 ? -1 : 1)); if (z != zoomRange.getValue()) { Container c = SwingUtilities.getAncestorOfClass( JViewport.class, e.getComponent()); if (c instanceof JViewport) { Rectangle r = ((JViewport) c).getBounds(); Point p = new Point(r.x + r.width / 2, r.y + r.height / 2); Point p1 = inversedPoint(p); double s = 1d + zoomRange.getValue() * .1; coordTransform.setToScale(s, s); Point p2 = inversedPoint(p); coordTransform.translate(p2.getX() - p1.getX(), p2.getY() - p1.getY()); Component c = e.getComponent(); Container p = SwingUtilities.getAncestorOfClass(JViewport.class, c); if (p instanceof JViewport) { JViewport vport = (JViewport) p; Rectangle ovr = vport.getViewRect(); double s = dir > 0 ? 1d / 1.2 : 1.2; coordTransform.scale(s, s); //double s = 1d + zoomRange.getValue() * .1; //coordTransform.setToScale(s, s); AffineTransform at = AffineTransform.getScaleInstance(s, s); Rectangle nvr = at.createTransformedShape(ovr).getBounds(); Point vp = nvr.getLocation(); vp.translate((nvr.width - ovr.width) / 2, (nvr.height - ovr.height) / 2); vport.setViewPosition(vp); c.revalidate(); c.repaint(); } } } //https://community.oracle.com/thread/1263955 //How to implement Zoom & Pan in Java using Graphics2D private Point inversedPoint(Point p1) { Point p2 = new Point(); try { AffineTransform inverse = coordTransform.createInverse(); inverse.transform(p1, p2); } catch (NoninvertibleTransformException ex) { ex.printStackTrace(); } return p2; } public AffineTransform getCoordTransform() { return coordTransform; } } } }} * 解説 [#w2dd6ea8] 上記のサンプルでは、`JPanel#getPreferredSize()`を拡大後の画像サイズを返すようにオーバーライドすることで、画像が`JViewport`より大きくなる場合は、スクロールバーが表示されるように設定しています。 ---- ズーム自体は、[[JPanelに表示した画像のズームとスクロール>Swing/ZoomingAndPanning]]で使用しているものとほぼ同じ`MouseWheelListener`を設定して実行していますが、画像を描画している`JPanel`を`JScrollPane`内に設定してスクロールバーでのスクロールを可能にしているため、`JPanel#paintComponent(...)`内での`AffineTransform`の使用方法を変更しています。 ズーム自体は、[[JPanelに表示した画像のズームとスクロール>Swing/ZoomingAndPanning]]で使用しているものとほぼ同じ`MouseWheelListener`を設定して実行していますが、画像を描画している`JPanel`を`JScrollPane`内に設定してスクロールバーでのスクロールを可能にしているため、`JPanel#paintComponent(...)`内での`AffineTransform`の使用方法などを変更しています。 - [[JPanelに表示した画像のズームとスクロール>Swing/ZoomingAndPanning]]のようにズームや移動を行うための`AffineTransform`(このサンプルでは`coordTransform`)を直接`Graphics2D`に設定すると、元からある`Graphics2D`コンテキスト内の`AffineTransform`(`JScrollBar`による移動)と競合して描画が乱れてしまう #code{{ g2.setTransform(coordTransform); g2.drawImage(img, 0, 0, this); }} - 2つの`AffineTransform`を`AffineTransform#concatenate(AffineTransform)`で連結してから、`Graphics2D#setTransform(AffineTransform)`で設定することで回避 #code{{ AffineTransform at = g2.getTransform(); at.concatenate(coordTransform); g2.setTransform(at); g2.drawImage(img, 0, 0, this); }} - または、[http://docs.oracle.com/javase/jp/8/docs/api/java/awt/Graphics2D.html#drawImage-java.awt.Image-java.awt.geom.AffineTransform-java.awt.image.ImageObserver- Graphics2D#drawImage(Image, AffineTransform, ImageObserver) (Java Platform SE 8)]を使用することで、`Graphics2D`コンテキスト内の`AffineTransform`が適用される前にイメージにズーム、移動変換を適用しておくことで回避 #code{{ g2.drawImage(img, coordTransform, this); }} * 参考リンク [#pd1ded23] - [[JPanelに表示した画像のズームとスクロール>Swing/ZoomingAndPanning]] -- マウスホイールによるズーム用のリスナを引用 - [[JScrollPaneのViewportをマウスで掴んでスクロール>Swing/HandScroll]] -- マウスドラッグによるスクロール用のリスナを引用 - [http://sozai-free.com/ 2000ピクセル以上のフリー写真素材集] * コメント [#xd8ff1ff] #comment #comment