如何让 JComponents 在缩小后始终居中?

How to keep JComponents always centered even after being shrunk?

Objective

我正在制作一个带有三个 JComponent 的 JPanel:一个广泛的自定义 JComponent 和两个自定义的 AbstractButtons,后者又从 JComponent 扩展而来。

我想用 2 行和 2 列来组织我的 JPanel,宽 JComponent 跨越两列。 AbstractButtons 周围有很多 space。这是我绘制的我想要实现的图表:

问题

基本上,当用户单击其中一个 AbstractButton 时,它会缩小。我希望它们即使在被点击时也始终完美地居中于它们的单元格中。但是,当我使用 GridBagLayout 时,我的代码并非如此;按钮变得偏离中心,并且它们的左上角保持在原来的位置。

代码

这就是下面的代码将显示的内容(红色按钮现在会在按下后缩小):

import java.awt.*;
import java.awt.event.*;

import javax.swing.*;
import javax.swing.border.*;

public class Example
{
    public static void main(String[] args)
    {
        // Instantiate components needed.
        JFrame frame = new JFrame();
        JPanel panel = new JPanel();
        frame.add(panel);
        Button b1 = new Button();
        Button b2 = new Button();
        WideComponent w = new WideComponent();

        // give margins to each component
        EmptyBorder padding = new EmptyBorder(20, 20, 20, 20);
        b1.setBorder(padding);
        b2.setBorder(padding);
        w.setBorder(padding);

        // Add stuff to panel
        panel.add(b1);
        panel.add(b2);
        panel.add(w);

        // Add panel to frame, show frame
        frame.add(panel);
        frame.setVisible(true);
        frame.pack();
    }
}

class Button extends AbstractButton implements MouseListener
{   
    final int WIDTH = 175;
    final int HEIGHT = 75; 
    boolean mousePressed;

    public Button() 
    {
        addMouseListener(this);
    }

    // draw method
    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        g.setColor(Color.RED);
        if (!mousePressed)
            g.fillRect(0, 0, WIDTH, HEIGHT);
        else
        {
            g.fillRect(0, 0, WIDTH - 30, HEIGHT - 30);
            g.setColor(Color.YELLOW);
        }
    }

    // MouseListener interface implementation
    public void mousePressed(MouseEvent me) { mousePressed = true; repaint(); }
    public void mouseReleased(MouseEvent me) { mousePressed = false; repaint(); }
    public void mouseClicked(MouseEvent e) {}
    public void mouseEntered(MouseEvent e) {}
    public void mouseExited(MouseEvent e) {}

    // JComponent sizing methods
    public Dimension getPreferredSize()
    {
        return new Dimension(WIDTH, HEIGHT);
    }
    public Dimension getMaximumSize() { return getPreferredSize(); }
    public Dimension getMinimumSize() { return getPreferredSize(); }    



}

class WideComponent extends JComponent
{
    final int WIDTH = 500;
    final int HEIGHT = 150; 

    // drawing method
    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        g.setColor(Color.BLUE);
        g.fillRect(0, 0, WIDTH, HEIGHT);
    }

    // JComponent sizing methods
    public Dimension getPreferredSize()
    {
        return new Dimension(WIDTH, HEIGHT);
    }
    public Dimension getMaximumSize() { return getPreferredSize(); }
    public Dimension getMinimumSize() { return getPreferredSize(); }    
}

也许您可以尝试更改按钮的边界,具体取决于它是缩小尺寸还是完整尺寸?仅当您的主 window 不可调整大小时才如此。

Component.setBounds(int x, int y, int width, int height)

您的示例代码与您绘制的图片不匹配,并且没有使用 GridBagLayout。 SSCCE 应该用您测试的代码复制问题。

问题出在自定义对象的实现上。您真正需要的是 class 中的自定义 属性,它反映了组件的当前状态。类似于:

public void setLargeSize(Boolean largeSize)
{
    this.largeSize = largeSize;
    revalidate();
    repaint();
}

然后需要根据这个变量修改getPreferredSize()方法return组件的大小:

@Override
public Dimension getPreferredSize()
{
    if (largeSize)
        return new Dimension(WIDTH, HEIGHT);
    else
        return new Dimension(WIDTH - 30, HEIGHT - 30);
}

现在布局管理器可以根据组件的当前状态完成它的工作。

你也可以更改你的绘画代码。绘画代码不需要检查按下状态,因为布局管理器已经考虑到了这一点。实际上,我认为您甚至不需要重写 paintComponent()。只需设置按钮的一些属性:

setBorderPainted( false );
setContentAreaPainted( false );
setFocusPainted( false );

然后您可以在 setLargeSize(...) 方法中设置背景颜色。

最后,MouseListener 将使用适当的参数调用 setLargeSize(...) 方法。

所以现在您需要做的就是确保正确设置约束,以便组件在其单元格中居中。您可能需要设置组件的对齐方式 x/y。