JLayeredPane,背景图片+“图标”图层

时间:2013-01-31 22:32:27

标签: java swing diagramming jlayeredpane null-layout-manager

我需要两个独立的JPanel(或任何轻量级组件),并最终嵌入到JPanel中,直接或通过像JLayeredPane这样的东西。因此,没有重型部件或玻璃板。较低的JPanel(名为BackgroundPanel)绘制背景图像或播放视频,同时保持纵横比并使用alpha。上面板(称为CompassPanel)上面有图标,允许用户添加图标,删除它们并移动它们(就像图表库一样,这个功能与这篇文章没有直接关系)。由于我的JNLP应用程序和部署环境的带宽限制,我无法添加许多外部依赖项。但是,如果有人知道一个可以处理alpha& amp的轻量级图表库。纵横比保持背景图像和视频,我是游戏。否则,我不能为我的生活弄清楚为什么在调整大小后分配这个空间: JLayeredPane taking up space that BackgroundPanel should draw on"

我在没有布局管理器的情况下读过JAVA tutorial(不是我想做的事,你在哪里GBL!?),但是对于这些缩放要求并且图标保持在同一部分在调整大小等过程中的图像我想不出另一种方法来做到这一点。

这是代码,我使用的是Java 1.7。另外,这是我的第一个stackoverflow,不要轻柔; - )

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.border.BevelBorder;

public class Panel extends JPanel {

    private static final Logger logger = Logger.getLogger(Panel.class.getName());

    public Panel() throws IOException {

        final BufferedImage backgroundImage = ImageIO.read(new URL(
                "http://www.windpoweringamerica.gov/images/windmaps/us_windmap_80meters_820w.jpg"));
        final Dimension backgroundImageSize = new Dimension(backgroundImage.getWidth(), backgroundImage.getHeight());
        logger.log(Level.INFO, "Image dimensions: {0}", backgroundImageSize);
        setToolTipText("This is the panel");

        final JLayeredPane layeredPane = new JLayeredPane();
        layeredPane.setBorder(BorderFactory.createLineBorder(Color.RED, 10));
        layeredPane.setToolTipText("This is the layered pane!");
        layeredPane.getInsets().set(0, 0, 0, 0);

        final BackgroundPanel backgroundImagePanel = new BackgroundPanel(backgroundImage);
        final CompassPanel compassPanel = new CompassPanel();
        backgroundImagePanel.setToolTipText("You'll probably never see me, I'm in the background, forever beneath the compass panel");
        compassPanel.setToolTipText("I'm the compass panel");

        // Per http://docs.oracle.com/javase/tutorial/uiswing/layout/none.html, for every container w/o a layout manager, I must:
        // 1) Set the container's layout manager to null by calling setLayout(null). -- I do this here
        // 2) Call the Component class's setbounds method for each of the container's children. --- I do this when resizing
        // 3) Call the Component class's repaint method. --- I do this when resizing

        setLayout(null);
        add(layeredPane);
        layeredPane.add(backgroundImagePanel, JLayeredPane.DEFAULT_LAYER);
        layeredPane.add(compassPanel, JLayeredPane.PALETTE_LAYER);

        // Whenever this panel gets resized, make sure the layered pane gets resized to preserve the aspect ratio of the background image
        addComponentListener(new ComponentAdapter() {
            @Override
            public void componentResized(ComponentEvent evt) {
                Dimension availableSize = calculateAvailableSize(Panel.this);
                Rectangle contentBounds = calculateBoundsToFitImage(availableSize, backgroundImageSize);
                // Ok, this is a big deal. Now I know how big everything has to be, lets force it all to be the right size & repaint.
                layeredPane.setBounds(contentBounds);
                backgroundImagePanel.setBounds(contentBounds);
                compassPanel.setBounds(contentBounds);

                Panel.this.repaint();
                logger.info(String.format("Panel size: %s. Available size: %s. Content Bounds: %s", getSize(), availableSize, contentBounds));
            }
        });
    }

    /**
     * Paints the constant fitted aspect-ratio background image with an alpha of 0.5
     */
    private static class BackgroundPanel extends JPanel {

        private static final Logger logger = Logger.getLogger(BackgroundPanel.class.getName());
        private final AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.5f);
        private final BufferedImage backgroundImage;

        BackgroundPanel(BufferedImage backgroundImage) {
            setLayout(null);
            this.backgroundImage = backgroundImage;
        }
        private Dimension lastPaintedDimensions = null;

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            final Dimension size = getSize();
            if (lastPaintedDimensions == null || !size.equals(lastPaintedDimensions)) {
                logger.log(Level.INFO, String.format("Painting background on %d x %d", size.width, size.height));
            }
            final Image paintMe = backgroundImage.getScaledInstance(size.width, size.height, Image.SCALE_SMOOTH);
            final Graphics2D g2 = (Graphics2D) g.create();
            g2.drawImage(paintMe, 0, 0, this);
            g2.setColor(Color.BLUE);
            g2.dispose();
            lastPaintedDimensions = size;
        }
    };

    private static class CompassPanel extends JPanel {

        final List<Compass> compassLabels = new ArrayList<>();

        CompassPanel() {
            setLayout(null);
            setOpaque(false);
            setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
        }
    }

    private static class Compass extends JLabel {

        private static final BufferedImage compassImage;

        static {
            try {
                compassImage = ImageIO.read(new URL("http://cdn1.iconfinder.com/data/icons/gur-project-1/32/1_7.png"));
            } catch (IOException ex) {
                throw new RuntimeException("Failed to read compass image", ex);
            }
        }
        final float xPercent, yPercent;

        public Compass(float xPercent, float yPercent) {
            this.xPercent = xPercent;
            this.yPercent = yPercent;
            setIcon(new ImageIcon(compassImage));
            setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));
            setOpaque(true);
            setCursor(Cursor.getDefaultCursor());
        }
    }

    public static void main(String[] args) throws IOException {
        final JFrame frame = new JFrame("Hello Stackoverflowwwwwww! Here is a Dynamic Layered Pane Question.");
        frame.setLayout(null);
        frame.setContentPane(new Panel());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(800, 600);
        frame.setVisible(true);
    }

    private static Dimension calculateAvailableSize(final JComponent component) {

        int availableHeight = component.getSize().height;
        int availableWidth = component.getSize().width;

        final Insets insets = component.getInsets();

        availableHeight -= insets.top;
        availableHeight -= insets.bottom;

        availableWidth -= insets.left;
        availableWidth -= insets.right;

        if (component.getBorder() != null) {
            Insets borderInsets = component.getBorder().getBorderInsets(component);
            if (borderInsets != null) {
                availableHeight -= borderInsets.top;
                availableHeight -= borderInsets.bottom;

                availableWidth -= borderInsets.left;
                availableWidth -= borderInsets.right;
            }
        }

        return new Dimension(availableWidth, availableHeight);
    }

    private static Rectangle calculateBoundsToFitImage(Dimension parentSize, Dimension imageSize) {
        final double scaleFactor;
        final int xOffset, yOffset, scaledHeight, scaledWidth;
        {
            final double xScaleFactor = (double) parentSize.width / imageSize.width;
            final double yScaleFactor = (double) parentSize.height / imageSize.height;
            scaleFactor = xScaleFactor > yScaleFactor ? yScaleFactor : xScaleFactor;
            scaledHeight = (int) Math.round(scaleFactor * imageSize.height);
            scaledWidth = (int) Math.round(scaleFactor * imageSize.width);
        }

        xOffset = (int) ((parentSize.width - scaledWidth) / 2.0);
        yOffset = (int) ((parentSize.height - scaledHeight) / 2.0);
        return new Rectangle(xOffset, yOffset, scaledWidth, scaledHeight);
    }
}

1 个答案:

答案 0 :(得分:1)

卫生署。我刚回答了自己的问题。调用#setBounds是相对于父容器的,因此x&amp; y偏移需要正确计算,所以这解决了:

backgroundImagePanel.setBounds(0, 0, contentBounds.width, contentBounds.height);
compassPanel.setBounds(0, 0, contentBounds.width, contentBounds.height);