Thomas G. Marshall said:
Scott Steiner coughed up:
Thomas G. Marshall said:
Scott Steiner coughed up:
hi,
I'm writing a backgammon game which involves drawing the board on a
JPanel and drawing the checkers on the board and have the user move
them around per drag and drop. The drawing contents of the JPanel
are scaled accordingly as the JPanel is resized.
All is fine so far except that there is much flickering as a checker
is dragged over the board. I assume this is because the whole board
is repainted every time during the drag and drop process of a
checker. The simplified code which involves drawing the board and
drawing and moving just one checker basically looks like this:
public class BoardPanel extends JPanel
{
public void paint(Graphics g)
Stop right there.
If you are intent on using a lightweight component like the swing
JPanel, then design the thing entirely using swing facilities.
Use paintComponent(). Swing JComponents are designed to use double
buffering by default. If that doesn't solve your flicker problem,
then report back. Double buffering is a standard graphics
technique---it'd be worth looking up on the off chance that you are
unfamiliar with it.
Good luck---report back.
[...]
hi Thomas,
thanks for your prompt reply. Here are my results:
1) using paintComponent() instead of paint() didn't solve my
flickering problem, unless there was more to change in my code other
than rename my paint() method to paintComponent().
2) I tried a repaint(x,y,width,height) instead of repaint() in my
method that is called in the mouseDragged event. For the values
x,y,width and height I used an area around the dragged checker which
worked fine and cleaned the trailing dirt the checker leaves behind
when it is dragged and avoids flickering since I'm not repainting the
entire JPanel. However, if the checker is dragged very fast, then I
do get a bit of a dirt trail because the cleaning area is not big
enough anymore to clear the dirt of the last position which is now
further away as the checker was moved very fast.
3) In fact I looked up on double buffering prior to my post but could
never get any of the sample code I found on the internet to fit in my
code and work. I realize I must have understood something wrong.
Hopefully you could help me out here on what exactly I must do.
Thanks again.
Can you create the smallest possible code that is cut and pasteable that
exhibits that problem? That "simplified code" you posted has undeclared
members in it. It would do you well to do this, as you might just discover
that you've done it right and there is no solution, though I don't think so.
I have pasted the code, not much really, the class where all the work is
done and a small starter class, that's 2 classes in all.
When you run the starter class then the board should display and you
should see a white round checker in the top right corner. You can now
drag the checker around. Also note that the application functions at any
window size i.e. the user can resize the window at any time.
Just note that the flicker is gone when I use repaint(x,y,w,h) instead
of paint() in method updateLocation(), however, if you drag the checker
very fast you will see some dirt left.
Many thanks!
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;
public class BoardPanel extends JPanel implements ComponentListener, MouseListener, MouseMotionListener
{
private Ellipse2D.Double checker;
private double scaleX = 1;
private double scaleY = 1;
private int posX = 986;
private int posY = 31;
private int clickedX, clickedY;
private int frameX, frameY;
private int deltaX = 0;
private int deltaY = 0;
private boolean pressedIn = false;
public BoardPanel()
{
this.addComponentListener(this);
this.addMouseListener(this);
this.addMouseMotionListener(this);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2D = (Graphics2D)g;
g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2D.scale(scaleX,scaleY);
g2D.setColor(new Color(80,40,0)); //brown border left
g2D.fill3DRect(0,0,576,839,true);
g2D.setColor(new Color(44,109,24)); //green left
g2D.fillRect(100,30,432,779);
g2D.setColor(new Color(80,40,0)); //brown border right
g2D.fill3DRect(576,0,576,839,true);
g2D.setColor(new Color(44,109,24)); //green right
g2D.fillRect(620,30,432,779);
g2D.setColor(new Color(0,39,0)); //ditch left
g2D.fillRect(20,30,60,779);
g2D.setColor(new Color(0,39,0)); //ditch right
g2D.fillRect(1072,30,60,779);
g2D.setColor(new Color(80,40,0)); //brown border ditch seperator left
g2D.fill3DRect(20,359,60,121,true);
g2D.setColor(new Color(80,40,0)); //brown border ditch seperator right
g2D.fill3DRect(1072,359,60,121,true);
for (int i = 1; i<=6; i++)
{
if (i%2 == 1)
g2D.setColor(new Color(25,25,25)); //point black
else
g2D.setColor(new Color(109,0,0)); //point red
int[] x = {100 + (72*(i-1)),136 + (72*(i-1)),172 + (72*(i-1))};
int[] y = {30,359,30};
g2D.fillPolygon(x,y,3);
}
for (int i = 1; i<=6; i++)
{
if (i%2 == 1)
g2D.setColor(new Color(25,25,25)); //point black
else
g2D.setColor(new Color(109,0,0)); //point red
int[] x = {620 + (72*(i-1)),656 + (72*(i-1)),692 + (72*(i-1))};
int[] y = {30,359,30};
g2D.fillPolygon(x,y,3);
}
for (int i = 1; i<=6; i++)
{
if (i%2 == 1)
g2D.setColor(new Color(109,0,0)); //point red
else
g2D.setColor(new Color(25,25,25)); //point black
int[] x = {100 + (72*(i-1)),136 + (72*(i-1)),172 + (72*(i-1))};
int[] y = {809,480,809};
g2D.fillPolygon(x,y,3);
}
for (int i = 1; i<=6; i++)
{
if (i%2 == 1)
g2D.setColor(new Color(109,0,0)); //point red
else
g2D.setColor(new Color(25,25,25)); //point black
int[] x = {620 + (72*(i-1)),656 + (72*(i-1)),692 + (72*(i-1))};
int[] y = {809,480,809};
g2D.fillPolygon(x,y,3);
}
//single white checker
g2D.setColor(new Color(234,204,174));
checker = new Ellipse2D.Double();
checker.setFrame(posX, posY, 60, 60);
g2D.fill(checker);
}
public void componentShown(ComponentEvent arg0) {}
public void componentHidden(ComponentEvent arg0) {}
public void componentMoved(ComponentEvent arg0) {}
public void componentResized(ComponentEvent arg0)
{
scaleX = (double)this.getWidth() / 1152;
scaleY = (double)this.getHeight() / 839;
this.repaint();
}
public void mousePressed(MouseEvent e)
{
if (this.checker.contains((e.getX()/(double)this.getWidth())*1152, (e.getY()/(double)this.getHeight())*839))
{
Cursor hand_cursor = new Cursor(Cursor.HAND_CURSOR);
this.setCursor(hand_cursor);
pressedIn = true;
Rectangle2D r = checker.getFrame();
frameX = (int)r.getX();
frameY = (int)r.getY();
clickedX = e.getX();
clickedY = e.getY();
}
else
{
pressedIn = false;
}
}
public void mouseReleased(MouseEvent e)
{
Cursor default_cursor = new Cursor(Cursor.DEFAULT_CURSOR);
this.setCursor(default_cursor);
}
public void mouseClicked(MouseEvent e){}
public void mouseExited(MouseEvent e){}
public void mouseEntered(MouseEvent e){}
public void mouseDragged(MouseEvent e)
{
if (pressedIn)
{
updateLocation(e);
}
}
public void mouseMoved(MouseEvent e){}
public void updateLocation(MouseEvent e)
{
deltaX = (int)(((e.getX() - clickedX)/(double)this.getWidth())*1152);
deltaY = (int)(((e.getY() - clickedY)/(double)this.getHeight())*839);
posX = frameX + deltaX;
posY = frameY + deltaY;
//only repaint small area around current point to avoid repaint of whole panel which leads to flicker
this.repaint(e.getX()-60,e.getY()-60,120,120);
}
}
//---------------------------------------------------------------------------------------
//Starter class
//---------------------------------------------------------------------------------------
import javax.swing.*;
import java.awt.*;
public class Test extends JFrame
{
public Test()
{
this.setSize(Toolkit.getDefaultToolkit().getScreenSize());
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container c = this.getContentPane();
c.setLayout(new BorderLayout());
BoardPanel p = new BoardPanel();
c.add(p);
this.setVisible(true);
this.setExtendedState(JFrame.MAXIMIZED_BOTH);
}
public static void main(String[] args)
{
new Test();
}
}