Swing/KineticScrolling の変更点
- 追加された行はこの色です。
- 削除された行はこの色です。
- Swing/KineticScrolling へ行く。
- Swing/KineticScrolling の差分を削除
--- category: swing folder: KineticScrolling title: JScrollPaneでキネティックスクロール tags: [JScrollPane, Animation, MouseListener, JViewport] author: aterai pubdate: 2010-08-16T13:34:26+09:00 description: JScrollPaneにキネティックスクロール(慣性スクロール)風の動作をするマウスリスナーを設定します。 image: https://lh4.googleusercontent.com/_9Z4BYR88imo/TQTO32D08pI/AAAAAAAAAdE/TpuoGrYo-Q0/s800/KineticScrolling.png hreflang: href: https://java-swing-tips.blogspot.com/2010/08/kinetic-scrolling-jscrollpane.html lang: en --- * 概要 [#summary] `JScrollPane`にキネティックスクロール(慣性スクロール)風の動作をするマウスリスナーを設定します。 #download(https://lh4.googleusercontent.com/_9Z4BYR88imo/TQTO32D08pI/AAAAAAAAAdE/TpuoGrYo-Q0/s800/KineticScrolling.png) * サンプルコード [#sourcecode] #code(link){{ class KineticScrollingListener2 extends MouseAdapter implements HierarchyListener { private static final int SPEED = 4; private static final int DELAY = 10; private static final double D = 0.8; private final JComponent label; private final Point startPt = new Point(); private final Point delta = new Point(); private final Cursor dc; private final Cursor hc = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); private final Timer inside = new Timer(DELAY, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JViewport vport = (JViewport) SwingUtilities.getUnwrappedParent(label); Point vp = vport.getViewPosition(); // System.out.format("s: %s, %s%n", delta, vp); vp.translate(-delta.x, -delta.y); vport.setViewPosition(vp); if (Math.abs(delta.x) > 0 || Math.abs(delta.y) > 0) { delta.setLocation((int) (delta.x * D), (int) (delta.y * D)); // Outside if (vp.x < 0 || vp.x + vport.getWidth() - label.getWidth() > 0) { delta.x = (int) (delta.x * D); } if (vp.y < 0 || vp.y + vport.getHeight() - label.getHeight() > 0) { delta.y = (int) (delta.y * D); } } else { inside.stop(); if (!isInside(vport, label)) { outside.start(); } } } }); private final Timer outside = new Timer(DELAY, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JViewport vport = (JViewport) SwingUtilities.getUnwrappedParent(label); Point vp = vport.getViewPosition(); // System.out.format("r: %s%n", vp); if (vp.x < 0) { vp.x = (int) (vp.x * D); } if (vp.y < 0) { vp.y = (int) (vp.y * D); } if (vp.x + vport.getWidth() - label.getWidth() > 0) { vp.x = (int) (vp.x - (vp.x + vport.getWidth() - label.getWidth()) * (1d - D)); } if (vp.y + vport.getHeight() > label.getHeight()) { vp.y = (int) (vp.y - (vp.y + vport.getHeight() - label.getHeight()) * (1d - D)); } vport.setViewPosition(vp); if (isInside(vport, label)) { outside.stop(); } } }); protected static final int SPEED = 4; protected static final int DELAY = 10; protected static final double D = .8; protected final JComponent label; protected final Point startPt = new Point(); protected final Point delta = new Point(); protected final Cursor dc; protected final Cursor hc = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); protected final Timer inside = new Timer(DELAY, null); protected final Timer outside = new Timer(DELAY, null); private static boolean isInside(JViewport vport, JComponent comp) { Point vp = vport.getViewPosition(); return vp.x >= 0 && vp.x + vport.getWidth() - comp.getWidth() <= 0 && vp.y >= 0 && vp.y + vport.getHeight() - comp.getHeight() <= 0; protected static boolean isInside(JViewport viewport, JComponent comp) { Point vp = viewport.getViewPosition(); return vp.x >= 0 && vp.x + viewport.getWidth() - comp.getWidth() <= 0 && vp.y >= 0 && vp.y + viewport.getHeight() - comp.getHeight() <= 0; } public KineticScrollingListener2(JComponent comp) { protected KineticScrollingListener2(JComponent comp) { super(); this.label = comp; this.dc = comp.getCursor(); inside.addActionListener(e -> dragInside()); outside.addActionListener(e -> dragOutside()); } private void dragInside() { JViewport viewport = (JViewport) SwingUtilities.getUnwrappedParent(label); Point vp = viewport.getViewPosition(); // System.out.format("s: %s, %s%n", delta, vp); vp.translate(-delta.x, -delta.y); viewport.setViewPosition(vp); if (Math.abs(delta.x) > 0 || Math.abs(delta.y) > 0) { delta.setLocation((int) (delta.x * D), (int) (delta.y * D)); // Outside if (vp.x < 0 || vp.x + viewport.getWidth() - label.getWidth() > 0) { delta.x = (int) (delta.x * D); } if (vp.y < 0 || vp.y + viewport.getHeight() - label.getHeight() > 0) { delta.y = (int) (delta.y * D); } } else { inside.stop(); if (!isInside(viewport, label)) { outside.start(); } } } private void dragOutside() { JViewport viewport = (JViewport) SwingUtilities.getUnwrappedParent(label); Point vp = viewport.getViewPosition(); // System.out.format("r: %s%n", vp); if (vp.x < 0) { vp.x = (int) (vp.x * D); } if (vp.y < 0) { vp.y = (int) (vp.y * D); } if (vp.x + viewport.getWidth() - label.getWidth() > 0) { vp.x = (int) (vp.x - (vp.x + viewport.getWidth() - label.getWidth()) * (1d - D)); } if (vp.y + viewport.getHeight() > label.getHeight()) { vp.y = (int) (vp.y - (vp.y + viewport.getHeight() - label.getHeight()) * (1d - D)); } viewport.setViewPosition(vp); if (isInside(viewport, label)) { outside.stop(); } } @Override public void mousePressed(MouseEvent e) { e.getComponent().setCursor(hc); startPt.setLocation(e.getPoint()); inside.stop(); outside.stop(); } @Override public void mouseDragged(MouseEvent e) { Point pt = e.getPoint(); JViewport vport = (JViewport) SwingUtilities.getUnwrappedParent(label); Point vp = vport.getViewPosition(); JViewport viewport = (JViewport) SwingUtilities.getUnwrappedParent(label); Point vp = viewport.getViewPosition(); vp.translate(startPt.x - pt.x, startPt.y - pt.y); vport.setViewPosition(vp); viewport.setViewPosition(vp); delta.setLocation(SPEED * (pt.x - startPt.x), SPEED * (pt.y - startPt.y)); startPt.setLocation(pt); } @Override public void mouseReleased(MouseEvent e) { e.getComponent().setCursor(dc); JViewport vport = (JViewport) SwingUtilities.getUnwrappedParent(label); if (isInside(vport, label)) { JViewport viewport = (JViewport) SwingUtilities.getUnwrappedParent(label); if (isInside(viewport, label)) { inside.start(); } else { outside.start(); } } @Override public void hierarchyChanged(HierarchyEvent e) { Component c = e.getComponent(); if ((e.getChangeFlags() & HierarchyEvent.DISPLAYABILITY_CHANGED) != 0 && !c.isDisplayable()) { inside.stop(); outside.stop(); } } } }} * 解説 [#explanation] - `scrollRectToVisible` -- マウスがリリースされたらタイマーを起動し、`JComponent#scrollRectToVisible(Rectangle)`メソッドでスクロール - `setViewPosition` -- マウスがリリースされたらタイマーを起動し、`JViewport#setViewPosition(Point)`メソッドでスクロール -- `View`として設定されている`JLabel`の外で移動が止まった(またはマウスがリリースされた)場合は、別のタイマーで`JLabel`の縁まで戻る * 参考リンク [#reference] - [[JScrollPaneのViewportをマウスで掴んでスクロール>Swing/HandScroll]] - [[JScrollPaneのオートスクロール>Swing/AutoScroll]] * コメント [#comment] #comment - 慣性(モーメンタム)スクロール、フリックスクロール(フリック+慣性スクロール?)、・・・でもやっぱり猫の掌スクロールを最初に思い出してしまう。 -- &user(aterai); &new{2010-08-16 (月) 13:41:47}; - `JDK 1.7.0`では、`JViewport#setViewPosition(Point)`を使って右下外部に移動できなくなっているので、[[JScrollPaneのViewportをマウスで掴んでスクロール>Swing/HandScroll]]と同じ対応をしてソースを更新。 -- &user(aterai); &new{2011-10-03 (月) 18:08:23}; #comment