I have a BufferedImage that I draw that doesn't show up unless I resize the window, and even then it only flickers:
// Remember, this is a JFrame class
#Override
public void paintComponents(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
super.paint(g2d);
Graphics p = drawPanel.getGraphics();
for (int i = 0; i < images.length; i++) {
BufferedImage im = images[i];
int ciw = getWidth() / 3;
int cih = getHeight() / 2;
int xpos = i % 3;
int ypos = i / 3;
int ix = i * ciw * xpos;
int iy = i * cih * ypos;
int iw = ciw;
int ih = cih;
p.drawImage(im, ix, iy, iw, ih, null);
}
// drawPanel.paintComponents(p);
// drawPanel.repaint();
}
I have done drawPanel.setDoubleBuffered(true).
If you want to see the whole code: https://github.com/firestar115/BCMaker
This code contains several things you shouldn't do.
Don't override paint(). Override paintComponent() instead.
Don't do this:
Graphics2D p = (Graphics2D) drawPanel.getGraphics();
Instead, just use the Graphics that were passed into the paintComponent() method.
Don't call paint() or paintComponent() from your own painting code.
Instead, just call repaint() when you want your paintComponent() method to be called. If you want it to be called multiple times, then use a Timer. Don't call repaint() from inside your paintComponent() method either.
If you want more specific help, please provide an MCVE.
Make a BufferedImage to draw your images on. Then, draw that BufferedImage on to drawPanel.getGraphics(). Do not call repaint().
#Override
public void paintComponents(Graphics g) {
// Graphics2D g2d = (Graphics2D) g;
// super.paint(g2d);
Graphics p = bi.getGraphics();
for (int i = 0; i < images.length; i++) {
BufferedImage im = images[i];
int ciw = getWidth() / 3;
int cih = getHeight() / 2;
int xpos = i % 3;
int ypos = i / 3;
int ix = i * ciw * xpos;
int iy = i * cih * ypos;
int iw = ciw;
int ih = cih;
p.drawImage(im, ix, iy, iw, ih, null);
}
drawPanel.drawImage(bi, 0, 0, null);
// drawPanel.paintComponents(p);
// drawPanel.repaint();
}
Related
My partner and I are trying to remake Tetris for our final project of the year in my Computer Science class we currently have a for loop that draws individual rectangles in an overwritten paint method.
private final int spacer = 30;
public int getSpacer()
{
return spacer;
}
public void paint(Graphics g) {
setBackground(Color.GRAY);
for(int i = getHeight()/2 - (spacer * 10); i < getHeight()/2 + (spacer * 10); i += spacer) {
for(int x = getWidth()/2 - (spacer * 5); x < getWidth()/2 + (spacer * 5); x += (spacer)) {
g.drawRect(x, i, (spacer), (spacer));
}
}
setForeground(Color.black);
}
The method basically takes the width and height of the window and makes a 10 x 20 grid of boxes that are 30 units, pixels I think, wide.
We'd like to make a Grid.java class that takes in color, the spacer int, and an x and y int. The constructor for Grid.java should draw the exact same thing as the code above using the for loop, but when we tried it gave us a white screen that would not resize with the window.
private final int spacer = 30;
private static Grid[][] arr = new Grid[10][20];
public int getSpacer()
{
return spacer;
}
public void paint(Graphics g) {
setBackground(Color.GRAY);
int countY = 0;
int countX = 0;
for(int y = getHeight()/2 - (spacer * 10); y < getHeight()/2 + (spacer * 10); y += spacer) {
for(int x = getWidth()/2 - (spacer * 5); x < getWidth()/2 + (spacer * 5); x += spacer) {
arr[countX][countY] = new Grid(x, y, spacer, g);
countX++;
}
countY++;
}
setForeground(Color.black);
}
*Grid.java Class*
package Tetris_Shapes;
import javax.swing.*;
import java.awt.*;
public class Grid {
private int x;
private int y;
private int side;
private Graphics g;
public Grid(int x, int y, int side, Graphics g) {
// g.drawRect(x, y, spacer, spacer);
this.x = x;
this.y = y;
this.side = side;
this.g = g;
paint(this.g);
}
private void paint(Graphics g) {
g.drawRect(x, y, side, side);
}
}
When we try and run this we get the white box that doesn't resize. My question is does anyone know of a way to get a constructor to draw shapes. Thank you in advance, this is pretty niche so I'm also going to apologize in advance.
I wrote a program that prints coordinates from GPS onto the screen as a scatter points.
here is the code:
class POSCanvas extends JPanel{
int cnt = 1;
private static final long serialVersionUID = 1L;
List<Vector2D> pnts = new ArrayList<Vector2D>();
public void getNewPoint (double xnew, double ynew){
Vector2D pnt = new Vector2D(cnt*xnew, cnt*ynew);
pnts.add(pnt);
cnt++;
}
public void paintComponent(Graphics g){
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
if (!pnts.isEmpty()){
super.paintComponent(g);
g2.setColor(Color.white);
Dimension size = getSize();
Insets insets = getInsets();
int w = size.width - insets.left - insets.right;
int h = size.height - insets.top - insets.bottom;
for (int i = 0; i < pnts.size(); i++ ){
Ellipse2D.Double OV = new Ellipse2D.Double(pnts.get(i).getX() % w, size.height - pnts.get(i).getY() % h, 5, 5);
g2.draw(OV);
repaint();
}
}
}
}
the problem with the drawing is that when the coordinates are bigger then the panel size the drawing makes no sense, the plotting starts from the left side again. here is an example:
I'd like to dynamically re-size the panel so the coordinates will always stay with its boundaries. How can I do that?
I want to draw images on generated coords after some time delay but I got nullpointerexception. I'm trying to make that my image will move from one point to another with visible time delay.
public class PaintStations extends JPanel implements Runnable {
private static final long serialVersionUID = 6734649580151111907L;
private Thread thread;
public PaintStations() {
setSize(new Dimension(MainWindow.WIDTH, MainWindow.HEIGHT));
setOpaque(false);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
}
private void plot(Graphics g, int x, int y) {
Graphics2D g2d = (Graphics2D) g;
Image img = Toolkit.getDefaultToolkit().getImage("cpn.png");
g2d.drawImage(img, x, y, this);
}
private void drawLine(Graphics g, int x1, int y1, int x2, int y2) {
int d = 0;
int dy = Math.abs(y2 - y1);
int dx = Math.abs(x2 - x1);
int dy2 = (dy << 1);
int dx2 = (dx << 1);
int ix = x1 < x2 ? 1 : -1;
int iy = y1 < y2 ? 1 : -1;
if (dy <= dx) {
for (;;) {
plot(g, x1, y1);
if (x1 == x2)
break;
x1 += ix;
d += dy2;
if (d > dx) {
y1 += iy;
d -= dx2;
}
}
} else {
for (;;) {
plot(g, x1, y1);
if (y1 == y2)
break;
y1 += iy;
d += dx2;
if (d > dy) {
x1 += ix;
d -= dy2;
}
}
}
}
#Override
public void run() {
drawLine(getGraphics(), 0, 0, 10, 10);
}
public void start() {
thread = new Thread(this);
thread.start();
}
}
Here is the errors I get:
Exception in thread "Thread-1" java.lang.NullPointerException
at PaintStations.plot(PaintStations.java:27)
at PaintStations.drawLine(PaintStations.java:44)
at PaintStations.run(PaintStations.java:71)
at java.lang.Thread.run(Unknown Source)
I'm testing putting image on random coords but repaint(); doesn't work correctly. It seems that paintComponent() method is fired after generateFuelStations() end, and on screen only appears one image on last generated coords.
public class PaintStations extends JPanel implements Runnable {
private static final long serialVersionUID = 6734649580151111907L;
private ArrayList<Point> stationsLocation;
private Shape region;
private int x;
private int y;
private int stationsNumber;
public PaintStations(Shape region, int stationsNumber) {
setSize(new Dimension(MainWindow.WIDTH, MainWindow.HEIGHT));
setOpaque(false);
this.region = region;
this.stationsNumber = stationsNumber;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("Repaint!");
Graphics2D g2d = (Graphics2D) g;
Image img = Toolkit.getDefaultToolkit().getImage("cpn.png");
g2d.drawImage(img, x, y, this);
}
private void generateFuelStations(int stationsNumber) {
Rectangle r = region.getBounds();
this.stationsLocation = new ArrayList<>();
for(int i=0; i<stationsNumber; i++) {
Random rand = new Random();
do {
x = (int) (r.getX() + rand.nextInt( (int) r.getWidth() ));
y = (int) (r.getY() + rand.nextInt( (int) r.getHeight() ));
} while(!region.contains(x,y));
System.out.println("X: " + x + " Y: " + y);
stationsLocation.add(new Point(x,y));
repaint();
}
}
#Override
public void run() {
generateFuelStations(stationsNumber);
}
}
Don't use a Thread for animation. Instead you should use a Swing Timer. Then when the Timer fires you adjust the properties (x1/y1, x2, y2) valus of the image you want to move and invoke repaint() on your component. These properties should be properties of the class.
Don't use getGraphics(). Custom painting is done by overriding the paintComponent() method and then you use the Graphics object passed to the method for your custom painting. So basically, I think, your paintComponent() method should invoke your drawLine(...) method.
Seems like your getGraphics() call returns null. You should ovverride the paintComponent() method instead of calling getGraphics() and update with repaint().
The documentation for getGraphics() says
Creates a graphics context for this component. This method will return null if this component is currently not displayable.
Your class is not displayable because you make no call to super() in your constructor. Always make a call to the parent class's constructor when you extend a class.
Note: This particular part of the documentation wasn't easy to find. When you're looking at a method and it says it overrides a parent class's method, always look at the parent class's method as well, because often (read: most of the time) an overridden method will have a call to the parent's method. Digging through the documentation will often give you an answer to why your program isn't working, though don't forget to also scrutinize your own code from time to time as well.
I'm working on a project to create a screen saver that draws random shapes. I have a few questions, but my main concern right now is how to get the shapes to stay on the screen and not just disappear after they are created. Here is my code. I cannot use any loops and I am not looking to change any of the functionality of what I have yet (other than possibly shapesDrawn).
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class ScreenSaver2 extends JPanel implements ActionListener {
private JFrame frame = new JFrame("FullSize");
private Rectangle rectangle;
boolean full;
protected void paintComponent(Graphics g) {
int r = (int)(Math.random() * 255);
int gr = (int)(Math.random() * 255);
int b = (int)(Math.random() * 255);
Color color = new Color(r, gr, b);
int width = 10 + (int)(Math.random() * 40);
int height = 10 + (int)(Math.random() * 40);
int x = (int)(Math.random() * (getWidth() - width));
int y = (int)(Math.random() * (getHeight() - height));
int whichShape = (int)(Math.random() * 3);
int shapesDrawn = 0;
super.paintComponent(g);
if (shapesDrawn >= 30) {
shapesDrawn = 0;
}
switch (whichShape) {
case 0:
g.setColor(color);
g.drawLine(x, y, x + width, y + height);
shapesDrawn++;
break;
case 1:
g.setColor(color);
g.drawRect(x, y, width, height);
shapesDrawn++;
break;
case 2:
g.setColor(color);
g.drawRoundRect(x, y, width, height, 25, 25);
shapesDrawn++;
break;
case 3:
g.setColor(color);
g.drawOval(x, y, width, height);
shapesDrawn++;
break;
}
}
ScreenSaver2() {
// Remove the title bar, min, max, close stuff
frame.setUndecorated(true);
// Add a Key Listener to the frame
frame.addKeyListener(new KeyHandler());
// Add this panel object to the frame
frame.add(this);
// Get the dimensions of the screen
rectangle = GraphicsEnvironment.getLocalGraphicsEnvironment()
.getDefaultScreenDevice().getDefaultConfiguration().getBounds();
// Set the size of the frame to the size of the screen
frame.setSize(rectangle.width, rectangle.height);
frame.setVisible(true);
// Remember that we are currently at full size
full = true;
// set and initialize timer
Timer t = new Timer(500, this);
t.setDelay(500);
t.start();
}
// This method will run when any key is pressed in the window
class KeyHandler extends KeyAdapter {
public void keyPressed(KeyEvent e) {
// Terminate the program.
if (e.getKeyChar() == 'x') {
System.out.println("Exiting");
System.exit(0);
}
// Change background color
else if (e.getKeyChar() == 'r') {
System.out.println("Change background color");
setBackground(new Color((int)(Math.random() * 256), (int)(Math.random() * 256), (int)(Math.random() * 256)));
repaint();
}
// Resize to half-screen
else if (e.getKeyChar() == 'z') {
System.out.println("Resizing");
frame.setSize((int)rectangle.getWidth() / 2, (int)rectangle.getHeight());
}
}
}
public void actionPerformed(ActionEvent e) {
repaint();
}
public static void main(String[] args) {
ScreenSaver2 obj = new ScreenSaver2();
}
}
Rather than painting each new shape within the paintComponent directly to the Graphics context, use a backing buffer to render the shapes to first and paint this to the Graphics context.
This way, you would simply need to maintain a simple counter each time a new shape is rendered.
This is also troublesome, because paintComponent may be called for any number of reasons, which, you don't control many, meaning that it would be possible for your program to paint more shapes before any timer ticks have actually occurred...
Updated with example
The only choice you have left is to create a backing buffer, onto which you can paint each change as required. This will "store" each change between paint cycles. You could then simply paint this to the screen...
private BufferedImage img;
//...
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
// Create the backing buffer
// This is a little cheat, creating a new image when the number of shapes
// exceeds the requirements, but it saves messing about with clearing
// a alpha image ;)
if (img == null || shapesDrawn >= 30) {
img = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
shapesDrawn = 0;
} else if (img.getWidth() != getWidth() || img.getHeight() != img.getHeight()) {
// Update the backing buffer to meet the requirements of the changed screen size...
BufferedImage buffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D gbuffer = buffer.createGraphics();
gbuffer.drawImage(img, 0, 0, this);
gbuffer.dispose();
img = buffer;
}
// Get a reference to the backing buffers graphics context...
Graphics2D gbuffer = img.createGraphics();
// Paint the shapes to the backing buffer...
int r = (int) (Math.random() * 255);
int gr = (int) (Math.random() * 255);
int b = (int) (Math.random() * 255);
Color color = new Color(r, gr, b);
int width = 10 + (int) (Math.random() * 40);
int height = 10 + (int) (Math.random() * 40);
int x = (int) (Math.random() * (getWidth() - width));
int y = (int) (Math.random() * (getHeight() - height));
int whichShape = (int) (Math.random() * 3);
int shapesDrawn = 0;
switch (whichShape) {
case 0:
gbuffer.setColor(color);
gbuffer.drawLine(x, y, x + width, y + height);
shapesDrawn++;
break;
case 1:
gbuffer.setColor(color);
gbuffer.drawRect(x, y, width, height);
shapesDrawn++;
break;
case 2:
gbuffer.setColor(color);
gbuffer.drawRoundRect(x, y, width, height, 25, 25);
shapesDrawn++;
break;
case 3:
gbuffer.setColor(color);
gbuffer.drawOval(x, y, width, height);
shapesDrawn++;
break;
}
// Dispose of the buffers graphics context, this frees up memory for us
gbuffer.dispose();
// Paint the image to the screen...
g2d.drawImage(img, 0, 0, this);
g2d.dispose();
}
Possibly the worst advice I've ever had to give
Change...
super.paintComponent(g);
if (shapesDrawn >= 30) {
shapesDrawn = 0;
}
To...
if (shapesDrawn >= 30) {
super.paintComponent(g);
shapesDrawn = 0;
}
I'm designing a strip chart, but it isn't displaying. Its encapsulating JFrame is displaying, and paintComponent() is being called, but no gray grid lines are appearing. Why is this?
class ChartArea extends JPanel {
private int Yscl, Xscl;
private static final Color BACKGROUND_COLOR = Color.BLACK;
private static final Color LINE_COLOR = Color.GREEN;
private static final Color GRIDLINE_COLOR = Color.GRAY;
ChartArea() {
setBackground(BACKGROUND_COLOR);
setForeground(LINE_COLOR);
Yscl = 20;
Xscl = 20;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
int height = getHeight();
int width = getWidth();
double max = data.getMax(); //gets the maximum value in the data set
try {
g2d.setStroke(new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER));
//put the origin in the bottom right. Positive is up and left
g2d.translate(width, height);
g2d.scale(-1d, -1d);
//if the data set exceeds the window height, scale it down
if (max > height) {
g2d.scale(1, height / max);
}
paintGrid(g2d);
paintLine(g2d);
} finally {
g2d.dispose();
}
}
private void paintGrid(Graphics2D g) {
g.setPaint(GRIDLINE_COLOR);
//Vertical Lines
for (double pos = 0; pos > getWidth(); pos += Xscl) {
Line2D line = new Line2D.Double(pos, 0d, pos, getHeight());
g.draw(line);
}
//Horizontal Lines
for (double pos = 0; pos > getHeight(); pos += Yscl) {
Line2D line = new Line2D.Double(0d, pos, getWidth(), pos);
g.draw(line);
}
}
private void paintLine(Graphics2D g) {
g.setPaint(LINE_COLOR);
Path2D plot2 = new Path2D.Double();
if (!data.isEmpty()) {
plot2.moveTo(0.0, data.get(0));
for (int i = 1; i < data.size(); i++) {
plot2.lineTo((double) i, data.get(i));
}
g.draw(plot2);
}
}
Looks like there a typo in for loops conditions that draw lines. The loops are never executed with current conditions. If you change the conditions to pos < getWidth(); and pos < getHeight(); you should see the grid. Here is a complete method:
private void paintGrid(Graphics2D g) {
g.setPaint(GRIDLINE_COLOR);
//Vertical Lines
//for (double pos = 0; pos > getWidth(); pos += Xscl) {
for (double pos = 0; pos < getWidth(); pos += Xscl) {
Line2D line = new Line2D.Double(pos, 0d, pos, getHeight());
g.draw(line);
}
//Horizontal Lines
//for (double pos = 0; pos > getHeight(); pos += Yscl) {
for (double pos = 0; pos < getHeight(); pos += Yscl) {
Line2D line = new Line2D.Double(0d, pos, getWidth(), pos);
g.draw(line);
}
}
Here is a result:
You are overriding paintComponent to only paint your custom panel. You simply need to paint the parent instance as well.
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
// custom painting
}