Java JLayeredPane,背景图像+&引用;图标";层

Java JLayeredPane,背景图像+&引用;图标";层,java,swing,diagramming,jlayeredpane,null-layout-manager,Java,Swing,Diagramming,Jlayeredpane,Null Layout Manager,我需要两个独立的JPanel(或任何轻量级组件)相互叠加,最终直接或通过类似于JLayeredPane的东西嵌入到JPanel中。因此,没有重型部件或玻璃窗格玻璃。较低的JPanel(命名为BackgroundPanel)绘制背景图像或播放视频,同时保持纵横比并使用alpha。上面的面板(称为CompassPanel)上有图标,允许用户添加、删除和移动图标(就像一个图表库,这个功能与本文没有直接关系)。由于JNLP应用程序和部署环境的带宽限制,我无法添加许多外部依赖项。然而,如果有人知道一个轻量

我需要两个独立的JPanel(或任何轻量级组件)相互叠加,最终直接或通过类似于JLayeredPane的东西嵌入到JPanel中。因此,没有重型部件或玻璃窗格玻璃。较低的JPanel(命名为BackgroundPanel)绘制背景图像或播放视频,同时保持纵横比并使用alpha。上面的面板(称为CompassPanel)上有图标,允许用户添加、删除和移动图标(就像一个图表库,这个功能与本文没有直接关系)。由于JNLP应用程序和部署环境的带宽限制,我无法添加许多外部依赖项。然而,如果有人知道一个轻量级的图表库,可以处理阿尔法和纵横比维护的背景图像和视频,我很高兴。否则,我一辈子都无法弄清楚为什么在调整大小后会分配此空间:

我已经阅读了正在进行的没有布局管理器(不是我想做的事情,你在哪里GBL!?),但是对于这些缩放要求和在调整大小期间图标停留在图像的同一部分等,我想不出其他方法来做这件事

下面是代码,我使用的是Java1.7。还有,这是我的第一次,别客气;-)

导入java.awt.AlphaComposite;
导入java.awt.Color;
导入java.awt.Composite;
导入java.awt.Cursor;
导入java.awt.Dimension;
导入java.awt.Graphics;
导入java.awt.Graphics2D;
导入java.awt.Image;
导入java.awt.Insets;
导入java.awt.Rectangle;
导入java.awt.event.ComponentAdapter;
导入java.awt.event.ComponentEvent;
导入java.awt.image.buffereImage;
导入java.io.IOException;
导入java.net.URL;
导入java.util.ArrayList;
导入java.util.List;
导入java.util.logging.Level;
导入java.util.logging.Logger;
导入javax.imageio.imageio;
导入javax.swing.BorderFactory;
导入javax.swing.ImageIcon;
导入javax.swing.JComponent;
导入javax.swing.JFrame;
导入javax.swing.JLabel;
导入javax.swing.JLayeredPane;
导入javax.swing.JPanel;
导入javax.swing.border.BevelBorder;
公共类面板扩展了JPanel{
私有静态最终记录器Logger=Logger.getLogger(Panel.class.getName());
公共面板()引发异常{
final BuffereImage backgroundImage=ImageIO.read(新URL(
"http://www.windpoweringamerica.gov/images/windmaps/us_windmap_80meters_820w.jpg"));
最终维度backgroundImageSize=新维度(backgroundImage.getWidth(),backgroundImage.getHeight());
log(Level.INFO,“图像尺寸:{0}”,backgroundImageSize);
setToolTipText(“这是面板”);
最终JLayeredPane layeredPane=新JLayeredPane();
layeredPane.setBorder(BorderFactory.createLineBorder(Color.RED,10));
setToolTipText(“这是分层窗格!”);
layeredPane.getInsets().set(0,0,0,0);
最终背景面板背景图像面板=新背景面板(背景图像);
最终CompassPanel CompassPanel=新CompassPanel();
backgroundImagePanel.setToolTipText(“你可能永远不会看到我,我在背景中,永远在指南针面板下”);
compassPanel.setToolTipText(“我是指南针面板”);
//http://docs.oracle.com/javase/tutorial/uiswing/layout/none.html,对于没有布局管理器的每个容器,我必须:
//1)通过调用setLayout(null)将容器的布局管理器设置为null
//2)为容器的每个子级调用组件类的setbounds方法。--我在调整大小时这样做
//3)调用组件类的重绘方法。--我在调整大小时这样做
setLayout(空);
添加(分层窗格);
layeredPane.add(backgroundImagePanel,JLayeredPane.DEFAULT_LAYER);
layeredPane.add(compassPanel、JLayeredPane.PALETTE\u层);
//每当调整此面板的大小时,请确保调整分层窗格的大小,以保留背景图像的纵横比
addComponentListener(新的ComponentAdapter(){
@凌驾
public void componentResistized(ComponentEvent evt){
维度可用大小=计算可用大小(Panel.this);
矩形contentBounds=calculateBoundsToFitImage(可用大小,背景图像大小);
//好的,这是一个大问题。现在我知道每件事都有多大了,让我们强迫它都是正确的大小和重新油漆。
layeredPane.setBounds(contentBounds);
backgroundImagePanel.setBounds(contentBounds);
compassPanel.setBounds(contentBounds);
Panel.this.repaint();
logger.info(String.format(“面板大小:%s.可用大小:%s.内容边界:%s”,getSize(),availableSize,contentBounds));
}
});
}
/**
*使用0.5的alpha绘制恒定拟合纵横比背景图像
*/
私有静态类BackgroundPanel扩展了JPanel{
私有静态最终记录器=Logger.getLogger(BackgroundPanel.class.getName());
私有最终AlphaComposite ac=AlphaComposite.getInstance(AlphaComposite.SRC_,0.5f);
私有最终缓冲区图像背景图像;
背景面板(BuffereImage背景图像){
setLayout(空);
this.backgroundImage=backgroundImage;
}
私有维度lastPaintedDimensions=null;
@凌驾
受保护组件(图形g){
超级组件(g);
最终尺寸=getSize();
如果(lastPaintedDimensions==null | |!size.equals(lastPaintedDimensions)){
logger.log(Level.INFO,String.format(“在%d x%d上绘制背景”,size.width,size.height));
}
最终图像
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);
    }
}
backgroundImagePanel.setBounds(0, 0, contentBounds.width, contentBounds.height);
compassPanel.setBounds(0, 0, contentBounds.width, contentBounds.height);