Мне нужно 2 отдельных JPanel (или любых легких компонента) друг над другом и, в конечном итоге, встроенных в JPanel, либо напрямую, либо через что-то вроде JLayeredPane. Таким образом, никаких тяжелых компонентов или стекла. Нижняя панель JPanel (с именем BackgroundPanel) рисует фоновое изображение или воспроизводит видео, сохраняя соотношение сторон и используя альфа-канал. На верхней панели (называемой CompassPanel) есть значки, и пользователь может добавлять значки, удалять их и перемещать (подобно библиотеке диаграмм, эта функциональность не имеет прямого отношения к этому посту). Я не могу добавить много внешних зависимостей из-за ограничений пропускной способности моего приложения JNLP и среды развертывания. Однако, если кто-то знает об облегченной библиотеке диаграмм, которая может обрабатывать фоновые изображения и видео с альфа-каналом и соотношением сторон, я в игре. В противном случае я не могу понять, почему это пространство выделяется после изменения размера:
Я прочитал руководство по Java по работе без менеджера компоновки. (не то, что я когда-либо хотел сделать, где ты, ГБЛ !?), но для этих требований масштабирования и того, чтобы значки оставались на одной и той же части изображения во время изменения размера и т. д. Я не могу придумать другого способа сделать это.
Вот код, я использую 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);
}
}
paintComponent
попытается оптимизировать свои перерисовки, рисуя только те части экрана, которые, по его мнению, необходимо обновить. Вы можете использовать эту информацию, чтобы определить, следует ли вам красить определенные части вашего компонента, особенно если у вас сложная краска. Дополнительные сведения см. в разделе Рисование в AWT и Swing. - person MadProgrammer   schedule 01.02.2013