Drawing a rectangle that won't disappear in next paint
- Don't override
paint
WITHOUT VERY, VERY good reason...usepaintComponent
instead - Always call
super.paintXxx
, these methods do a lot in the background, failing to call super is only going to come back and haunt you. - If you're using multiple panes as rectangles, make the
MyPanel
transparent.
Paint's are stateless. There is no connection between the last paint and the next. On each paint request you are expected to update the entire state.
Andrew's suggest of double buffering (or back buffering) is and excellent one, and I highly encourage you to have a look an implementing it.
In the mean time, I put this little example together...
Basically, you press and hold the mouse button and it will randomly add another rectangle to the panel every 40 milli-seconds (roughly 25 frames a second).
I got this up to a 1000 rects without any issue, was able to resize the window without and issue or obviously slow down...
public class MyPanel extends JPanel {
private List<MyRectangle> lstShapes;
private Timer populate;
public MyPanel() {
lstShapes = new ArrayList<MyRectangle>(25);
populate = new Timer(40, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int x = (int) (Math.random() * getWidth());
int y = (int) (Math.random() * getHeight());
int width = (int) (Math.random() * (getWidth() / 4));
int height = (int) (Math.random() * (getHeight() / 4));
if (x + width > getWidth()) {
x = getWidth() - width;
}
if (y + height > getHeight()) {
y = getHeight() - height;
}
Color color = new Color(
(int) (Math.random() * 255),
(int) (Math.random() * 255),
(int) (Math.random() * 255));
lstShapes.add(new MyRectangle(x, y, width, height, color));
repaint();
}
});
populate.setInitialDelay(0);
populate.setRepeats(true);
populate.setCoalesce(true);
addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
populate.restart();
}
@Override
public void mouseReleased(MouseEvent e) {
populate.stop();
}
});
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
for (MyRectangle rect : lstShapes) {
rect.paint(g2d);
}
FontMetrics fm = g2d.getFontMetrics();
String text = Integer.toString(lstShapes.size());
g2d.setColor(getForeground());
g2d.drawString(text, getWidth() - fm.stringWidth(text), getHeight() - fm.getHeight() + fm.getAscent());
}
public class MyRectangle extends Rectangle {
private Color color;
public MyRectangle(int x, int y, int width, int height, Color color) {
super(x, y, width, height);
this.color = color;
}
public Color getColor() {
return color;
}
public void paint(Graphics2D g2d) {
g2d.setColor(getColor());
g2d.fill(this);
}
}
}
have a go, it's fun ;)
ps- I got to up to over 5000 rectangles before I noticed a slow down (I modified the code down to a 10 milli second delay and was adding 10 new rectangles per tick)
Paint rectangle each time a method is called
There are two common approaches
- You need to keep a list of Rectangles to paint and iterate through the list every time.
- Paint your Rectangle to a BufferedImage and paint the BufferedImage.
Check out Custom Painting Approaches for working examples of both of these approaches.
Drawing a shape on a JPanel which already uses the paint method
First two things that you should remember: Never override paint
but paintComponent
and call super.paintComponent
in there so that borders and everything works as expected. Regarding why this is the case, reference this question: Difference between paint() and paintcomponent()?
Now to answer your question. Assuming you have an existing logic to determine in which square you want to draw your Ellipse (let's assume you have two Integer
s elX
and elY
that are the column and row of your square) you can simply go and draw it after you have finished drawing the board itself.
Imagine sample code like this:
@Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
// Draw the board
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
g.drawRect(j * squareSize, i * squareSize, squareSize, squareSize);
}
}
// Draw the ellipse at the correct location using half the size of a normal square.
g.drawOval(elX * squareSize + squareSize / 4, elY * squareSize + squareSize / 4, squareSize / 2 , squareSize / 2);
}
Now the final part of how to go about actually determining where to draw your ellipse.
A simple solution would be to add a MouseListener
to your panel. And then in the mouseClicked
method you calculate where you actually did click.
Could look like this:
this.addMouseListener(new MouseListener()
{
@Override
public void mouseClicked(MouseEvent e)
{
int column = e.getX() / squareSize;
int row = e.getY() / squareSize;
board[column][row] = 1;
}
[...] // add the other methods to override
}
Then you slightly adapt your paintComponent
method with something like this:
for (int column = 0; column < width; ++column)
{
for (int row = 0; row < height; ++row)
{
if (board[column][row] == 1)
{
g.drawOval(column * squareSize + squareSize / 4, row * squareSize + squareSize / 4, squareSize / 2, squareSize / 2);
}
}
}
and now you draw an ellipse everywhere you click. You could also check if the clicked square already has 1
set as a value and reset it to 0
to have some toggle mechanism or increment it and draw different things based on the integer value... it's all up to you :)
Why doesn't repaint() in the draw() method work (doesn't call paint())?
Slowpoke answer.. it worked after changing Window to transparent JFrame and drawing Rectangle to adding Jpanel with black background like this:
public class BlackRectangle extends JPanel {
public BlackRectangle() {
new JPanel();
setBackground(Color.BLACK);
setVisible(true);
}
public void setNew(int x, int y, int width, int height) {
setBounds(x, y, width, height);
}
public void clearPanel() {
setBounds(0, 0, 0, 0);
}
}
public class FrameTransparent extends JFrame{
public FrameTransparent() {
new JFrame();
setAlwaysOnTop(true);
setUndecorated(true);
setBackground(new Color(0, 0, 0, 0));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(null);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
setSize(screenSize);
setLocationRelativeTo(null);
setVisible(true);
}
}
public class Main {
public static void main (String [] args){
new Main();
}
public Main(){
BlackRectangle blackRectangle = new BlackRectangle();
FrameTransparent frameTransparent = new FrameTransparent();
frameTransparent.add(blackRectangle);
blackRectangle.setNew(10, 70, 200, 200);
frameTransparent.revalidate();
frameTransparent.repaint();
}
}
Movement stuck when clicking buttons
Short answer for the first. Add:
button1.setFocusable(false);
button2.setFocusable(false);
button3.setFocusable(false);
button4.setFocusable(false);
button5.setFocusable(false);
button6.setFocusable(false);
button7.setFocusable(false);
button8.setFocusable(false);
button9.setFocusable(false);
button10.setFocusable(false);
Java drawing rectangle upside down with varying heigth
Use
g.fillRect(x, y - h, 100, h);
instead.
Related Topics
Hashmap: One Key, Multiple Values
Java - When to Use 'This' Keyword
How to Set the Maximum Memory Usage for Jvm
How to Synchronize a Static Variable Among Threads Running Different Instances of a Class in Java
Test If Element Is Present Using Selenium Webdriver
Unsupportedoperationexception When Trying to Remove from the List Returned by Array.Aslist
Java 8 Nested (Multi Level) Group By
Drawing a Rectangle That Won't Disappear in Next Paint
How to Convert a Java 8 Intstream to a List
Java: Run a Function After a Specific Number of Seconds
Big-O Summary for Java Collections Framework Implementations
How to Log SQL Statements in Spring Boot
Integer Wrapper Class and == Operator - Where Is Behavior Specified
How to Get Pid of Process I'Ve Just Started Within Java Program