1

2 つの個別の JPanel (または任意の軽量コンポーネント) を相互に重ね、最終的に直接または JLayeredPane のようなものを介して JPanel 内に埋め込む必要があります。したがって、重量のあるコンポーネントやガラス板はありません。下部の JPanel (BackgroundPanel という名前) は、縦横比を維持し、アルファを使用しながら、背景画像をペイントするか、ビデオを再生します。上部のパネル (CompassPanel と呼ばれる) にはアイコンがあり、ユーザーはアイコンを追加、削除、移動できます (作図ライブラリのように、この機能はこの投稿には直接関係ありません)。JNLP アプリとデプロイメント環境の帯域幅の制約により、多くの外部依存関係を追加できません。ただし、アルファとアスペクト比が維持された背景画像とビデオを処理できる軽量の作図ライブラリを誰かが知っていれば、私はゲームです. JLayeredPane が BackgroundPanel を描画する必要があるスペースを占めている

レイアウトマネージャーなしで行くことに関するJAVAチュートリアルを読みました(私が今までやりたかったことではありません、GBLはどこですか!?)が、これらのスケーリング要件と、サイズ変更中などにアイコンが画像の同じ部分にとどまるようにするためです.これを行う別の方法は考えられません。

これがコードです。私はJava 1.7を使用しています。また、これは私の最初のスタック オーバーフローです。優しくしないでください ;-)

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);
    }
}
4

1 に答える 1

1

ドー。私は自分の質問に答えただけです。#setBounds の呼び出しは親コンテナーに対して相対的であるため、x と y のオフセットを適切に考慮する必要があるため、次のことが修正されます。

backgroundImagePanel.setBounds(0, 0, contentBounds.width, contentBounds.height);
compassPanel.setBounds(0, 0, contentBounds.width, contentBounds.height);
于 2013-01-31T23:02:20.627 に答える