TITLE:ImageIconの形でJButtonを作成

ImageIconの形でJButtonを作成

Posted by terai at 2008-07-21
  • category: swing folder: RoundImageButton title: ImageIconの形でJButtonを作成 tags: [JButton, Shape, ImageIcon] author: aterai pubdate: 2008-07-21T16:27:56+09:00 description: 任意のShapeとその形に透過色を設定した画像を使ってJButtonを作成します。 image: https://lh6.googleusercontent.com/_9Z4BYR88imo/TQTSO4fquKI/AAAAAAAAAic/UdMAZSREN1U/s800/RoundImageButton.png hreflang:
       href: https://java-swing-tips.blogspot.com/2008/07/create-round-image-jbutton.html
       lang: en

概要

任意のShapeとその形に透過色を設定した画像を使ってJButtonを作成します。

概要

任意のShapeとその形に透過色を設定した画像を使ってJButtonを作成します。
  • &jnlp;
  • &jar;
  • &zip;

#screenshot

サンプルコード

#spanend
#spanadd
* サンプルコード [#sourcecode]
#spanend
#spanadd
#code(link){{
#spanend
class RoundButton extends JButton {
  protected Shape base;
  protected Shape shape
#spanadd

#spanend
  public RoundButton() {
    this(null, null);
  }
#spanadd

#spanend
  public RoundButton(Icon icon) {
    this(null, icon);
  }
#spanadd

#spanend
  public RoundButton(String text) {
    this(text, null);
  }
#spanadd

#spanend
  public RoundButton(Action a) {
    this();
    setAction(a);
  }
#spanadd

#spanend
  public RoundButton(String text, Icon icon) {
    setModel(new DefaultButtonModel());
    init(text, icon);
    if(icon==null) {
    if (icon == null) {
      return;
    }
    int iw = Math.max(icon.getIconWidth(), icon.getIconHeight());
    int sw = 1;
    setBorder(BorderFactory.createEmptyBorder(sw,sw,sw,sw));
    Dimension dim = new Dimension(iw+sw+sw, iw+sw+sw);
    setPreferredSize(dim);
    setMaximumSize(dim);
    setMinimumSize(dim);
    setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));
    setBackground(Color.BLACK);
    setContentAreaFilled(false);
    setFocusPainted(false);
    //setVerticalAlignment(SwingConstants.TOP);
    // setVerticalAlignment(SwingConstants.TOP);
    setAlignmentY(Component.TOP_ALIGNMENT);
    initShape();
  }
  protected Shape shape, base;
#spanadd

#spanend
  protected void initShape() {
    if(!getBounds().equals(base)) {
    if (!getBounds().equals(base)) {
      Dimension s = getPreferredSize();
      base = getBounds();
      shape = new Ellipse2D.Float(0, 0, s.width-1, s.height-1);
      shape = new Ellipse2D.Float(0, 0, s.width - 1, s.height - 1);
    }
  }
  @Override
  protected void paintBorder(Graphics g) {
#spanadd

#spanend
  @Override public Dimension getPreferredSize() {
    Icon icon = getIcon();
    Insets i = getInsets();
    int iw = Math.max(icon.getIconWidth(), icon.getIconHeight());
    return new Dimension(iw + i.right + i.left, iw + i.top + i.bottom);
  }
#spanadd

#spanend
  @Override protected void paintBorder(Graphics g) {
    initShape();
    Graphics2D g2 = (Graphics2D)g;
    Graphics2D g2 = (Graphics2D) g.create();
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                        RenderingHints.VALUE_ANTIALIAS_ON);
    g2.setColor(getBackground());
    //g2.setStroke(new BasicStroke(1.0f));
    // g2.setStroke(new BasicStroke(1f));
    g2.draw(shape);
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                        RenderingHints.VALUE_ANTIALIAS_OFF);
    g2.dispose();
  }
  @Override
  public boolean contains(int x, int y) {
#spanadd

#spanend
  @Override public boolean contains(int x, int y) {
    initShape();
    return shape.contains(x, y);
    // 以下、透過色が0でクリック不可にする場合の例
    // or return super.contains(x, y) && ((image.getRGB(x, y) >> 24) & 0xff) > 0;
  }
}

解説

上記のサンプルでは、JButtonに円形の画像を貼り付けてボタンを作成しています。
  • 円形、同サイズのPNG画像(円の外側が透過色)を三種類用意してJButtonに設定
    • setIcon
    • setPressedIcon
    • setRolloverIcon
  • setContentAreaFilled(false)などを設定して、ボタン自体の描画はしない
  • 推奨、最小、最大サイズを画像のサイズに合わせる
    • ただし、縁の線を描画するため、画像サイズより上下左右1pt大きくなるようEmptyBorderを設定している
  • containsをオーバーライドして、円の外側をクリックしてもボタンが反応しないようにする
    • 画像の透過色から、円を生成している訳ではなく、画像のサイズから円図形を別途作成している
    • 画像の透過色から、クリック可能な領域を設定する場合は、JComponentの形状定義を変更する
  • paintBorderをオーバーライドして、元の縁は描画せずにその幅の線で独自に円を描画する
    • containsで使用した図形を利用

解説

上記のサンプルでは、JButtonに円形の画像を貼り付けてボタンを作成しています。
  • - ボタンの揃えを変更するために、JPanelではなく、Boxを利用しているので、JDK 5 でも JDK 6 と同じように描画するために、Box#paintComponent を以下のようにオーバーライドしています。
  • Bug ID: 4907674 Box disregards setBackground() even when set Opaque(true)
  • 円形で同サイズのPNG画像(円の外側が透過色)を3種類用意し以下のメソッドでJButtonに設定
    • setIcon
    • setPressedIcon
    • setRolloverIcon
  • setContentAreaFilled(false)などを設定してボタン自体の描画はしない
  • JButtonの推奨、最小、最大サイズを画像のサイズに合わせる
    • ただし、縁の線を描画するため、画像サイズより上下左右1px大きくなるようEmptyBorderを設定
  • containsをオーバーライドして円の外側をクリックしてもボタンが反応しないようにする
    • このサンプルでは、画像の透過色から円を生成している訳ではなく画像のサイズから円図形を別途作成
    • 画像の透過色からクリック可能な領域を設定する場合はJComponentの形状定義を変更するを参照
  • paintBorderをオーバーライドして元の縁は描画せずにその幅の線で独自に円を描画する
    • containsで使用した図形を利用

#screenshot(,screenshot1.png)

  • -
  • ボタンの揃えを変更するためにJPanelではなくBoxを利用しているのでJDK 5でもJDK 6と同じように描画するためにBox#paintComponentを以下のようにオーバーライド JDK 6で修正済み
    • JDK-4907674 Box disregards setBackground() even when set Opaque(true) - Java Bug System
    • RoundImageButton1.png
      private final Box box = // JDK 6 Box.createHorizontalBox();
        // JDK 5
        new Box(BoxLayout.X_AXIS) {
          protected void paintComponent(Graphics g) {
            if(ui != null) {
          @Override protected void paintComponent(Graphics g) {
            if (ui != null) {
              super.paintComponent(g);
            }else if(isOpaque()) {
            } else if (isOpaque()) {
              g.setColor(getBackground());
              g.fillRect(0, 0, getWidth(), getHeight());
            }
          }
        };
      
  • - 以下のようなButtonUIを使って、JButtonをオーバーライドしない方法もあります。
    #spanend
    #spandel
    JButton button = new JButton(icon);
    #spanend
    #spandel
    button.setUI(new RoundImageButtonUI());
    #spanend
    #spandel
    
    #spanend
    #spandel
    class RoundImageButtonUI extends BasicButtonUI{
    #spanend
      protected Shape shape, base;
      @Override
      protected void installDefaults(AbstractButton b) {
        super.installDefaults(b);
        Icon icon = b.getIcon();
        if(icon==null) return;
        int iw = Math.max(icon.getIconWidth(), icon.getIconHeight());
        int sw = 1;
        b.setBorder(BorderFactory.createEmptyBorder(sw,sw,sw,sw));
        b.setContentAreaFilled(false);
        b.setFocusPainted(false);
        b.setOpaque(false);
        b.setBackground(Color.BLACK);
        Dimension dim = new Dimension(iw+sw+sw, iw+sw+sw);
        b.setPreferredSize(dim);
        b.setMaximumSize(dim);
        b.setMinimumSize(dim);
        //b.setVerticalAlignment(SwingConstants.TOP);
        b.setAlignmentY(Component.TOP_ALIGNMENT);
        initShape(b);
      }
      @Override
      protected void installListeners(AbstractButton b) {
        BasicButtonListener listener = new BasicButtonListener(b) {
          @Override public void mousePressed(MouseEvent e) {
            AbstractButton b = (AbstractButton) e.getSource();
            initShape(b);
            if(shape.contains(e.getX(), e.getY())) {
              super.mousePressed(e);
            }
          }
          @Override public void mouseEntered(MouseEvent e) {
            if(shape.contains(e.getX(), e.getY())) {
              super.mouseEntered(e);
            }
          }
          @Override public void mouseMoved(MouseEvent e) {
            if(shape.contains(e.getX(), e.getY())) {
              super.mouseEntered(e);
            }else{
              super.mouseExited(e);
            }
          }
        };
        if(listener != null) {
          b.addMouseListener(listener);
          b.addMouseMotionListener(listener);
          b.addFocusListener(listener);
          b.addPropertyChangeListener(listener);
          b.addChangeListener(listener);
        }
      }
      @Override
      public void paint(Graphics g, JComponent c) {
        super.paint(g, c);
        Graphics2D g2 = (Graphics2D)g;
        initShape(c);
        //Border
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                            RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setColor(c.getBackground());
        //g2.setStroke(new BasicStroke(1.0f));
        g2.draw(shape);
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                            RenderingHints.VALUE_ANTIALIAS_OFF);
      }
      private void initShape(JComponent c) {
        if(!c.getBounds().equals(base)) {
          Dimension s = c.getPreferredSize();
          base = c.getBounds();
          shape = new Ellipse2D.Float(0, 0, s.width-1, s.height-1);
        }
      }
    #spandel
    }
    #spanend
    #spandel
    

参考リンク

参考リンク

コメント

コメント