概要

JPanelGridLayoutでコンポーネントを追加し、これをJScrollPaneに配置して、グリッド単位での表示、スクロールアニメーションによる切り替えを行います。

サンプルコード

class GridPanel extends JPanel implements Scrollable {
  public static int cols = 3, rows = 4;
  public static Dimension size = new Dimension(160 * cols, 120 * rows);

  public GridPanel() {
    super(new GridLayout(rows, cols, 0, 0));
    // scrollBar.putClientProperty("JScrollBar.fastWheelScrolling", Boolean.FALSE);
  }

  @Override public Dimension getPreferredScrollableViewportSize() {
    Dimension d = getPreferredSize();
    return new Dimension(d.width / cols, d.height / rows);
  }

  @Override public int getScrollableUnitIncrement(
    Rectangle visibleRect, int orientation, int direction) {
    return orientation == SwingConstants.HORIZONTAL ?
           visibleRect.width : visibleRect.height;
  }

  @Override public int getScrollableBlockIncrement(
    Rectangle visibleRect, int orientation, int direction) {
    return orientation == SwingConstants.HORIZONTAL ?
           visibleRect.width : visibleRect.height;
  }

  @Override public boolean getScrollableTracksViewportWidth() {
    return false;
  }

  @Override public boolean getScrollableTracksViewportHeight() {
    return false;
  }

  @Override public Dimension getPreferredSize() {
    return size;
  }
}

class ScrollAction extends AbstractAction {
  private static Timer scroller;
  private final Point vec;
  private final JScrollPane scrollPane;

  public ScrollAction(String name, JScrollPane scrollPane, Point vec) {
    super(name);
    this.scrollPane = scrollPane;
    this.vec = vec;
  }

  @Override public void actionPerformed(ActionEvent e) {
    final JViewport vport = scrollPane.getViewport();
    final JComponent v = (JComponent) vport.getView();
    final int w   = vport.getWidth(),
              h   = vport.getHeight(),
              sx  = vport.getViewPosition().x,
              sy  = vport.getViewPosition().y;
    final Rectangle rect = new Rectangle(w, h);
    if (scroller != null && scroller.isRunning()) return;
    scroller = new Timer(5, new ActionListener() {
      double MAX = 100d;
      int count = (int) MAX;
      @Override public void actionPerformed(ActionEvent e) {
        double a = easeInOut(--count / MAX);
        int dx = (int) (w - a * w + .5);
        int dy = (int) (h - a * h + .5);
        if (count <= 0) {
          dx = w;
          dy = h;
          scroller.stop();
        }
        rect.setLocation(sx + vec.x * dx, sy + vec.y * dy);
        v.scrollRectToVisible(rect);
      }
    });
    scroller.start();
  }

  private static double easeInOut(double t) {
    // range: 0.0<=t<=1.0
    if (t < .5) {
      return .5 * pow3(t * 2d);
    } else {
      return .5 * (pow3(t * 2d - 2d) + 2d);
    }
  }

  private static double pow3(double a) {
    // return Math.pow(a, 3d);
    return a * a * a;
  }
}
View in GitHub: Java, Kotlin

解説

上記のサンプルでは、GridLayoutを使ってグリッド状に配置したコンポーネントをJScrollPanescrollRectToVisible(...)メソッドを使用して、スクロールアニメーション付きでグリッド単位の表示切り替えを行なっています。

  • 最初に表示されるコンポーネントは、左上にある(一番最初にJPanelに追加した)コンポーネント
  • PreferredScrollableViewportSizeの変更(グリッドのサイズ変更)には対応していない
  • JScrollPaneの各スクロールバーは常に非表示なので、マウスホイールによるスクロールには対応していない
  • フォーカスの移動に表示切り替えは対応していない
  • CardLayout+スクロールアニメーションでも同様の動作が可能?

参考リンク

コメント