I have a JFrame to which I add a JPanel. I'm doing some animation, so I implement a BufferStrategy for rendering. I also a rendering loop to keep it rendering while running.
If I run the program like normal, the JPanel renders correctly. Of course, then there's no animation. If I run it with the loop and hte BufferedStrategy, the JPanel extends to the full size of the application, and underneath the title bar of the JFrame. I can't find a good reason for this to be happening, but it's frustrating because I need to do some precise drawing, and can't have some of it hidden underneath the title bar.
I assume it's because I'm not calling super.paintComponent(), but I really shouldn't call it anyway, since I'm rendering on my own, not within the normal Swing pipeline.
Is there some API call I need to make to make the JPanel position itself correctly within my render call?
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();
}
}
The 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 uses it's own rendering engine, which is a passive implementation. You're attempting to circumvent this with your own, active, rendering engine, the two are going to butt heads.
Because the
BufferStrategybelongs to theJFrame, it's created within the confines of it, so0x0will be the top left position of theJFrame, not theJPanel.Swing's rendering engine will do this translation automatically for you.
You have two basic choices.
JPaneland simply have a "render" class which does this independently (and use aCanvasinstead of theJFrameas the bases for theBufferStrategy)Timeras the primary rendering engineSwing
Timerbased example...