【问题标题】:Choosing a Layout Manager - Java选择布局管理器 - Java
【发布时间】:2016-06-08 15:57:34
【问题描述】:

我找不到合适的布局管理器。我在 JPanel 中有一些图像,它们的大小都不同,所以我想使用 Flow Layout 让经理处理它们。经理尽可能填满第一行,然后转移到另一行。这一切都很好,但我想要的是在第二个“扭曲”处停下来。我只想显示 2 行图像,如果您想查看更多图像,则必须单击 JButton 来加载其他图像。实际上 FlowLayout 一直插入第三行,并且图像被切成了一半,因为面板不够“高”。有小费吗? 我已经尝试过 Flow Layout 和 Mig Layout 没有成功。

【问题讨论】:

  • GridBag 是我用过的一个不错的。
  • 您必须确定要显示的图像数量,例如一行 3 张图像和 JPanel 上的两行图像,然后仅显示该数量。 FlowLayout 有效,GridLayout 可能看起来更好。
  • 不显示溢出组件是不寻常的 - 如果没有任何效果,您可以随时实现自己的 LayoutManager
  • GridBagGridLayout 会有所帮助。
  • Grid/GridBag 布局 afaik 更糟糕。如果我有一排长的水平图像而另一排是垂直的,那么输出应该很难看。 @wero 我想我会试试你说的。您是否建议针对此问题实施 LayoutManager 或扩展 FlowLayout? (创建一个答案,这样我就可以投票给你)

标签: java swing layout miglayout flowlayout


【解决方案1】:

将 FlowLayout(outerPanel) 与 GridBagLayout(innerPanel) 结合起来 你可以自己定义行,然后在上面放一个 JScrollPane,这样它就不会剪切你的图片,你仍然可以看到它们的全尺寸(我的建议)

【讨论】:

  • 我不确定我是否理解你,但你的实现中的问题是知道我什么时候必须跳转到下一行。我不知道图像的大小(之前),所以我不知道该行何时已满。当然我可以得到每个图像的宽度,并计算它。但我一直在寻找可以为我处理的布局管理器。
【解决方案2】:

我忍不住想解决这个问题。我曾希望使用 FlowLayouts,但最后我只使用了 GridBagLayouts:每行图像一个,整个容器一个。我可能在灵活性方面过度设计,但我想这样的东西将来可能对我自己或其他人有用。

本质上,每次容器改变大小,或添加/删除图像,或任何视觉属性改变时,都会调用updateLayout(),它会从头开始重建所有 GridBagLayout 面板。我确信有一些方法可以提高效率,而且我确信可以从 scatch 编写一个 LayoutManager2 实现来完成这项工作,但这对我来说表现相当不错。

import java.util.List;
import java.util.ArrayList;
import java.util.Objects;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Rectangle;

import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;

import java.awt.image.BufferedImage;

import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class ImagePanel
extends JPanel {
    private static final long serialVersionUID = 1;

    /** @serial */
    private final List<Icon> images = new ArrayList<>();

    /** @serial */
    private final List<Icon> scaledImages = new ArrayList<>();

    /** @serial */
    private int maxRowCount = 2;

    /** @serial */
    private int hgap = 6;

    /** @serial */
    private int vgap = 6;

    /** @serial */
    private int maxImageWidth = 200;

    /** @serial */
    private int maxImageHeight = 200;

    public ImagePanel() {
        setLayout(new GridBagLayout());
        addComponentListener(new ComponentAdapter() {
            @Override
            public void componentResized(ComponentEvent event) {
                updateLayout();
            }
        });
    }

    @Override
    public void addNotify() {
        super.addNotify();
        updateLayout();
    }

    @Override
    public Dimension getPreferredSize() {
        Rectangle screen = findGraphicsConfiguration().getBounds();

        Dimension size = new Dimension();
        Dimension row = new Dimension();
        int rowsComputed = 0;
        int gap = 0;
        for (Icon image : scaledImages) {
            if (row.width > 0 &&
                row.width + gap + image.getIconWidth() > screen.width) {

                if (++rowsComputed >= maxRowCount) {
                    break;
                }

                size.width = Math.max(size.width, row.width);
                size.height += (size.height > 0 ? vgap : 0) + row.height;
                row.setSize(0, 0);
                gap = 0;
            }
            row.width += gap + image.getIconWidth();
            row.height = Math.max(row.height, image.getIconHeight());
            gap = hgap;
        }

        size.width = Math.max(size.width, row.width);
        size.height += (size.height > 0 ? vgap : 0) + row.height;

        return size;
    }

    private void updateLayout() {
        int width = getWidth();
        if (width == 0) {
            return;
        }

        for (Component rowContainer : getComponents()) {
            ((JComponent) rowContainer).removeAll();
        }

        GridBagConstraints rowConstraints = new GridBagConstraints();
        rowConstraints.gridwidth = GridBagConstraints.REMAINDER;
        rowConstraints.weightx = 1;
        rowConstraints.anchor = GridBagConstraints.FIRST_LINE_START;

        int row = -1;
        int rowWidth = 0;
        GridBagConstraints gbc = new GridBagConstraints();
        for (Icon image : scaledImages) {
            JComponent rowContainer = (row >= 0 && row < getComponentCount() ?
                (JComponent) getComponent(row) : null);
            int gap = (rowWidth > 0 ? hgap : 0);

            if (rowContainer == null ||
                rowWidth + gap + image.getIconWidth() > width) {

                if (++row >= maxRowCount) {
                    break;
                }

                gap = 0;
                rowWidth = 0;

                if (row < getComponentCount()) {
                    rowContainer = (JComponent) getComponent(row);
                } else {
                    rowContainer = new JPanel(new GridBagLayout());
                    add(rowContainer, rowConstraints);
                }
                rowConstraints.insets.top = vgap;
            }

            gbc.insets.left = gap;
            JComponent imageContainer = new JLabel(image);
            rowContainer.add(imageContainer, gbc);

            rowWidth += gap + image.getIconWidth();
        }

        for (int i = getComponentCount() - 1; i >= maxRowCount; i--) {
            remove(i);
        }
    }

    private GraphicsConfiguration findGraphicsConfiguration() {
        GraphicsConfiguration config = getGraphicsConfiguration();
        if (config == null) {
            GraphicsEnvironment env =
                GraphicsEnvironment.getLocalGraphicsEnvironment();
            config = env.getDefaultScreenDevice().getDefaultConfiguration();
        }
        return config;
    }

    private Icon scale(Icon image) {
        int imageWidth = image.getIconWidth();
        int imageHeight = image.getIconHeight();
        if (imageWidth > maxImageWidth || imageHeight > maxImageHeight) {
            float scale = Math.min((float) maxImageWidth / imageWidth,
                                   (float) maxImageHeight / imageHeight);
            if (scale < 1) {
                GraphicsConfiguration config = findGraphicsConfiguration();
                BufferedImage scaledImage = config.createCompatibleImage(
                    (int) (imageWidth * scale),
                    (int) (imageHeight * scale));
                Graphics2D g = scaledImage.createGraphics();
                g.scale(scale, scale);
                image.paintIcon(this, g, 0, 0);
                g.dispose();

                image = new ImageIcon(scaledImage);
            }
        }

        return image;
    }

    public List<Icon> getImages() {
        return new ArrayList<>(images);
    }

    public void clearImages() {
        images.clear();
        updateLayout();
        revalidate();
    }

    public void addImage(Icon image) {
        Objects.requireNonNull(image, "Image cannot be null");
        images.add(image);
        scaledImages.add(scale(image));
        updateLayout();
        revalidate();
    }

    public void removeImage(Icon image) {
        int index = images.indexOf(image);
        if (index >= 0) {
            removeImage(index);
        }
    }

    public void removeImage(int index) {
        images.remove(index);
        scaledImages.remove(index);
        updateLayout();
        revalidate();
    }

    public int getHgap() {
        return hgap;
    }

    public void setHgap(int gap) {
        if (gap < 0) {
            throw new IllegalArgumentException("Gap must be at least zero");
        }

        int old = this.hgap;
        this.hgap = gap;

        if (old != gap) {
            updateLayout();
            revalidate();
        }

        firePropertyChange("hgap", old, gap);
    }

    public int getVgap() {
        return vgap;
    }

    public void setVgap(int gap) {
        if (gap < 0) {
            throw new IllegalArgumentException("Gap must be at least zero");
        }

        int old = this.vgap;
        this.vgap = gap;

        if (old != gap) {
            updateLayout();
            revalidate();
        }

        firePropertyChange("vgap", old, gap);
    }

    public int getMaxRowCount() {
        return maxRowCount;
    }

    public void setMaxRowCount(int count) {
        if (count < 0) {
            throw new IllegalArgumentException("Count must be at least zero");
        }

        int old = this.maxRowCount;
        this.maxRowCount = count;

        if (old != count) {
            updateLayout();
            revalidate();
        }

        firePropertyChange("maxRowCount", old, count);
    }

    public int getMaxImageWidth() {
        return maxImageWidth;
    }

    private void recomputeScaledImages() {
        scaledImages.clear();
        for (Icon image : images) {
            scaledImages.add(scale(image));
        }
    }

    public void setMaxImageWidth(int width) {
        if (width <= 0) {
            throw new IllegalArgumentException("Width must be positive");
        }

        int old = this.maxImageWidth;
        this.maxImageWidth = width;

        if (old != width) {
            recomputeScaledImages();
            updateLayout();
            revalidate();
        }

        firePropertyChange("maxImageWidth", old, width);
    }

    public int getMaxImageHeight() {
        return maxImageHeight;
    }

    public void setMaxImageHeight(int height) {
        if (height <= 0) {
            throw new IllegalArgumentException("Height must be positive");
        }

        int old = this.maxImageHeight;
        this.maxImageHeight = height;

        if (old != height) {
            recomputeScaledImages();
            updateLayout();
            revalidate();
        }

        firePropertyChange("maxImageHeight", old, height);
    }

    public static void main(final String[] args)
    throws java.io.IOException {
        if (args.length == 0) {
            System.err.println("Usage: java " + ImagePanel.class.getName()
                + " <directory> | <url1> <url2> ...");
            System.exit(2);
        }

        final List<java.net.URL> urls;
        if (args.length == 1 && !args[0].contains(":")) {
            urls = new ArrayList<>();
            try (java.nio.file.DirectoryStream<java.nio.file.Path> dir =
                java.nio.file.Files.newDirectoryStream(
                    java.nio.file.Paths.get(args[0]))) {

                for (java.nio.file.Path file : dir) {
                    urls.add(file.toUri().toURL());
                }
            }
        } else {
            urls = new ArrayList<>(args.length);
            for (String url : args) {
                urls.add(new java.net.URL(url));
            }
        }

        java.awt.EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                ImagePanel imagePanel = new ImagePanel();
                for (java.net.URL url : urls) {
                    imagePanel.addImage(new ImageIcon(url));
                }

                javax.swing.JFrame frame =
                    new javax.swing.JFrame("ImagePanel");
                frame.setDefaultCloseOperation(
                    javax.swing.JFrame.EXIT_ON_CLOSE);

                JPanel panel = new JPanel(new GridBagLayout());
                GridBagConstraints gbc = new GridBagConstraints();
                gbc.fill = GridBagConstraints.HORIZONTAL;
                gbc.anchor = GridBagConstraints.FIRST_LINE_START;
                gbc.weightx = 1;
                gbc.weighty = 1;
                panel.add(imagePanel, gbc);

                frame.getContentPane().add(panel);
                frame.pack();
                frame.setLocationByPlatform(true);
                frame.setVisible(true);
            }
        });
    }
}

【讨论】:

  • 我会在明天早上阅读代码。但是我已经尝试了几次,它会调整我的图像大小并用它们填充网格,就我所见而言。所有图像的宽度都相同,因此不需要使用 FlowLayout,这是有意的吗?还是我做错了什么?如果您可以通过邮件与我联系以获取有关代码的更多信息并找到解决方案,我将很高兴。 umuril.lyerood@gmail.com
  • 是的,我正在测试不同尺寸的图像,所以我添加了 maxImageWidth 和 maxImageHeight 属性。您可以通过删除 scaledImages 字段并删除代码中对其的所有引用来轻松删除它,除了在 getPreferredSizeupdateLayout 中的循环中,您可以简单地将 scaledImages 替换为 images
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多