• 追加された行はこの色です。
  • 削除された行はこの色です。
---
category: swing
folder: CustomDecoratedFrame
title: JFrameのタイトルバーなどの装飾を独自のものにカスタマイズする
tags: [JFrame, MouseListener, MouseMotionListener, JPanel, JLabel, ContentPane, Transparent]
author: aterai
pubdate: 2010-01-18T11:27:29+09:00
description: JFrameのタイトルバーなどを非表示にして独自に描画し、これに移動リサイズなどの機能も追加します。
image: https://lh3.googleusercontent.com/_9Z4BYR88imo/TQTKV1P7mYI/AAAAAAAAAV0/u4qjd-ItBYU/s800/CustomDecoratedFrame.png
hreflang:
    href: https://java-swing-tips.blogspot.com/2010/05/custom-decorated-titlebar-jframe.html
    lang: en
---
* 概要 [#wc20d97b]
* 概要 [#summary]
`JFrame`のタイトルバーなどを非表示にして独自に描画し、これに移動リサイズなどの機能も追加します。

#download(https://lh3.googleusercontent.com/_9Z4BYR88imo/TQTKV1P7mYI/AAAAAAAAAV0/u4qjd-ItBYU/s800/CustomDecoratedFrame.png)

* サンプルコード [#da76e44b]
* サンプルコード [#sourcecode]
#code(link){{
class ResizeWindowListener extends MouseAdapter {
  private Rectangle startSide = null;
  private final JFrame frame;
  public ResizeWindowListener(JFrame frame) {
    this.frame = frame;
  }

  @Override public void mousePressed(MouseEvent e) {
    startSide = frame.getBounds();
  }

  @Override public void mouseDragged(MouseEvent e) {
    if(startSide==null) return;
    if (startSide == null) return;
    Component c = e.getComponent();
    if(c==topleft) {
    if (c == topleft) {
      startSide.y += e.getY();
      startSide.height -= e.getY();
      startSide.x += e.getX();
      startSide.width -= e.getX();
    }else if(c==top) {
    } else if (c == top) {
      startSide.y += e.getY();
      startSide.height -= e.getY();
    }else if(c==topright) {
    } else if (c == topright) {
      startSide.y += e.getY();
      startSide.height -= e.getY();
      startSide.width += e.getX();
    }else if(c==left) {
    } else if (c == left) {
      startSide.x += e.getX();
      startSide.width -= e.getX();
    }else if(c==right) {
    } else if (c == right) {
      startSide.width += e.getX();
    }else if(c==bottomleft) {
    } else if (c == bottomleft) {
      startSide.height += e.getY();
      startSide.x += e.getX();
      startSide.width -= e.getX();
    }else if(c==bottom) {
    } else if (c == bottom) {
      startSide.height += e.getY();
    }else if(c==bottomright) {
    } else if (c == bottomright) {
      startSide.height += e.getY();
      startSide.width += e.getX();
    }
    frame.setBounds(startSide);
  }
}
}}

* 解説 [#dd2ca04c]
上記のサンプルではタイトルバーを、`setUndecorated(true)`で非表示にし、移動可能にした`JPanel`を追加してタイトルバーにしています。
リサイズは、[https://forums.oracle.com/thread/1365156 Swing - Undecorated and resizable dialog]や`BasicInternalFrameUI.java`、`MetalRootPaneUI#MouseInputHandler`などを参考にして、周辺にそれぞれ対応するリサイズカーソルを設定した`JLabel`を配置しています。
* 解説 [#explanation]
上記のサンプルでは`JFrame`の元のタイトルバーを`setUndecorated(true)`で非表示にし、マウスドラッグで移動可能にした`JPanel`を追加してタイトルバーの代わりにしています。

----
`JDK 1.7.0`の場合、`JFrame`の背景色を透明(`frame.setBackground(new Color(0,0,0,0));`)にし、`ContentPane`の左右上の角をクリアして透明にしています。
- マウスドラッグでのフレームのリサイズは[https://community.oracle.com/thread/1365156 Swing - Undecorated and resizable dialog]や`BasicInternalFrameUI.java`、`MetalRootPaneUI#MouseInputHandler`などを参考にして、周辺にそれぞれ対応するリサイズカーソルを設定した`JLabel`を配置して実行
- `JDK 1.7.0`の場合`JFrame#setBackground(new Color(0x0, true))`で`JFrame`の背景色を透明化、かつ`ContentPane`の左右上の角をクリアして透明化
- [[JRootPaneにリサイズのための装飾を設定する>Swing/WindowDecorationStyle]]のように`JRootPane#setWindowDecorationStyle(JRootPane.PLAIN_DIALOG)`を使用してリサイズする方法もある

----
- [[JRootPaneにリサイズのための装飾を設定する>Swing/WindowDecorationStyle]]のように、`JRootPane#setWindowDecorationStyle(JRootPane.PLAIN_DIALOG);`を使用してリサイズする方法もあります。
#code{{
// package example;

#code{{
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class WindowDecorationStyleTest {
public class MainPanel {
  private static final Color BG_COLOR = Color.ORANGE;
  public JComponent makeTitleBar() {
    JLabel label = new JLabel("Title");
    JLabel label = new JLabel("Title", SwingConstants.CENTER);
    label.setOpaque(true);
    label.setForeground(Color.WHITE);
    label.setBackground(Color.BLACK);
    label.setBackground(BG_COLOR);
    DragWindowListener dwl = new DragWindowListener();
    label.addMouseListener(dwl);
    label.addMouseMotionListener(dwl);

    JPanel title = new JPanel(new BorderLayout());
    title.setBorder(BorderFactory.createMatteBorder(0, 4, 4, 4, Color.BLACK));
    title.setBorder(BorderFactory.createMatteBorder(0, 2, 2, 2, BG_COLOR));
    title.add(label);
    title.add(new JButton(new AbstractAction("x") {
      @Override public void actionPerformed(ActionEvent e) {
        Window w = SwingUtilities.windowForComponent((Component) e.getSource());
        w.dispatchEvent(new WindowEvent(w, WindowEvent.WINDOW_CLOSING));
      }
    }), BorderLayout.EAST);
    title.add(makeCloseButton(), BorderLayout.EAST);
    return title;
  }

  public JComponent makeUI() {
    return new JScrollPane(new JTree());
  }
  public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
      @Override public void run() {
        createAndShowGUI();

  private static JButton makeCloseButton() {
    JButton button = new JButton(new CloseIcon());
    button.setContentAreaFilled(false);
    button.setFocusPainted(false);
    button.setBorder(BorderFactory.createEmptyBorder());
    button.setOpaque(true);
    button.setBackground(BG_COLOR);
    button.addActionListener(e -> {
      JComponent b = (JComponent) e.getSource();
      Container c = b.getTopLevelAncestor();
      if (c instanceof Window) {
        Window w = (Window) c;
        w.dispatchEvent(new WindowEvent(w, WindowEvent.WINDOW_CLOSING));
      }
    });
    return button;
  }

  public static void main(String[] args) {
    EventQueue.invokeLater(MainPanel::createAndShowGUI);
  }

  public static void createAndShowGUI() {
    JFrame frame = new JFrame();
    frame.setUndecorated(true);

    WindowDecorationStyleTest demo = new WindowDecorationStyleTest();
    MainPanel demo = new MainPanel();
    JRootPane root = frame.getRootPane();
    root.setWindowDecorationStyle(JRootPane.PLAIN_DIALOG);
    root.setBorder(BorderFactory.createMatteBorder(4, 8, 8, 8, Color.BLACK));
    root.setBorder(BorderFactory.createMatteBorder(2, 4, 4, 4, BG_COLOR));
    JLayeredPane layeredPane = root.getLayeredPane();
    Component c = layeredPane.getComponent(1);
    if (c instanceof JComponent) {
      JComponent orgTitlePane = (JComponent) c;
      orgTitlePane.removeAll();
      orgTitlePane.setLayout(new BorderLayout());
      orgTitlePane.add(demo.makeTitleBar());
    }
    frame.setMinimumSize(new Dimension(300, 120));
    frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

    try {
      UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
      ex.printStackTrace();
      Toolkit.getDefaultToolkit().beep();
    }

    frame.getContentPane().add(demo.makeUI());
    frame.setSize(320, 240);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }
}

class DragWindowListener extends MouseAdapter {
  private final transient Point startPt = new Point();
  private final Point startPt = new Point();
  private Window window;
  @Override public void mousePressed(MouseEvent me) {
    if (window == null) {
      Object o = me.getSource();
      if (o instanceof Window) {
        window = (Window) o;
      } else if (o instanceof JComponent) {
        window = SwingUtilities.windowForComponent(me.getComponent());
      }
    }
    startPt.setLocation(me.getPoint());
  }

  @Override public void mouseDragged(MouseEvent me) {
    if (window != null) {
      Point pt = new Point();
      pt = window.getLocation(pt);
      int x = pt.x - startPt.x + me.getX();
      int y = pt.y - startPt.y + me.getY();
      window.setLocation(x, y);
    }
  }
}

class CloseIcon implements Icon {
  @Override public void paintIcon(Component c, Graphics g, int x, int y) {
    Graphics2D g2 = (Graphics2D) g.create();
    g2.translate(x, y);
    g2.setPaint(Color.WHITE);
    g2.drawLine(4, 4, 11, 11);
    g2.drawLine(4, 5, 10, 11);
    g2.drawLine(5, 4, 11, 10);
    g2.drawLine(11, 4, 4, 11);
    g2.drawLine(11, 5, 5, 11);
    g2.drawLine(10, 4, 4, 10);
    g2.dispose();
  }

  @Override public int getIconWidth() {
    return 16;
  }

  @Override public int getIconHeight() {
    return 16;
  }
}
}}

* 参考リンク [#p98cd47e]
- [https://forums.oracle.com/thread/1365156 Swing - Undecorated and resizable dialog]
* 参考リンク [#reference]
- [https://community.oracle.com/thread/1365156 Swing - Undecorated and resizable dialog]
- [[JWindowをマウスで移動>Swing/DragWindow]]
- [[JInternalFrameをJFrameとして表示する>Swing/InternalFrameTitleBar]]
- [[JRootPaneにリサイズのための装飾を設定する>Swing/WindowDecorationStyle]]

* コメント [#nc60542a]
* コメント [#comment]
#comment
- `blogger`の方にコメントをもらって、調査、修正中だけど、`dual-monitor`環境が無いのでテストしづらい…。 -- &user(aterai); &new{2010-10-06 (水) 13:01:36};
- [http://java-swing-tips.blogspot.com/2010/05/custom-decorated-titlebar-jframe.html blogspot]で指摘されていた件について: このサンプルを`1.6.0_xx`+`WebStart`で実行すると、画面の外にフレームをドラッグすることが出来なかったのですが、`JRE`のバージョンを`1.7.0`にすると、`WebStart`で起動しても画面外に移動可能になっているみたいです。もしかしてデュアルディスプレイでも移動できるようになっているのかも?(確認してないですが...) -- &user(aterai); &new{2011-09-06 (火) 21:27:18};
//-- <java version="1.7+" /> にしたjnlp http://terai.xrea.jp/swing/customdecoratedframe/example2.jnlp -- &user(aterai); &new{2012-05-10 (木) 15:45:16};
- マルチモニター関係のメモ: [http://bugs.java.com/bugdatabase/view_bug.do?bug_id=7123767 Bug ID: 7123767 Wrong tooltip location in Multi-Monitor configurations] -- &user(aterai); &new{2012-08-14 (火) 13:55:29};
- [https://java-swing-tips.blogspot.com/2010/05/custom-decorated-titlebar-jframe.html blogspot]で指摘されていた件について: このサンプルを`1.6.0_xx`+`WebStart`で実行すると、画面外へのフレームのドラッグは不可でしたが、`Java 1.7.0`では`WebStart`で起動しても画面外に移動可能になっているようです。もしかしてデュアルディスプレイでも移動できるようになっているのかも?(未確認...) -- &user(aterai); &new{2011-09-06 (火) 21:27:18};
//-- <java version="1.7+" /> にしたjnlp https://ateraimemo.com/swing/customdecoratedframe/example2.jnlp -- &user(aterai); &new{2012-05-10 (木) 15:45:16};
- マルチモニター関係のメモ: [https://bugs.openjdk.org/browse/JDK-7123767 Bug ID: 7123767 Wrong tooltip location in Multi-Monitor configurations] -- &user(aterai); &new{2012-08-14 (火) 13:55:29};

#comment