当自定义按钮更改文本时,Java布局不会更新组件大小

时间:2014-04-02 20:30:10

标签: java swing button layout

我正在制作一个按钮,当你点击它时它会改变文字。但是当我单击按钮并更改文本时,按钮不会根据文本更改大小。相反,它变小了,并试图做到这一点" ..."没有足够空间的东西。这是我的代码:

Text.java

import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.swing.JFrame;
import javax.swing.JPanel;


public class Test {
    public static void main(String[] args){
        JFrame frame = new JFrame();
        JPanel panel = new JPanel();
        frame.add(panel);
        final CButton button = new CButton("");
        panel.add(button);
        button.addMouseListener(new MouseListener(){
            @Override
            public void mouseClicked(MouseEvent arg0) {
                button.setText(button.getText()+"f");
            }

            @Override
            public void mouseEntered(MouseEvent arg0) {

            }

            @Override
            public void mouseExited(MouseEvent arg0) {

            }

            @Override
            public void mousePressed(MouseEvent arg0) {

            }

            @Override
            public void mouseReleased(MouseEvent arg0) {

            }
        });
        frame.setSize(500,500);
        frame.setVisible(true);
    }
}

CButton.java

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.Area;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;

import javax.swing.JButton;
import javax.swing.JOptionPane;

public class CButton extends JButton implements ComponentListener, KeyListener {
    protected static final int BORDER_WIDTH = 5;
    private static final Font font = new Font("Arial", Font.PLAIN, 30);
    private static final Insets INSETS_MARGIN = new Insets(2, 5, 2, 5);

    private static final long serialVersionUID = 1L;
    protected Area m_areaDraw = null;
    private Area m_areaFill = null;

    private double m_dHeightDraw = 0d;
    private double m_dHeightFill = 0d;
    private double m_dWidthDraw = 0d;

    private double m_dWidthFill = 0d;
    private int m_nMinHeight = 0;
    private int m_nMinWidth = 0;

    private int m_nStringHeightMax = 0;
    private int m_nStringWidthMax = 0;
    private RoundRectangle2D m_rrect2dDraw = null;
    private RoundRectangle2D m_rrect2dFill = null;
    private Shape m_shape = null;

    public CButton(String strLabel) {
        super(strLabel);
        setContentAreaFilled(false);
        setMargin(CButton.INSETS_MARGIN);
        setFocusPainted(false);
        addComponentListener(this);
        addKeyListener(this);
        // Determine the buttons initial size
        setFont(CButton.font);
        Frame frame = JOptionPane.getRootFrame();
        FontMetrics fm = frame.getFontMetrics(getFont());
        m_nStringWidthMax = fm.stringWidth(getText());
        m_nStringWidthMax = Math.max(m_nStringWidthMax,
                fm.stringWidth(getText()));
        // WARNING: use getMargin. it refers to dist btwn text and border.
        // Also use getInsets. it refers to the width of the border
        int nWidth = Math.max(m_nMinWidth, m_nStringWidthMax + getMargin().left
                + this.getInsets().left + getMargin().right
                + this.getInsets().right);
        m_nStringHeightMax = fm.getHeight();
        // WARNING: use getMargin. it refers to dist btwn text and border.
        // Also use getInsets. it refers to the width of the border
        int nHeight = Math.max(m_nMinHeight, m_nStringHeightMax
                + getMargin().left + this.getInsets().left + getMargin().right
                + this.getInsets().right);
        setPreferredSize(new Dimension(
                nWidth + ((2 * getFont().getSize()) / 5), nHeight
                        + ((2 * getFont().getSize()) / 5)));
        // Set the initial draw and fill dimensions
        setShape();
    }

    @Override
    public void componentHidden(ComponentEvent e) {
    }

    @Override
    public void componentMoved(ComponentEvent e) {
    }

    // Needed if we want this button to resize
    @Override
    public void componentResized(ComponentEvent e) {
        m_shape = new Rectangle2D.Float(0, 0, getBounds().width,
                getBounds().height);
        m_dWidthFill = (double) getBounds().width - 1;
        m_dHeightFill = (double) getBounds().height - 1;
        m_dWidthDraw = ((double) getBounds().width - 1)
                - (CButton.BORDER_WIDTH - 1);
        m_dHeightDraw = ((double) getBounds().height - 1)
                - (CButton.BORDER_WIDTH - 1);
        setShape();
        repaint();
    }

    @Override
    public void componentShown(ComponentEvent e) {
    }

    @Override
    public boolean contains(int nX, int nY) {
        if ((null == m_shape) || m_shape.getBounds().equals(getBounds())) {
            m_shape = new Rectangle2D.Float(0, 0, this.getBounds().width,
                    this.getBounds().height);
        }
        return m_shape.contains(nX, nY);
    }

    // This is so the button is triggered when it has focus
    // and we press the Enter key.
    @Override
    public void keyPressed(KeyEvent e) {
        if ((e.getSource() == this) && (e.getKeyCode() == KeyEvent.VK_ENTER)) {
            doClick();
        }
    };

    @Override
    public void keyReleased(KeyEvent e) {
    };

    @Override
    public void keyTyped(KeyEvent e) {
    };

    @Override
    public void paintBorder(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;
        RenderingHints hints = new RenderingHints(
                RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setRenderingHints(hints);
        g2.setColor(Color.black);
        Stroke strokeOld = g2.getStroke();
        g2.setStroke(new BasicStroke(CButton.BORDER_WIDTH,
                BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
        g2.draw(m_areaDraw);

        if (getModel().isRollover()) {
            g2.setColor(Color.GRAY);
            g2.draw(m_areaDraw);
        }
        g2.setStroke(strokeOld);
    };

    @Override
    public void paintComponent(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;
        RenderingHints hints = new RenderingHints(
                RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setRenderingHints(hints);
        if (getModel().isArmed()) {
            g2.setColor(Color.CYAN.darker());
        } else {
            g2.setColor(Color.CYAN);
        }
        g2.fill(m_areaFill);
        super.paintComponent(g2);
    }

    private void setShape() {
        // Area
        double dArcLengthFill = Math.min(m_dWidthFill, m_dHeightFill);
        m_rrect2dFill = new RoundRectangle2D.Double(0d, 0d, m_dWidthFill,
                m_dHeightFill, dArcLengthFill, dArcLengthFill);
        // WARNING: arclength and archeight are divided by 2
        // when they get into the roundedrectangle shape
        m_areaFill = new Area(m_rrect2dFill);
        // Border
        double dArcLengthDraw = Math.min(m_dWidthDraw, m_dHeightDraw);
        m_rrect2dDraw = new RoundRectangle2D.Double(
                (CButton.BORDER_WIDTH - 1) / 2, (CButton.BORDER_WIDTH - 1) / 2,
                m_dWidthDraw, m_dHeightDraw, dArcLengthDraw, dArcLengthDraw);
        m_areaDraw = new Area(m_rrect2dDraw);
    }

    @Override
    public void setText(String strText) {
        super.setText(strText);
        int nWidth = Math.max(m_nMinWidth, m_nStringWidthMax + getInsets().left
                + getInsets().right);
        int nHeight = Math.max(0, getPreferredSize().height);
        setPreferredSize(new Dimension(nWidth, nHeight));

        m_dWidthFill = getBounds().width - 1;
        m_dHeightFill = getBounds().height - 1;

        if ((m_dWidthFill <= 0) || (m_dHeightFill <= 0)) {
            m_dWidthFill = (double) getPreferredSize().width - 1;
            m_dHeightFill = (double) getPreferredSize().height - 1;
        }

        m_dWidthDraw = m_dWidthFill - (CButton.BORDER_WIDTH - 1);
        m_dHeightDraw = m_dHeightFill - (CButton.BORDER_WIDTH - 1);

        setShape();
    }
}

2 个答案:

答案 0 :(得分:1)

你打电话

setPreferredSize(new Dimension(
    nWidth + ((2 * getFont().getSize()) / 5), nHeight
    + ((2 * getFont().getSize()) / 5)));

在构造函数中,但永远不会更改它。这意味着每次布局管理器询问组件它想要的大小时,它总是得到相同的值。

首选解决方案是覆盖getPreferredSize并计算其大小,如果它不复杂或耗时

接下来的问题是你为什么要付出这么多额外的努力。基本上,您应该只允许父JButton提供其首选尺寸并在其周围添加您的要求,或者只使用margins属性甚至是Border

KeyListener似乎也没用,因为这是按钮的默认行为,无论如何,关键绑定将是首选解决方案

答案 1 :(得分:0)

我的第一个想法是它与你的大量CButton实施有关。至少,复制/粘贴代码创建了一个有效的应用程序。这是我书中的一大优点。

当我简单地用nWidth方法替换奇怪的setText()计算时使用:

int nWidth = 100;

点击后尺寸增加并显示“f”,我认为这是你想要的。

所以,我不能真正说出如何计算宽度,但至少你知道在哪里寻找和改变哪条线。