I am writing an application which has a JLayeredPane (call it layers) containing two JPanels in different layers. I override the paintComponent method of the JPanel at the bottom (call it grid_panel) so it paints a grid, and the the paintComponent method of the one at the top (call it circuit_panel) so it paints a circuit.
Here's a summary of the structure:
layers -
|-circuit_panel (on top)
|-grid_panel (at bottom)
I want the grid_panel to stay static, ie, not to do any repaint (except the initial one) since it does not change.
The trouble is, whenever I call circuit_panel.repaint(), grid_panel gets repainted as well! This is a definitely not efficient.
I think this is due to the eager painting behavior of JLayeredPane. Is there a way to disable this feature in JLayeredPane?
In case you're interested to see the above effect, I've written a small demo program:
public class Test2 extends JFrame {
public Test2() {
JLayeredPane layers = new JLayeredPane();
layers.setPreferredSize(new Dimension(600, 400));
MyPanel1 myPanel1 = new MyPanel1();
MyPanel2 myPanel2 = new MyPanel2();
myPanel1.setSize(600, 400);
myPanel2.setSize(600, 400);
myPanel1.setOpaque(false);
myPanel2.setOpaque(false);
myPanel2.addMouseListener(new MyMouseListener(myPanel2));
layers.add(myPanel1, new Integer(100)); // At bottom
layers.add(myPanel2, new Integer(101)); // On top
this.getContentPane().add(layers, BorderLayout.CENTER);
this.setSize(600, 400);
}
class MyPanel1 extends JPanel {
Color getRandomColor() {
int r = (int) (256 * Math.random());
int g = (int) (256 * Math.random());
int b = (int) (256 * Math.random());
return new Color(r, g, b);
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(getRandomColor());
g2d.fillRoundRect(30, 30, 60, 60, 5, 5);
}
}
class MyPanel2 extends JPanel {
Color getRandomColor() {
int r = (int) (256 * Math.random());
int g = (int) (256 * Math.random());
int b = (int) (256 * Math.random());
return new Color(r, g, b);
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(getRandomColor());
g2d.fillRoundRect(45, 45, 75, 75, 5, 5);
}
}
class MyMouseListener extends MouseAdapter {
JPanel panel;
MyMouseListener(JPanel panel) {
this.panel = panel;
}
@Override
public void mouseClicked(MouseEvent e) {
panel.repaint();
}
}
/**
* @param args
*/
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
(new Test2()).setVisible(true);
}
});
}
}
As you found, a BufferedImage
is an effective way to cache complex content for efficient rendering; CellTest
is an example. A flyweight renderer, shown here, is another approach. Finally, I've re-factored your instructive example in a way that may make experimentation easier.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
/** @see https://stackoverflow.com/q/9625495/230513 */
public class LayerDemo extends JFrame {
private static final Dimension d = new Dimension(320, 240);
public LayerDemo() {
JLayeredPane layers = new JLayeredPane();
layers.setPreferredSize(d);
layers.add(new LayerPanel(1 * d.height / 8), 100);
layers.add(new LayerPanel(2 * d.height / 8), 101);
layers.add(new LayerPanel(3 * d.height / 8), 102);
this.add(layers, BorderLayout.CENTER);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.pack();
this.setLocationByPlatform(true);
}
private static class LayerPanel extends JPanel {
private static final Random r = new Random();
private int n;
private Color color = new Color(r.nextInt());
public LayerPanel(int n) {
this.n = n;
this.setOpaque(false);
this.setBounds(n, n, d.width / 2, d.height / 2);
this.addMouseListener(new MouseHandler(this));
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(color);
g2d.fillRoundRect(0, 0, getWidth(), getHeight(), 16, 16);
g2d.setColor(Color.black);
g2d.drawString(String.valueOf(n), 5, getHeight() - 5);
}
private void update() {
color = new Color(r.nextInt());
repaint();
}
}
private static class MouseHandler extends MouseAdapter {
LayerPanel panel;
MouseHandler(LayerPanel panel) {
this.panel = panel;
}
@Override
public void mouseClicked(MouseEvent e) {
panel.update();
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
(new LayerDemo()).setVisible(true);
}
});
}
}
来源:https://stackoverflow.com/questions/9625495/jlayeredpane-and-painting