• title: JToolBarに配置したアイコンをドラッグして並べ替える tags: [JToolBar, JLabel, Icon, DragAndDrop, MouseListener, MouseMotionListener, JWindow] author: aterai pubdate: 2013-04-08T01:18:50+09:00 description: JToolBarに配置したアイコンをドラッグ&ドロップで並べ替えます。

概要

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

サンプルコード

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以外の場所にドロップされた場合は、ドラッグ前の位置にJLabelを戻す

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

参考リンク

コメント