Java: 如何控制JPanel的长宽比?

3

我有一个JPanel,我希望它保持正方形,但是我希望它的大小可以填充其父级JFrame中的最大空间,但仍然保持正方形,即将JFrame的最短边作为正方形的宽度。

我已经在网上搜索过了,检查了所有布局管理器,但似乎没有一个简单的解决方案来解决这个非常简单的问题。


1
Swing定时器延迟了组件监听器的事件。 - mKorbel
3个回答

11

这里输入图像描述 这里输入图像描述

import java.awt.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;

public class YouAreSoSquare {

    private static JPanel createPanel() {
        // GBL is important for the next step..
        JPanel gui = new JPanel(new GridBagLayout());
        JPanel squareComponent = new JPanel() {
            private static final long serialVersionUID = 1L;
            @Override
            public Dimension getPreferredSize() {
                // Relies on being the only component
                // in a layout that will center it without
                // expanding it to fill all the space.
                Dimension d = this.getParent().getSize();
                int newSize = d.width > d.height ? d.height : d.width;
                newSize = newSize == 0 ? 100 : newSize;
                return new Dimension(newSize, newSize);
            }
        };
        squareComponent.setBackground(Color.RED);
        gui.add(squareComponent);
        return gui;
    }

    public static void main(String[] args) {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (Exception useDefault) {
                }
                JFrame mainFrame = new JFrame("..So Square");
                mainFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                mainFrame.setLocationByPlatform(true);
                mainFrame.add(createPanel());
                mainFrame.pack();
                mainFrame.setMinimumSize(mainFrame.getSize());
                mainFrame.setVisible(true);
            }
        };
        SwingUtilities.invokeLater(r);
    }
}

2
您可以使用 GridBagLayoutComponentListener 来实现。例如:(参考自:https://community.oracle.com/thread/1265752?start=0&tstart=0
public class AspectRatio {
    public static void main(String[] args) {
        final JPanel innerPanel = new JPanel();
        innerPanel.setBackground(Color.YELLOW);

        final JPanel container = new JPanel(new GridBagLayout());
        container.add(innerPanel);
        container.addComponentListener(new ComponentAdapter() {
            @Override
            public void componentResized(ComponentEvent e) {
                resizePreview(innerPanel, container);
            }
        });
        final JFrame frame = new JFrame("AspectRatio");
        frame.getContentPane().add(container);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(600, 600);
        frame.setVisible(true);
    }

    private static void resizePreview(JPanel innerPanel, JPanel container) {
        int w = container.getWidth();
        int h = container.getHeight();
        int size =  Math.min(w, h);
        innerPanel.setPreferredSize(new Dimension(size, size));
        container.revalidate();
    }
}

谢谢,这个完美地解决了问题。我唯一做的修改是将容器JPanel的布局管理器从GridBagLayout替换为FlowLayout,以减少闪烁,因为FlowLayout仅基于其组件的首选大小来确定其大小,这正是您希望的,因为每次调整大小后都会手动设置首选大小。 - ALXGTV

2

这是我提供的最可重用解决方案,它使用了Swing布局概念。因此,您只需复制/粘贴此布局类并在您的Swing容器上设置布局即可使用它。

我很惊讶在网络上没有找到这样的解决方案,所以在这里提供!其中包括一个主方法,它创建了一个类似于Andrew Thompson屏幕截图的JFrame。

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.LayoutManager;

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

//@Slf4j
/**
 * A Swing Layout that will shrink or enlarge keep the content of a container while keeping
 * it's aspect ratio. The caveat is that only a single component is supported or an exception
 * will be thrown.
 * This is the component's {@link Component#getPreferredSize()} method that must return the
 * correct ratio. The preferredSize will not be preserved but the ratio will.
 * 
 * @author @francoismarot
 * @see https://gist.github.com/fmarot/f04346d0e989baef1f56ffd83bbf764d
 */
public class SingleComponentAspectRatioKeeperLayout implements LayoutManager {

    /** Will be used for calculus in case no real component is in the parent */
    private static Component fakeComponent = new JPanel();

    public SingleComponentAspectRatioKeeperLayout() {
        fakeComponent.setPreferredSize(new Dimension(0, 0));
    }

    @Override
    public void addLayoutComponent(String arg0, Component arg1) {
    }

    @Override
    public void layoutContainer(Container parent) {
        Component component = getSingleComponent(parent);
        Insets insets = parent.getInsets();
        int maxWidth = parent.getWidth() - (insets.left + insets.right);
        int maxHeight = parent.getHeight() - (insets.top + insets.bottom);

        Dimension prefferedSize = component.getPreferredSize();
        Dimension targetDim = getScaledDimension(prefferedSize, new Dimension(maxWidth, maxHeight));

        double targetWidth = targetDim.getWidth();
        double targetHeight = targetDim.getHeight();

        double hgap = (maxWidth - targetWidth) / 2;
        double vgap = (maxHeight - targetHeight) / 2;

        // Set the single component's size and position.
        component.setBounds((int) hgap, (int) vgap, (int) targetWidth, (int) targetHeight);
    }

    private Component getSingleComponent(Container parent) {
        int parentComponentCount = parent.getComponentCount();
        if (parentComponentCount > 1) {
            throw new IllegalArgumentException(this.getClass().getSimpleName()
                    + " can not handle more than one component");
        }
        Component comp = (parentComponentCount == 1) ? parent.getComponent(0) : fakeComponent;
        return comp;
    }

    private Dimension getScaledDimension(Dimension imageSize, Dimension boundary) {
        double widthRatio = boundary.getWidth() / imageSize.getWidth();
        double heightRatio = boundary.getHeight() / imageSize.getHeight();
        double ratio = Math.min(widthRatio, heightRatio);
        return new Dimension((int) (imageSize.width * ratio), (int) (imageSize.height * ratio));
    }

    @Override
    public Dimension minimumLayoutSize(Container parent) {
        return preferredLayoutSize(parent);
    }

    @Override
    public Dimension preferredLayoutSize(Container parent) {
        return getSingleComponent(parent).getPreferredSize();
    }

    @Override
    public void removeLayoutComponent(Component parent) {
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JPanel panel = new JPanel(); // the panel we want to keep it's aspect ratio
        panel.setPreferredSize(new Dimension(300, 600));
        panel.setBackground(Color.ORANGE);

        JPanel wrapperPanel = new JPanel(new SingleComponentAspectRatioKeeperLayout());
        wrapperPanel.add(panel);

        frame.getContentPane().add(wrapperPanel);
        frame.setSize(450, 450);

        frame.setVisible(true);
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接