JPanel 的位置被 BufferStrategy 破坏了
JPanel position whacked by BufferStrategy
我有一个 JFrame,我向其中添加了一个 JPanel。我正在做一些动画,所以我实现了一个用于渲染的 BufferStrategy。我还有一个渲染循环以在 运行ning 时保持渲染。
如果我 运行 程序正常,JPanel 会正确呈现。当然,那就没有动画了。如果我 运行 使用循环和 hte BufferedStrategy,JPanel 将扩展到应用程序的完整大小,并位于 JFrame 的标题栏下方。我找不到发生这种情况的充分理由,但这令人沮丧,因为我需要做一些精确的绘图,并且不能将其中的一些隐藏在标题栏下方。
我想这是因为我没有调用 super.paintComponent()
,但我真的不应该调用它,因为我是自己渲染的,而不是在正常的 Swing 管道中。
我是否需要进行一些 API 调用才能使 JPanel 在我的渲染调用中正确定位?
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import javax.swing.JFrame;
public class MainFrame extends JFrame implements Runnable {
private static final long serialVersionUID = 2190062312369662956L;
protected ViewPanel _viewPanel = null;
public MainFrame() {
setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
createGui();
}
protected void createGui() {
setSize( 600, 400 );
setTitle( "Exact Positioning" );
setVisible( true );
setResizable( false );
_viewPanel = new ViewPanel();
_viewPanel.init();
// the layout type shouldn't matter since this is the only component in the frame
add( _viewPanel );
}
@Override
public void run() {
// setup
this.createBufferStrategy( 2 );
BufferStrategy buffStrategy = this.getBufferStrategy();
// render loop
while( true ) {
Graphics g = null;
try {
g = buffStrategy.getDrawGraphics();
_viewPanel.render( g );
} finally {
g.dispose();
}
buffStrategy.show();
// pause a tad
try {
Thread.sleep( 500 );
} catch (InterruptedException e) {
// Required catch block
e.printStackTrace();
} catch (Exception e) {
System.out.println( "Sorry, don't know what happened: " + e.toString() );
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Thread t1 = new Thread(new MainFrame());
t1.start();
// if I start the app this way, the JPanel draws correctly
// MainFrame a = new MainFrame();
}
}
JPanel:
import java.awt.Color;
import java.awt.Graphics;
import java.util.Random;
import javax.swing.JPanel;
public class ViewPanel extends JPanel {
private static int APP_WIDTH = 600;
private static int APP_HEIGHT = 400;
private static final long serialVersionUID = -8019663913250286271L;
public ViewPanel() {
setBackground(Color.GRAY);
}
public void init() {
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent( g );
render( g );
}
// Where I do the drawing. It's called from the rendering loop in the JFrame
public void render( Graphics g ) {
// refresh the background since we're not relying on paintComponent all the time
Color bgc = getBackground();
g.setColor( bgc );
g.fillRect( 0, 0, APP_WIDTH, APP_HEIGHT );
// just paint a moving box
drawBox( g );
// draw a line to prove correctness. In the loop, you can see part of this line is hidden
// underneath the title bar
g.setColor( Color.red );
g.drawLine(0, 0, APP_WIDTH, APP_HEIGHT);
}
protected void drawBox( Graphics g ) {
// get a random color
Random ran = new Random();
int red = ran.nextInt( 255 );
int grn = ran.nextInt( 255 );
int ble = ran.nextInt( 255 );
Color colour = new Color( red, grn, ble );
g.setColor( colour );
// get a random position
int x = ran.nextInt( APP_WIDTH - 50);
int y = ran.nextInt( APP_HEIGHT - 50);
// draw it
g.fillRect( x, y, 50, 50 );
}
}
Swing 使用它自己的呈现引擎,这是一种被动实现。你正试图用你自己的、活跃的、渲染引擎来规避这个问题,这两者将会发生冲突。
因为BufferStrategy
属于JFrame
,它是在JFrame
的范围内创建的,所以0x0
将是JFrame
的左上角位置,而不是JPanel
.
Swing 的呈现引擎会自动为您完成此转换。
您有两个基本选择。
- 不要基于
JPanel
进行渲染,只需使用一个“渲染”class 即可独立完成此操作(并使用 Canvas
而不是 JFrame
作为 BufferStrategy
) 的基础
- 使用 Swing
Timer
作为主要渲染引擎
基于 Swing Timer
的示例...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new ViewPanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class ViewPanel extends JPanel {
private static int APP_WIDTH = 600;
private static int APP_HEIGHT = 400;
private static final long serialVersionUID = -8019663913250286271L;
public ViewPanel() {
setBackground(Color.GRAY);
Timer timer = new Timer(5, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
repaint();
}
});
timer.start();
}
public void init() {
}
@Override
public Dimension getPreferredSize() {
return new Dimension(APP_HEIGHT, APP_HEIGHT);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
render(g);
}
// Where I do the drawing. It's called from the rendering loop in the JFrame
public void render(Graphics g) {
// refresh the background since we're not relying on paintComponent all the time
Color bgc = getBackground();
g.setColor(bgc);
g.fillRect(0, 0, APP_WIDTH, APP_HEIGHT);
// just paint a moving box
drawBox(g);
// draw a line to prove correctness. In the loop, you can see part of this line is hidden
// underneath the title bar
g.setColor(Color.red);
g.drawLine(0, 0, APP_WIDTH, APP_HEIGHT);
}
protected void drawBox(Graphics g) {
// get a random color
Random ran = new Random();
int red = ran.nextInt(255);
int grn = ran.nextInt(255);
int ble = ran.nextInt(255);
Color colour = new Color(red, grn, ble);
g.setColor(colour);
// get a random position
int x = ran.nextInt(APP_WIDTH - 50);
int y = ran.nextInt(APP_HEIGHT - 50);
// draw it
g.fillRect(x, y, 50, 50);
}
}
}
我有一个 JFrame,我向其中添加了一个 JPanel。我正在做一些动画,所以我实现了一个用于渲染的 BufferStrategy。我还有一个渲染循环以在 运行ning 时保持渲染。
如果我 运行 程序正常,JPanel 会正确呈现。当然,那就没有动画了。如果我 运行 使用循环和 hte BufferedStrategy,JPanel 将扩展到应用程序的完整大小,并位于 JFrame 的标题栏下方。我找不到发生这种情况的充分理由,但这令人沮丧,因为我需要做一些精确的绘图,并且不能将其中的一些隐藏在标题栏下方。
我想这是因为我没有调用 super.paintComponent()
,但我真的不应该调用它,因为我是自己渲染的,而不是在正常的 Swing 管道中。
我是否需要进行一些 API 调用才能使 JPanel 在我的渲染调用中正确定位?
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import javax.swing.JFrame;
public class MainFrame extends JFrame implements Runnable {
private static final long serialVersionUID = 2190062312369662956L;
protected ViewPanel _viewPanel = null;
public MainFrame() {
setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
createGui();
}
protected void createGui() {
setSize( 600, 400 );
setTitle( "Exact Positioning" );
setVisible( true );
setResizable( false );
_viewPanel = new ViewPanel();
_viewPanel.init();
// the layout type shouldn't matter since this is the only component in the frame
add( _viewPanel );
}
@Override
public void run() {
// setup
this.createBufferStrategy( 2 );
BufferStrategy buffStrategy = this.getBufferStrategy();
// render loop
while( true ) {
Graphics g = null;
try {
g = buffStrategy.getDrawGraphics();
_viewPanel.render( g );
} finally {
g.dispose();
}
buffStrategy.show();
// pause a tad
try {
Thread.sleep( 500 );
} catch (InterruptedException e) {
// Required catch block
e.printStackTrace();
} catch (Exception e) {
System.out.println( "Sorry, don't know what happened: " + e.toString() );
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Thread t1 = new Thread(new MainFrame());
t1.start();
// if I start the app this way, the JPanel draws correctly
// MainFrame a = new MainFrame();
}
}
JPanel:
import java.awt.Color;
import java.awt.Graphics;
import java.util.Random;
import javax.swing.JPanel;
public class ViewPanel extends JPanel {
private static int APP_WIDTH = 600;
private static int APP_HEIGHT = 400;
private static final long serialVersionUID = -8019663913250286271L;
public ViewPanel() {
setBackground(Color.GRAY);
}
public void init() {
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent( g );
render( g );
}
// Where I do the drawing. It's called from the rendering loop in the JFrame
public void render( Graphics g ) {
// refresh the background since we're not relying on paintComponent all the time
Color bgc = getBackground();
g.setColor( bgc );
g.fillRect( 0, 0, APP_WIDTH, APP_HEIGHT );
// just paint a moving box
drawBox( g );
// draw a line to prove correctness. In the loop, you can see part of this line is hidden
// underneath the title bar
g.setColor( Color.red );
g.drawLine(0, 0, APP_WIDTH, APP_HEIGHT);
}
protected void drawBox( Graphics g ) {
// get a random color
Random ran = new Random();
int red = ran.nextInt( 255 );
int grn = ran.nextInt( 255 );
int ble = ran.nextInt( 255 );
Color colour = new Color( red, grn, ble );
g.setColor( colour );
// get a random position
int x = ran.nextInt( APP_WIDTH - 50);
int y = ran.nextInt( APP_HEIGHT - 50);
// draw it
g.fillRect( x, y, 50, 50 );
}
}
Swing 使用它自己的呈现引擎,这是一种被动实现。你正试图用你自己的、活跃的、渲染引擎来规避这个问题,这两者将会发生冲突。
因为BufferStrategy
属于JFrame
,它是在JFrame
的范围内创建的,所以0x0
将是JFrame
的左上角位置,而不是JPanel
.
Swing 的呈现引擎会自动为您完成此转换。
您有两个基本选择。
- 不要基于
JPanel
进行渲染,只需使用一个“渲染”class 即可独立完成此操作(并使用Canvas
而不是JFrame
作为BufferStrategy
) 的基础
- 使用 Swing
Timer
作为主要渲染引擎
基于 Swing Timer
的示例...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new ViewPanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class ViewPanel extends JPanel {
private static int APP_WIDTH = 600;
private static int APP_HEIGHT = 400;
private static final long serialVersionUID = -8019663913250286271L;
public ViewPanel() {
setBackground(Color.GRAY);
Timer timer = new Timer(5, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
repaint();
}
});
timer.start();
}
public void init() {
}
@Override
public Dimension getPreferredSize() {
return new Dimension(APP_HEIGHT, APP_HEIGHT);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
render(g);
}
// Where I do the drawing. It's called from the rendering loop in the JFrame
public void render(Graphics g) {
// refresh the background since we're not relying on paintComponent all the time
Color bgc = getBackground();
g.setColor(bgc);
g.fillRect(0, 0, APP_WIDTH, APP_HEIGHT);
// just paint a moving box
drawBox(g);
// draw a line to prove correctness. In the loop, you can see part of this line is hidden
// underneath the title bar
g.setColor(Color.red);
g.drawLine(0, 0, APP_WIDTH, APP_HEIGHT);
}
protected void drawBox(Graphics g) {
// get a random color
Random ran = new Random();
int red = ran.nextInt(255);
int grn = ran.nextInt(255);
int ble = ran.nextInt(255);
Color colour = new Color(red, grn, ble);
g.setColor(colour);
// get a random position
int x = ran.nextInt(APP_WIDTH - 50);
int y = ran.nextInt(APP_HEIGHT - 50);
// draw it
g.fillRect(x, y, 50, 50);
}
}
}