TITLE:JToolBarに配置したアイコンをドラッグして並べ替える

Posted by at 2013-04-08

JToolBarに配置したアイコンをドラッグして並べ替える

JToolBarに配置したアイコンをドラッグ&ドロップで並べ替えます。

  • &jnlp;
  • &jar;
  • &zip;
RearrangeToolBarIcon.png

サンプルコード

class DragHandler extends MouseAdapter{
  private final JWindow window = new JWindow();
  private Component draggingComonent = null;
  private int index = -1;
  private Component gap = Box.createHorizontalStrut(24);
  private Point startPt = null;
  private int gestureMotionThreshold = DragSource.getDragThreshold();
  public DragHandler() {
    window.setBackground(new Color(0, true));
  }
  @Override public void mousePressed(MouseEvent e) {
    JComponent parent = (JComponent)e.getComponent();
    if(parent.getComponentCount()<=1) {
      startPt = null;
      return;
    }
    startPt = e.getPoint();
  }
  @Override
  public void mouseDragged(MouseEvent e) {
    Point pt = e.getPoint();
    JComponent parent = (JComponent)e.getComponent();
    int t = Math.sqrt(Math.pow(pt.x-startPt.x, 2)+Math.pow(pt.y-startPt.y, 2))
    if(startPt != null && t>gestureMotionThreshold) {
      startPt = null;
      Component c = parent.getComponentAt(pt);
      index = parent.getComponentZOrder(c);
      if(c == parent || index < 0) {
        return;
      }
      draggingComonent = c;

      parent.remove(draggingComonent);
      parent.add(gap, index);
      parent.revalidate();
      parent.repaint();

      window.add(draggingComonent);
      window.pack();

      Dimension d = draggingComonent.getPreferredSize();
      Point p = new Point(pt.x - d.width/2, pt.y - d.height/2);
      SwingUtilities.convertPointToScreen(p, parent);
      window.setLocation(p);
      window.setVisible(true);

      return;
    }
    if(!window.isVisible() || draggingComonent==null) {
      return;
    }

    Dimension d = draggingComonent.getPreferredSize();
    Point p = new Point(pt.x - d.width/2, pt.y - d.height/2);
    SwingUtilities.convertPointToScreen(p, parent);
    window.setLocation(p);

    for(int i=0; i<parent.getComponentCount(); i++) {
      Component c = parent.getComponent(i);
      Rectangle r = c.getBounds();
      Rectangle r1 = new Rectangle(r.x, r.y, r.width/2, r.height);
      Rectangle r2 = new Rectangle(r.x+r.width/2, r.y, r.width/2, r.height);
      if(r1.contains(pt)) {
        if(c==gap) {
          return;
        }
        int n = i-1>=0 ? i : 0;
        parent.remove(gap);
        parent.add(gap, n);
        parent.revalidate();
        parent.repaint();
        return;
      }else if(r2.contains(pt)) {
        if(c==gap) {
          return;
        }
        parent.remove(gap);
        parent.add(gap, i);
        parent.revalidate();
        parent.repaint();
        return;
      }
    }
    parent.remove(gap);
    parent.revalidate();
    parent.repaint();
  }

  @Override
  public void mouseReleased(MouseEvent e) {
    startPt = null;
    if(!window.isVisible() || draggingComonent==null) {
      return;
    }
    Point pt = e.getPoint();
    JComponent parent = (JComponent)e.getComponent();

    Component cmp = draggingComonent;
    draggingComonent = null;
    window.setVisible(false);

    for(int i=0; i<parent.getComponentCount(); i++) {
      Component c = parent.getComponent(i);
      Rectangle r = c.getBounds();
      Rectangle r1 = new Rectangle(r.x, r.y, r.width/2, r.height);
      Rectangle r2 = new Rectangle(r.x+r.width/2, r.y, r.width/2, r.height);
      if(r1.contains(pt)) {
        int n = i-1>=0 ? i : 0;
        parent.remove(gap);
        parent.add(cmp, n);
        parent.revalidate();
        parent.repaint();
        return;
      }else if(r2.contains(pt)) {
        parent.remove(gap);
        parent.add(cmp, i);
        parent.revalidate();
        parent.repaint();
        return;
      }
    }
    if(parent.getBounds().contains(pt)) {
      parent.remove(gap);
      parent.add(cmp);
    }else{
      parent.remove(gap);
      parent.add(cmp, index);
    }
    parent.revalidate();
    parent.repaint();
  }
}
View in GitHub: Java, Kotlin

解説

上記のサンプルでは、`JToolBarMouseListenerMouseMotionListenerを追加して、JLabel`に配置したアイコンを並べ替えています。

  • ドラッグされて移動対象になった`JLabelは、JToolBarから削除され、透明なJWindow`に移動
    • `JWindow`なので、元のフレームの外側にも移動可能
  • ドロップする位置に`JWindowが移動してきた場合、JToolBarBox.createHorizontalStrut(24);で作成したアイコンと同じ幅のBox`を配置
  • `JLabelがドロップされると、ダミーのBoxは削除され、JLabel`と入れ替え
    • `JWindow`は非表示に設定

制限

  • `JToolBar#setFloatable(false);`にしないとアイコンを移動できない
  • `JButton`などを移動できない
  • 非表示のコンポーネント(`Box.createGlue()`など)がドラッグできてしまう
  • `Ubuntu`などで移動の描画がチラつく

参考リンク

コメント