I'm completely stuck on a problem i'm having with this program where I have to draw a city with swing. Basically what i'm trying to do is make it so that the windows don't change every frame. I've tried just about everything I can think of and nothing has worked yet.
Here is the main class that draws everything
import java.applet.Applet;
import java.awt.*;
import java.util.*;
import javax.swing.JFrame;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
public class Skyline extends JFrame implements MouseMotionListener
{
private int mX, mY; //Mouse cooddinates
private Image mImage; //Image buffer
//private Image mImage2; //Image buffer
private int num = 0;
private Building bldg1 = new Building(305, 110, 30);
private Building bldg2 = new Building(380, 125, 170);
private Building bldg3 = new Building(245, 200, 325);
private Building bldg4 = new Building(470, 170, 555);
private Building bldg5 = new Building(395, 200, 755);
private Background bg = new Background();
public void init ()
{
}
public static void main(String []args)
{
Skyline f = new Skyline();
f.setSize(1017, 661); //Sets size of window
f.setTitle("Skyline"); //Sets title of window
f.show();
}
public void paintOffscreen(Graphics page)
{
//Draws the background
bg.draw(page);
//Moving square
num++;
if (num > 1200)
num = 0;
page.setColor(Color.yellow);
page.fillRect(num,100,100,100);
//Draws the buildings
bldg1.draw(page);
bldg2.draw(page);
bldg3.draw(page);
bldg4.draw(page);
bldg5.draw(page);
//Mouse move square
int s = 100;
page.setColor(Color.yellow);
page.fillRect(mX - s / 2, mY - s / 2, s, s);
repaint();
}
//====================================BUFFER CODE========================================
public void paint(Graphics g)
{
//Clear the buffer
Dimension d = getSize();
checkOffscreenImage();
Graphics offG = mImage.getGraphics();
offG.setColor(getBackground());
offG.fillRect(0, 0, d.width, d.height);
//Save frame to buffer
paintOffscreen(mImage.getGraphics());
//Draw the buffer
g.drawImage(mImage, 0, 0, null);
}
private void checkOffscreenImage()
{
Dimension d = getSize();
if (mImage == null || mImage.getWidth(null) != d.width || mImage.getHeight(null) != d.height)
mImage = createImage(d.width, d.height);
}
//=======================================================================================
//==================================MOUSE MOVE CODE======================================
public Skyline()
{
addMouseMotionListener(this);
setVisible(true);
}
public void mouseMoved(MouseEvent me)
{
Graphics g = getGraphics();
mX = (int) me.getPoint().getX();
mY = (int) me.getPoint().getY();
update(g);
//repaint();
}
public void mouseDragged(MouseEvent me)
{
mouseMoved(me);
}
//=======================================================================================
}
And here is the window class that might be able to be fixed somehow.
import java.applet.Applet;
import java.awt.*;
import java.util.Random;
import javax.swing.JFrame;
public class Windows extends JFrame
{
private Random gen = new Random();
private int height, width, locX;
private int onOff = 0;
public Windows()
{
height = 305;
width = 110;
locX = 30;
}
public Windows(int height, int width, int locX)
{
this.height = height;
this.width= width;
this.locX = locX;
}
public void draw(Graphics page)
{
page.setColor (Color.darkGray);
page.fillRect (locX, 550 - height, width, height);
for (int i = 550 - height + 5; i < 550; i += 15)
{
for (int x = locX + 5; x < locX + width; x += 15)
{
onOff = gen.nextInt(2);
if(onOff == 0)
page.setColor(Color.black);
else
page.setColor(Color.yellow);
page.fillRect (x,i,10,10);
}
}
}
}
Heres the building class just in case.
import java.applet.Applet;
import java.awt.*;
import javax.swing.JFrame;
public class Building extends JFrame
{
private int height, width, locX;
private int onOff;
private Windows windows1;// = new Windows(height, width, locX);
public Building()
{
height = 305;
width = 110;
locX = 30;
windows1 = new Windows(height, width, locX);
}
public Building(int height, int width, int locX)
{
this.width = width;
this.height = height;
this.locX = locX;
windows1 = new Windows(height, width, locX);
}
public void draw(Graphics page)
{
page.setColor (Color.darkGray);
page.fillRect (locX, 550 - height, width, height);
windows1.draw(page);
}
}
And the bg class just to be safe
import java.applet.Applet;
import java.awt.*;
public class Background extends Applet
{
private int height, width;
public Background()
{
height = 400;
width = 2000;
}
public Background(int height, int width)
{
this.height = height;
this.width = width;
}
public void draw(Graphics page)
{
//Draws the sky
page.setColor(Color.cyan);
page.fillRect(0,0,2000,2000);
//Draws the grass
page.setColor (Color.green);
page.fillRect (0,500,width,height);
}
}
A number of things jump out at me immediately...
You're trying to use a off screen buffer, but you're recreating it each time you paint to the screen...
public void paintOffscreen(Graphics page)
{
//Draws the background
bg.draw(page);
//Moving square
num++;
if (num > 1200)
num = 0;
page.setColor(Color.yellow);
page.fillRect(num,100,100,100);
//Draws the buildings
bldg1.draw(page);
bldg2.draw(page);
bldg3.draw(page);
bldg4.draw(page);
bldg5.draw(page);
//Mouse move square
int s = 100;
page.setColor(Color.yellow);
page.fillRect(mX - s / 2, mY - s / 2, s, s);
repaint();
}
Additionally, the last call in the method is to repaint. This is a bad idea. This could cause you paint method to be recalled, again and again and again...
You would be better of rendering the backing buffer only when it needs to change...
public void paint(Graphics g)
{
super.paint(g); // YOU MUST CALL super.paint!!!!
//Clear the buffer
Dimension d = getSize();
checkOffscreenImage();
//Draw the buffer
g.drawImage(mImage, 0, 0, null);
}
private void checkOffscreenImage()
{
Dimension d = getSize();
if (mImage == null || mImage.getWidth(null) != d.width || mImage.getHeight(null) != d.height) {
mImage = createImage(d.width, d.height);
Graphics offG = mImage.getGraphics();
offG.setColor(getBackground());
offG.fillRect(0, 0, d.width, d.height);
//Save frame to buffer
paintOffscreen(offG);
offG.dispose(); // If you create it, you must dispose of it...
}
}
Now, this is going to raise some issues with invalidating the buffer. This can be achieved by overriding invalidate and setting the mImage to null
public void invalidate() {
mImage = null;
super.invalidate();
}
You're extending most of your components from JFrame???
Building, Window and Background do no painting of there own (from the content of Swing), you are simply calling the draw method. There is no need to extend from JFrame or JApplet, they're adding no benefit to your program and are simply confusing the issues.
You should, only very rarely, need to override paint on a top level container like JFrame. You are better off using something like JPanel and override the paintComponent method, if for no other reason, they (top level containers) aren't double buffered.
I would move the logic for Skyline into a JPanel and then add it to a JFrame for displaying - IMHO
UPDATED
I've gone through the code and updated it to work the (basic) way I think it should and found a couple of other things along the way...
This this is a bad idea...
public void mouseMoved(MouseEvent me) {
Graphics g = getGraphics();
mX = (int) me.getPoint().getX();
mY = (int) me.getPoint().getY();
update(g);
//repaint();
}
There should never be any need for you to call update(Graphics), besides, the Graphics context you got is simply a snap shot of the last repaint. This will drastically slow you painting process any way, as it is repeatedly calling paint.
So, this is my take...
public class Skyline extends JFrame {
private int num = 0;
private Building bldg1 = new Building(305, 110, 30);
private Building bldg2 = new Building(380, 125, 170);
private Building bldg3 = new Building(245, 200, 325);
private Building bldg4 = new Building(470, 170, 555);
private Building bldg5 = new Building(395, 200, 755);
private Background bg = new Background();
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
Skyline f = new Skyline();
f.setSize(1017, 661); //Sets size of window
f.setTitle("Skyline"); //Sets title of window
f.setVisible(true);
}
});
}
public Skyline() {
setLayout(new BorderLayout());
add(new SkyLinePane());
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public class SkyLinePane extends JPanel {
private Image mImage; //Image buffer
private boolean painting = false;
private int mX, mY; //Mouse cooddinates
public SkyLinePane() {
addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent me) {
mX = (int) me.getPoint().getX();
mY = (int) me.getPoint().getY();
repaint();
}
});
}
protected void updateBuffer() {
if (!painting && mImage == null) {
painting = true;
new BackgroundPainter(this).execute();
}
}
//====================================BUFFER CODE========================================
#Override
public void paintComponent(Graphics g) {
Dimension d = getSize();
if (mImage != null) {
g.drawImage(mImage, 0, 0, null);
} else {
updateBuffer();
}
g.setColor(Color.RED);
g.drawOval(mX - 5, mY - 5, 10, 10);
}
//=======================================================================================
protected void setBackground(Image image) {
mImage = image;
painting = false;
repaint();
}
}
public class BackgroundPainter extends SwingWorker<Image, Image> {
private SkyLinePane skyLinePane;
public BackgroundPainter(SkyLinePane skyLinePane) {
this.skyLinePane = skyLinePane;
}
#Override
protected Image doInBackground() throws Exception {
Dimension d = skyLinePane.getSize();
Image backgroundBuffer = null;
if (d.width > 0 && d.height > 0) {
System.out.println("Paint offscreen...");
backgroundBuffer = createImage(d.width, d.height);
Graphics offG = backgroundBuffer.getGraphics();
offG.setColor(getBackground());
offG.fillRect(0, 0, d.width, d.height);
//Save frame to buffer
paintOffscreen(offG);
offG.dispose();
System.out.println("Done Paint offscreen...");
}
return backgroundBuffer;
}
#Override
protected void done() {
try {
skyLinePane.setBackground(get());
} catch (ExecutionException exp) {
exp.printStackTrace();
} catch (InterruptedException exp) {
exp.printStackTrace();
}
}
public void paintOffscreen(Graphics page) {
//Draws the background
bg.draw(page);
//Moving square
num++;
if (num > 1200) {
num = 0;
}
page.setColor(Color.yellow);
page.fillRect(num, 100, 100, 100);
//Draws the buildings
bldg1.draw(page);
bldg2.draw(page);
bldg3.draw(page);
bldg4.draw(page);
bldg5.draw(page);
}
}
//=======================================================================================
public class Windows {
private Random gen = new Random();
private int height, width, locX;
private int onOff = 0;
public Windows() {
height = 305;
width = 110;
locX = 30;
}
public Windows(int height, int width, int locX) {
this.height = height;
this.width = width;
this.locX = locX;
}
public void draw(Graphics page) {
page.setColor(Color.darkGray);
page.fillRect(locX, 550 - height, width, height);
for (int i = 550 - height + 5; i < 550; i += 15) {
for (int x = locX + 5; x < locX + width; x += 15) {
onOff = gen.nextInt(2);
if (onOff == 0) {
page.setColor(Color.black);
} else {
page.setColor(Color.yellow);
}
page.fillRect(x, i, 10, 10);
}
}
}
}
public class Building {
private int height, width, locX;
private int onOff;
private Windows windows1;// = new Windows(height, width, locX);
public Building() {
height = 305;
width = 110;
locX = 30;
windows1 = new Windows(height, width, locX);
}
public Building(int height, int width, int locX) {
this.width = width;
this.height = height;
this.locX = locX;
windows1 = new Windows(height, width, locX);
}
public void draw(Graphics page) {
page.setColor(Color.darkGray);
page.fillRect(locX, 550 - height, width, height);
windows1.draw(page);
}
}
public class Background {
private int height, width;
public Background() {
height = 400;
width = 2000;
}
public Background(int height, int width) {
this.height = height;
this.width = width;
}
public void draw(Graphics page) {
//Draws the sky
page.setColor(Color.cyan);
page.fillRect(0, 0, 2000, 2000);
//Draws the grass
page.setColor(Color.green);
page.fillRect(0, 500, width, height);
}
}
}
Basically, I moved the core rendering of the skyling to it's own panel and used JComponent#paintComponent to render the skyline.
I employed a SwingWorker to off load the rendering of the backing buffer to another thread, allowing the UI to remain responsive while the backing buffer was rendered.
Related
I'm trying to make it when you click the cameraButton, the graphics show, but when clicked again, it closes.
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
int camButtonWidth = 500;
int camButtonHeight = 33;
int camButtonX = (width - camButtonWidth) / 2;
int camButtonY = (height - camButtonHeight) - 5;
int camWidth = width - 50;
int camHeight = (height - (camButtonHeight * 2)) - 10;
int camX = (width - camWidth) / 2;
int camY = ((height - camHeight) - camButtonHeight) / 2;
Graphics2D g1 = (Graphics2D) g;
Graphics2D g2 = (Graphics2D) g;
Graphics2D g3 = (Graphics2D) g;
RoundRectangle2D camButton = new RoundRectangle2D.Double(camButtonX, camButtonY, camButtonWidth, camButtonHeight, 25, 25);
RoundRectangle2D cameras = new RoundRectangle2D.Double(camX, camY, camWidth, camHeight, 25, 25);
// Background
g1.setColor(Color.BLACK);
g1.fillRect(0, 0, width, height);
addMouseListener(new MouseListener() {
#Override
public void mouseClicked(MouseEvent e) {
if (camButton.contains(e.getPoint())) {
camUp = !camUp;
repaint();
}
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
});
// Camera Button
g2.setColor(camColor);
g2.fill(camButton);
paintCameras = camUp;
// Cameras
g3.setColor(camColor);
if (paintCameras) {
g3.fill(cameras);
}
repaint();
}
Try to change make it when you click the camera button, a graphics object shows, but when clicked again, it closes.
To get this sort of program to work you should:
Create your MouseListener in code that is only called once, such as within a constructor
Create an instance field in the class to represent the camera button, such as a Rectangle or RoundRectangle2D and give it a viable object reference
In the mouse listener, toggle the state of a boolean variable if a click occurs within the shape that represents the camera button, e.g., camUp = !camUp; as you're doing
And then call repaint().
In the paintComponent method, check the state of the boolearn variable with an if statement, and if true, draw the image inside the if statement.
Keep the mouse listener and the painting code separate and in separate methods (or constructor).
Never call repaint() within a painting method as that will cause an uncontrolled animation. If you need a Swing animation, then use a Swing Timer so that you can fully control it. I don't see the need for it here.
For example:
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
#SuppressWarnings("serial")
public class GraphicsExample extends JPanel {
private static final int IMG_WIDTH = 400;
private static final int PREF_W = (3 * IMG_WIDTH) / 2;
private static final int PREF_H = PREF_W;
private static final Color BTN_COLOR = Color.RED;
private static final Color HOVER_COLOR = new Color(255, 100, 100);
private static final Color BTN_CLK_COLOR = new Color(180, 0, 0);
private static final int IMG_X = IMG_WIDTH / 2;
private static final int IMG_Y = IMG_X;
private double camX = 10;
private double camY = camX;
private double camWidth = 200;
private double camHeight = 80;
private Color buttonColor = Color.RED;
private RoundRectangle2D cameraButton = new RoundRectangle2D.Double(camX, camY, camWidth, camHeight, 25, 25);
private Image img;
private boolean showImage = false;
private JCheckBox toggleModeChkBox = new JCheckBox("Toggle Mode");
// private boolean toggleMode = true;
public GraphicsExample() {
add(toggleModeChkBox);
setPreferredSize(new Dimension(PREF_W, PREF_H));
img = createMyImage();
MouseAdapt mouseAdapt = new MouseAdapt();
addMouseListener(mouseAdapt);
addMouseMotionListener(mouseAdapt);
}
private Image createMyImage() {
BufferedImage img = new BufferedImage(IMG_WIDTH, IMG_WIDTH, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
g2.setPaint(new GradientPaint(0, 0, Color.RED, 100, 100, Color.BLUE, true));
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int gap = 10;
g2.fillOval(gap, gap, IMG_WIDTH - 2 * gap, IMG_WIDTH - 2 * gap);
g2.dispose();
return img;
}
private class MouseAdapt extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
if (cameraButton.contains(e.getPoint())) {
buttonColor = BTN_CLK_COLOR;
if (toggleModeChkBox.isSelected()) {
showImage = !showImage;
} else {
showImage = true;
}
} else {
buttonColor = BTN_COLOR;
}
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
if (cameraButton.contains(e.getPoint())) {
buttonColor = HOVER_COLOR;
} else {
buttonColor = Color.RED;
}
if (!toggleModeChkBox.isSelected()) {
showImage = false;
}
repaint();
}
#Override
public void mouseMoved(MouseEvent e) {
if (cameraButton.contains(e.getPoint())) {
buttonColor = HOVER_COLOR;
} else {
buttonColor = Color.RED;
}
if (!toggleModeChkBox.isSelected()) {
showImage = false;
}
repaint();
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(buttonColor);
g2.fill(cameraButton);
if (showImage) {
int x = (getWidth() - IMG_WIDTH) / 2;
int y = (getHeight() - IMG_WIDTH) / 2;
g2.drawImage(img, x, y, this);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
GraphicsExample mainPanel = new GraphicsExample();
JFrame frame = new JFrame("GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
So I created a simple simple simulation where squares are spawned randomly with random vectors and bounce of the edges of the window.
I wanted it to take into account the window being resized. So that if I change the dimensions of the window from 600x600 to 1200x600 the squares will bounce of the new border rather than 600x600.
I tried doing getWidth() getHeight() but it would return 0.
So I put it in the pain() (since it gets called on window resize) method and saved the return values as local variables. But I cannot call getjpWidth() from the Rect class.
So basically what I need is to get new window dimension into the move() method in the Rect class.
Please feel free to point out any other mistakes and things that can be done better. I'm new to 2D programming (studying Computer Science)
Application
import javax.swing.*;
public class Application {
private Application(){
//create a JFrame window
JFrame frame = new JFrame("Moving Squares");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//add a JPanel
GamePanel gamePanel = new GamePanel();
frame.add(gamePanel);
//pack the window around the content
frame.pack();
//center
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String args[]){
new Application();
}
}
GamePanel
import java.awt.*;
import java.util.ArrayList;
import javax.swing.*;
public class GamePanel extends JPanel implements Runnable{
private int jpWidth=0, jpHeight=0;
//set JPanel size
private static final Dimension DESIRED_SIZE = new Dimension(600,600);
#Override
public Dimension getPreferredSize(){
return DESIRED_SIZE;
}
//constructor
GamePanel(){
Thread t = new Thread(this);
t.start();
}
private ArrayList <Rect> rect=new ArrayList<>();
public void run(){
for(int i=0; i<15; i++){
rect.add(new Rect());
}
while(true){
for(Rect rect:rect){
rect.move();
}
//repaint still image for better frames
//should be 100fps instead it's >144fps
repaint();
try{Thread.sleep(10);}
catch(InterruptedException e){/**/};
repaint();
try{Thread.sleep(10);}
catch(InterruptedException e){/**/};
repaint();
try{Thread.sleep(10);}
catch(InterruptedException e){/**/};
}
}
public void paint(Graphics g){
Graphics2D g2d = (Graphics2D) g.create();
jpWidth=getWidth();
jpHeight=getHeight();
g2d.setColor(Color.white);
g2d.fillRect(0,0,jpWidth,jpHeight);
for(Rect rect:rect) {
g2d.setColor(Color.black);
g2d.fillRect(rect.getXcord()-1, rect.getYcord()-1, rect.getWidth()+2, rect.getHeight()+2);
g2d.setColor(Color.getHSBColor(rect.getR(), rect.getG(), rect.getB()));
g2d.fillRect(rect.getXcord(), rect.getYcord(), rect.getWidth(), rect.getHeight());
}
}
public int getJpWidth() {
return jpWidth;
}
public int getJpHeight() {
return jpHeight;
}
}
Rect
import java.util.Random;
public class Rect {
//properties
private int width=30, height=30;
private int R, G, B;
//movement
private int xCord, yCord;
private int xVector, yVector;
private int xSlope, ySlope;
public Rect(){
Random rand = new Random();
//random color
R=rand.nextInt(255);
G=rand.nextInt(255);
B=rand.nextInt(255);
//random spawn position
xCord=rand.nextInt(600-width);
yCord=rand.nextInt(600-height);
//direction
do{
xVector=rand.nextInt(3) - 1;
yVector=rand.nextInt(3) - 1;
}while(xVector==0 || yVector==0);
//slope
do{
xSlope=rand.nextInt(3);
ySlope=rand.nextInt(3);
}while(xSlope==0 || ySlope==0);
xVector*=xSlope;
yVector*=ySlope;
}
public void move(){
//if(xCord>=//how to get screen width ? ){}
if((xCord>=600-width) || (xCord<=0)){
bounceX();
}
if((yCord>=600-height) || (yCord<=0)) {
bounceY();
}
xCord+=xVector;
yCord+=yVector;
}
public void bounceX(){
xVector*=-1;
}
public void bounceY(){
yVector*=-1;
}
public int getR() {
return R;
}
public int getG() {
return G;
}
public int getB() {
return B;
}
public int getXcord() {
return xCord;
}
public int getYcord() {
return yCord;
}
public int getWidth(){
return width;
}
public int getHeight(){
return height;
}
}
So basically what I need is to get new window dimension into the move() method in the Rect class.
Don't know if it is the best design but I pass the "panel" as a parameter to the "move()" method so its width/height can be used.
Here is some old code I have lying around that shows this approach:
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;
public class BallAnimation4
{
private static void createAndShowUI()
{
BallPanel panel = new BallPanel();
JFrame frame = new JFrame("BallAnimation4");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( panel );
frame.pack();
frame.setLocationRelativeTo( null );
//frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame.setVisible( true );
panel.addBalls(5);
panel.startAnimation();
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
class BallPanel extends JPanel implements ActionListener
{
private ArrayList<Ball> balls = new ArrayList<Ball>();
public BallPanel()
{
setLayout( null );
// setBackground( Color.BLACK );
}
public void addBalls(int ballCount)
{
Random random = new Random();
for (int i = 0; i < ballCount; i++)
{
Ball ball = new Ball();
ball.setRandomColor(true);
ball.setLocation(random.nextInt(getWidth()), random.nextInt(getHeight()));
// ball.setMoveRate(32, 32, 1, 1, true);
ball.setMoveRate(16, 16, 1, 1, true);
// ball.setSize(32, 32);
ball.setSize(64, 64);
balls.add( ball );
}
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
for (Ball ball: balls)
{
ball.draw(g);
}
}
#Override
public Dimension getPreferredSize()
{
return new Dimension(800, 600);
}
public void startAnimation()
{
Timer timer = new Timer(1000/60, this);
timer.start();
}
public void actionPerformed(ActionEvent e)
{
move();
repaint();
}
private void move()
{
for (Ball ball : balls)
{
ball.move(this);
}
}
class Ball
{
public Color color = Color.BLACK;
public int x = 0;
public int y = 0;
public int width = 1;
public int height = 1;
private int moveX = 1;
private int moveY = 1;
private int directionX = 1;
private int directionY = 1;
private int xScale = moveX;
private int yScale = moveY;
private boolean randomMove = false;
private boolean randomColor = false;
private Random myRand = null;
public Ball()
{
myRand = new Random();
setRandomColor(randomColor);
}
public void move(JPanel parent)
{
int iRight = parent.getSize().width;
int iBottom = parent.getSize().height;
x += 5 + (xScale * directionX);
y += 5 + (yScale * directionY);
if (x <= 0)
{
x = 0;
directionX *= (-1);
xScale = randomMove ? myRand.nextInt(moveX) : moveX;
if (randomColor) setRandomColor(randomColor);
}
if (x >= iRight - width)
{
x = iRight - width;
directionX *= (-1);
xScale = randomMove ? myRand.nextInt(moveX) : moveX;
if (randomColor) setRandomColor(randomColor);
}
if (y <= 0)
{
y = 0;
directionY *= (-1);
yScale = randomMove ? myRand.nextInt(moveY) : moveY;
if (randomColor) setRandomColor(randomColor);
}
if (y >= iBottom - height)
{
y = iBottom - height;
directionY *= (-1);
yScale = randomMove ? myRand.nextInt(moveY) : moveY;
if (randomColor) setRandomColor(randomColor);
}
}
public void draw(Graphics g)
{
g.setColor(color);
g.fillOval(x, y, width, height);
}
public void setColor(Color c)
{
color = c;
}
public void setLocation(int x, int y)
{
this.x = x;
this.y = y;
}
public void setMoveRate(int xMove, int yMove, int xDir, int yDir, boolean randMove)
{
this.moveX = xMove;
this.moveY = yMove;
directionX = xDir;
directionY = yDir;
randomMove = randMove;
}
public void setRandomColor(boolean randomColor)
{
this.randomColor = randomColor;
switch (myRand.nextInt(3))
{
case 0: color = Color.BLUE;
break;
case 1: color = Color.GREEN;
break;
case 2: color = Color.RED;
break;
default: color = Color.BLACK;
break;
}
}
public void setSize(int width, int height)
{
this.width = width;
this.height = height;
}
}
}
Also, note that for animation you should be using a Swing Timer to schedule the animation. Updates to Swing components should be done on the Event Dispatch Thread (EDT). While not likely to cause a problem with this simple application it is a good habit to make sure this basic rule is followed otherwise you can have random problems and it is never easy to debug a random problem.
I'm new to AWT, this is just a sample program to see how it works.
My problem is that on events like resize/minimize, the window is cleared.
I have overridden paintComponent as specified in many SO questions, and the code is actually called (because if I put a println in there, I see the output), however nothing is displayed.
I've tried this on Windows and Linux and the result is the same, so I guess I'm missing something.
Here is the code:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JPanel;
class Site {
private Integer x;
private Integer y;
private Integer width;
private Integer height;
private Graphics2D g2d;
private boolean isOpen = false;
private Integer id = null;
private Integer root = null;
public Site(Integer id, Integer x, Integer y, Integer width, Integer height, Graphics2D g2d) {
this.id = id;
this.root = id;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.g2d = g2d;
}
public void Draw() {
g2d.setPaint(new Color(0, 0, 0));
g2d.setStroke(new BasicStroke(2));
Integer x_top_corner = x - (width / 2);
Integer y_top_corner = y - (height / 2);
g2d.drawRect(x_top_corner, y_top_corner, width, height);
System.out.println(x_top_corner);
if (isOpen) {
g2d.setPaint(new Color(150, 150, 200));
} else {
g2d.setPaint(new Color(200, 200, 200));
}
g2d.fillRect(x_top_corner + 1, y_top_corner + 1, width - 2, height - 2);
}
public void Open() {
this.isOpen = true;
}
public void Close() {
this.isOpen = false;
}
}
class Surface extends JPanel {
private ArrayList<Site> sites = new ArrayList<Site>();
private Graphics2D g2d = null;
private void initSites() {
Integer width = 30;
Integer height = 30;
Integer index = 0;
for (Integer y = height; y < 800 - height - 30; y += height) {
for (Integer x = width; x < 800 - width; x += width) {
sites.add(new Site(index, x, y, width, height, g2d));
index++;
}
}
Integer x = 20;
Integer y = 3;
sites.get(x + (y * 25)).Open();
sites.get(x + 1 + (y * 25)).Open();
sites.get(x).Open();
sites.get(4).Open();
sites.get(9).Open();
sites.get(13).Open();
sites.get(18).Open();
}
private void doDrawing(Graphics g) {
if (g2d == null) {
g2d = (Graphics2D) g;
}
if (sites.size() == 0) {
initSites();
}
for (Site site : sites) {
site.Draw();
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
doDrawing(g);
}
}
public class Squares extends JFrame {
public Squares() {
initUI();
}
private void initUI() {
add(new Surface());
setTitle("Squares demo");
setSize(800, 800);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
Squares ex = new Squares();
ex.setVisible(true);
}
}
You're using Graphics2D as a field assuming that one graphics object is as good as the next, and that's not how you do Swing drawing. The Graphics object given into the paintComponent method is not long-lived or stable, and it must be used and not stored into a field for later use. Instead only use the Graphics object dynamically passed into your program from the JVM via the paintComponent's parameter. For example if you give Draw (rename it to draw) a Graphics or Graphics2D parameter, then pass it in from the paintComponent method:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
class Site {
private Integer x;
private Integer y;
private Integer width;
private Integer height;
// private Graphics2D g2d;
private boolean isOpen = false;
private Integer id = null;
private Integer root = null;
// !! public Site(Integer id, Integer x, Integer y, Integer width, Integer
// height, Graphics2D g2d) {
public Site(Integer id, Integer x, Integer y, Integer width, Integer height) {
this.id = id;
this.root = id;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
// this.g2d = g2d;
}
public void draw(Graphics2D g2d) {
g2d.setPaint(new Color(0, 0, 0));
g2d.setStroke(new BasicStroke(2));
Integer x_top_corner = x - (width / 2);
Integer y_top_corner = y - (height / 2);
g2d.drawRect(x_top_corner, y_top_corner, width, height);
System.out.println(x_top_corner);
if (isOpen) {
g2d.setPaint(new Color(150, 150, 200));
} else {
g2d.setPaint(new Color(200, 200, 200));
}
g2d.fillRect(x_top_corner + 1, y_top_corner + 1, width - 2, height - 2);
}
public void Open() {
this.isOpen = true;
}
public void Close() {
this.isOpen = false;
}
}
class Surface extends JPanel {
private ArrayList<Site> sites = new ArrayList<Site>();
private Graphics2D g2d = null;
private int width;
private int height;
public Surface(int width, int height) {
this.width = width;
this.height = height;
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(width, height);
}
private void initSites() {
Integer width = 30;
Integer height = 30;
Integer index = 0;
for (Integer y = height; y < 800 - height - 30; y += height) {
for (Integer x = width; x < 800 - width; x += width) {
// sites.add(new Site(index, x, y, width, height, g2d));
sites.add(new Site(index, x, y, width, height)); // !!
index++;
}
}
Integer x = 20;
Integer y = 3;
sites.get(x + (y * 25)).Open();
sites.get(x + 1 + (y * 25)).Open();
sites.get(x).Open();
sites.get(4).Open();
sites.get(9).Open();
sites.get(13).Open();
sites.get(18).Open();
}
private void doDrawing(Graphics g) {
if (sites.size() == 0) {
initSites();
}
for (Site site : sites) {
site.draw((Graphics2D) g);
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
doDrawing(g);
}
}
public class Squares extends JFrame {
private static final int SIDE_LENGTH = 800;
public Squares() {
initUI();
}
private void initUI() {
add(new Surface(SIDE_LENGTH, SIDE_LENGTH));
setTitle("Squares demo");
// setSize(800, 800);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setLocationRelativeTo(null);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
Squares ex = new Squares();
ex.setVisible(true);
});
}
}
Also,
run the program from the Swing event thread only
Don't set sizes directly as you're doing. Override getPreferredSize instead
I'm trying to improve my understanding of Java, particularly Java GUI, by making a puzzle program. Currently the user selects an image, which is cut up into a specified number of pieces. The pieces are drawn randomly to the screen but they seem to be covered by blank portions of other pieces, and not all of them show up, but I can print out all the coordinates. I am using absolute positioning because a LayoutManager didn't seem to work. I briefly tried layeredPanes but they confused me and didn't seem to solve the problem. I would really appreciate some help.
Here are the 2 relevant classes:
import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;
public class PuzzlePieceDriver extends JFrame
{
private static Dimension SCREENSIZE = Toolkit.getDefaultToolkit().getScreenSize();
private static final int HEIGHT = SCREENSIZE.height;
private static final int WIDTH = SCREENSIZE.width;
public static int MY_WIDTH;
public static int MY_HEIGHT;
private static BufferedImage image;
private int xPieces = PuzzleMagicDriver.getXPieces();
private int yPieces = PuzzleMagicDriver.getYPieces();
private PuzzlePiece[] puzzle = new PuzzlePiece[xPieces*yPieces];
public Container pane = this.getContentPane();
private JLayeredPane layeredPane = new JLayeredPane();
public PuzzlePieceDriver(ImageIcon myPuzzleImage)
{
MY_WIDTH = myPuzzleImage.getIconWidth()+(int)myPuzzleImage.getIconHeight()/2;
MY_HEIGHT = myPuzzleImage.getIconHeight()+(int)myPuzzleImage.getIconHeight()/2;
setTitle("Hot Puzz");
setSize(MY_WIDTH,MY_HEIGHT);
setLocationByPlatform(true);
pane.setLayout(null);
image = iconToImage(myPuzzleImage); //pass image into bufferedImage form
puzzle = createClip(image);
//pane.add(layeredPane);
setVisible(true);
}//end constructor
public static BufferedImage iconToImage(ImageIcon icon)
{
Image img = icon.getImage();
int w = img.getWidth(null);
int h = img.getHeight(null);
BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
Graphics g = image.createGraphics();
// Paint the image onto the buffered image
g.drawImage(img, 0, 0, null);
g.dispose();
return image;
}//end BufferedImage
protected int randomNumber(int min, int max)
{
int temp =
min + (int)(Math.random() * ((max - min) + 1));
return temp;
}//end randomNumber
private PuzzlePiece[] createClip(BufferedImage passedImage)
{
int cw, ch;
int w,h;
w = image.getWidth(null);
h = image.getHeight(null);
cw = w/xPieces;
ch = h/yPieces;
int[] cells=new int[xPieces*yPieces];
int dx, dy;
BufferedImage clip = passedImage;
//layeredPane.setPreferredSize(new Dimension(w,h));
for (int x=0; x<xPieces; x++)
{
int sx = x*cw;
for (int y=0; y<yPieces; y++)
{
int sy = y*ch;
int cell = cells[x*xPieces+y];
dx = (cell / xPieces) * cw;
dy = (cell % yPieces) * ch;
clip= passedImage.getSubimage(sx, sy, cw, ch);
int myX = randomNumber(0,(int)w);
int myY = randomNumber(0,(int)h);
PuzzlePiece piece=new PuzzlePiece(clip,myX,myY);
puzzle[x*xPieces+y]=piece;
piece.setBounds(myX,myY,w,h);
//layeredPane.setBounds(myX,myY,w,h);
//layeredPane.add(piece,new Integer(x*xPieces+y));
pane.add(piece);
piece.repaint();
}//end nested for
}//end for
return puzzle;
}//end createClip
}//end class
Sorry if the spacing is a little messed up!
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
public class PuzzlePiece extends JPanel
{
private Point imageCorner; //the image's top-left corner location
private Point prevPt; //mouse location for previous event
private Boolean insideImage =false;
private BufferedImage image;
public PuzzlePiece(BufferedImage clip, int x, int y)
{
image = clip;
imageCorner = new Point(x,y);
//repaint();
}//end constructor
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawImage(image, (int)getImageCornerX(),(int)getImageCornerY(), this);
System.out.println("paint "+getImageCornerX()+" "+getImageCornerY());
//repaint();
//g.dispose();
}//end paintComponent
public Point getImageCorner()
{
return imageCorner;
}//end getImageCorner
public double getImageCornerY()
{
return imageCorner.getY();
}//end getImageCornerY
public double getImageCornerX()
{
return imageCorner.getX();
}//end getPoint
}//end class PuzzlePiece
Any help would be appreciated, I've gotten really stuck! Thanks!!
I was really intrigued by this idea, so I made another example, using a custom layout manager.
public class MyPuzzelBoard extends JPanel {
public static final int GRID_X = 4;
public static final int GRID_Y = 4;
private BufferedImage image;
public MyPuzzelBoard(BufferedImage image) {
setLayout(new VirtualLayoutManager());
setImage(image);
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
removeAll();
generatePuzzel();
} else {
Component comp = getComponentAt(e.getPoint());
if (comp != null && comp != MyPuzzelBoard.this) {
setComponentZOrder(comp, 0);
invalidate();
revalidate();
repaint();
}
}
}
});
}
public void setImage(BufferedImage value) {
if (value != image) {
image = value;
removeAll();
generatePuzzel();
}
}
public BufferedImage getImage() {
return image;
}
protected float generateRandomNumber() {
return (float) Math.random();
}
protected void generatePuzzel() {
BufferedImage image = getImage();
if (image != null) {
int imageWidth = image.getWidth();
int imageHeight = image.getHeight();
int clipWidth = imageWidth / GRID_X;
int clipHeight = imageHeight / GRID_Y;
for (int x = 0; x < GRID_X; x++) {
for (int y = 0; y < GRID_Y; y++) {
float xPos = generateRandomNumber();
float yPos = generateRandomNumber();
Rectangle bounds = new Rectangle((x * clipWidth), (y * clipHeight), clipWidth, clipHeight);
MyPiece piece = new MyPiece(image, bounds);
add(piece, new VirtualPoint(xPos, yPos));
}
}
}
invalidate();
revalidate();
repaint();
}
public class VirtualPoint {
private float x;
private float y;
public VirtualPoint(float x, float y) {
this.x = x;
this.y = y;
}
public float getX() {
return x;
}
public float getY() {
return y;
}
public void setX(float x) {
this.x = x;
}
public void setY(float y) {
this.y = y;
}
}
public class VirtualLayoutManager implements LayoutManager2 {
private Map<Component, VirtualPoint> mapConstraints;
public VirtualLayoutManager() {
mapConstraints = new WeakHashMap<>(25);
}
#Override
public void addLayoutComponent(Component comp, Object constraints) {
if (constraints instanceof VirtualPoint) {
mapConstraints.put(comp, (VirtualPoint) constraints);
}
}
#Override
public Dimension maximumLayoutSize(Container target) {
return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
}
#Override
public float getLayoutAlignmentX(Container target) {
return 0.5f;
}
#Override
public float getLayoutAlignmentY(Container target) {
return 0.5f;
}
#Override
public void invalidateLayout(Container target) {
}
#Override
public void addLayoutComponent(String name, Component comp) {
}
#Override
public void removeLayoutComponent(Component comp) {
mapConstraints.remove(comp);
}
#Override
public Dimension preferredLayoutSize(Container parent) {
return new Dimension(400, 400);
}
#Override
public Dimension minimumLayoutSize(Container parent) {
return preferredLayoutSize(parent);
}
#Override
public void layoutContainer(Container parent) {
int width = parent.getWidth();
int height = parent.getHeight();
for (Component comp : parent.getComponents()) {
VirtualPoint p = mapConstraints.get(comp);
if (p != null) {
int x = Math.round(width * p.getX());
int y = Math.round(height * p.getY());
Dimension size = comp.getPreferredSize();
x = Math.min(x, width - size.width);
y = Math.min(y, height - size.height);
comp.setBounds(x, y, size.width, size.height);
}
}
}
}
}
Basically, this uses a "virtual" coordinate system, where by rather then supply absolute x/y positions in pixels, you provide them as percentage of the parent container. Now, to be honest, it wouldn't take much to convert back to absolute positioning, just this way, you also get layout scaling.
The example also demonstrates Z-reording (just in case) and the double click simple re-randomizes the puzzel
Oh, I also made the piece transparent (opaque = false)
Oh, one thing I should mention, while going through this example, I found that it was possible to have pieces placed off screen (completely and partially).
You may want to check your positioning code to make sure that the images when they are laid out aren't been moved off screen ;)
Try using setBorder(new LineBorder(Color.RED)) in your puzzle piece constructor to see where the bounds of your puzzle pieces are. If they are where you'd expect them to be, it's likely that your positioning is wrong. Also make your puzzle pieces extend JComponent instead, or use setOpaque(false) if you're extending JPanel.
There are lots of suggestions I'd like to make, but first...
The way you choose a random position is off...
int myX = randomNumber(0,(int)w);
int myY = randomNumber(0,(int)h);
This allows duplicate position's to be generated (and overlaying cells)
UPDATES (using a layout manager)
Okay, so this is a slight shift in paradigm. Rather then producing a clip and passing it to the piece, I allowed the piece to make chooses about how it was going to render the the piece. Instead, I passed it the Rectangle it was responsible for.
This means, you could simply use something like setCell(Rectangle) to make a piece change (unless you're hell bent on drag'n'drop ;))
I ended up using Board panel due to some interesting behavior under Java 7, but that's another question ;)
package puzzel;
import java.awt.BorderLayout;
import java.awt.EventQueue;
import javax.swing.*;
public class PuzzlePieceDriver extends JFrame {
public PuzzlePieceDriver(ImageIcon myPuzzleImage) {
setTitle("Hot Puzz");
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new BorderLayout());
add(new Board(myPuzzleImage));
pack();
setVisible(true);
}//end constructor
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
ImageIcon image = new ImageIcon(PuzzlePieceDriver.class.getResource("/issue459.jpg"));
PuzzlePieceDriver driver = new PuzzlePieceDriver(image);
driver.setLocationRelativeTo(null);
driver.setVisible(true);
}
});
}
}//end class
A piece panel...
The panel overrides the preferred and minimum size methods...while it works for this example, it's probably better to use setPreferredSize and setMiniumumSize instead ;)
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package puzzel;
import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
public class PuzzlePiece extends JPanel {
private BufferedImage masterImage;
private Rectangle pieceBounds;
private BufferedImage clip;
public PuzzlePiece(BufferedImage image, Rectangle bounds) {
masterImage = image;
pieceBounds = bounds;
// Make sure the rectangle fits the image
int width = Math.min(pieceBounds.x + pieceBounds.width, image.getWidth() - pieceBounds.x);
int height = Math.min(pieceBounds.y + pieceBounds.height, image.getHeight() - pieceBounds.y);
clip = image.getSubimage(pieceBounds.x, pieceBounds.y, width, height);
}//end constructor
#Override
public Dimension getPreferredSize() {
return pieceBounds.getSize();
}
#Override
public Dimension getMinimumSize() {
return getPreferredSize();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
int x = 0;
int y = 0;
g.drawImage(clip, x, y, this);
g.setColor(Color.RED);
g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
}//end paintComponent
}//end class PuzzlePiece
The board panel...used mostly because of some interesting issues I was having with Java 7...Implements a MouseListener, when you run the program, click the board, it's fun ;)
package puzzel;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
/**
*
* #author shane
*/
public class Board extends JPanel {
public static final int X_PIECES = 4;
public static final int Y_PIECES = 4;
private PuzzlePiece[] puzzle = new PuzzlePiece[X_PIECES * Y_PIECES];
private static BufferedImage image;
public Board(ImageIcon myPuzzleImage) {
image = iconToImage(myPuzzleImage); //pass image into bufferedImage form
puzzle = createClip();
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
removeAll();
invalidate();
createClip();
// doLayout();
invalidate();
revalidate();
repaint();
}
});
}
public static BufferedImage iconToImage(ImageIcon icon) {
Image img = icon.getImage();
int w = img.getWidth(null);
int h = img.getHeight(null);
BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
Graphics g = image.createGraphics();
// Paint the image onto the buffered image
g.drawImage(img, 0, 0, null);
g.dispose();
return image;
}//end BufferedImage
protected int randomNumber(int min, int max) {
int temp = min + (int) (Math.random() * ((max - min) + 1));
return temp;
}//end randomNumber
private PuzzlePiece[] createClip() {
int cw, ch;
int w, h;
w = image.getWidth(null);
h = image.getHeight(null);
cw = w / X_PIECES;
ch = h / Y_PIECES;
// Generate a list of cell bounds
List<Rectangle> lstBounds = new ArrayList<>(25);
for (int y = 0; y < h; y += ch) {
for (int x = 0; x < w; x += cw) {
lstBounds.add(new Rectangle(x, y, cw, ch));
}
}
BufferedImage clip = image;
setLayout(new GridBagLayout());
for (int x = 0; x < X_PIECES; x++) {
for (int y = 0; y < Y_PIECES; y++) {
// Get a random index
int index = randomNumber(0, lstBounds.size() - 1);
// Remove the bounds so we don't duplicate any positions
Rectangle bounds = lstBounds.remove(index);
PuzzlePiece piece = new PuzzlePiece(clip, bounds);
puzzle[x * X_PIECES + y] = piece;
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = x;
gbc.gridy = y;
gbc.fill = GridBagConstraints.BOTH;
add(piece, gbc);
piece.invalidate();
piece.repaint();
}//end nested for
}//end for
invalidate();
repaint();
return puzzle;
}//end createClip
}
Now I know you eventually going to ask about how to move a piece, GridBagLayout has this wonderful method called getConstraints which allows you to retrieve the constraints used to layout the component in question. You could then modify the gridx and gridy values and use setConstraints to update it (don't forget to call invalidate and repaint ;))
I'd recommend having a read of How to Use GridBagLayout for more information ;)
Eventually, you'll end up with something like:
I'm trying to make an image of random pixels. I wrote this code, but no usefulness
LadderSnack.java
import java.awt.*;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import javax.swing.*;
public class LadderSnack extends Canvas implements Runnable {
public static JFrame frame = new JFrame("EmiloLadderSnack v. 1.0");
public static int width = Toolkit.getDefaultToolkit().getScreenSize().width, height = Toolkit.getDefaultToolkit().getScreenSize().height;
public boolean run = false;
public Thread thread;
public BufferedImage img;
public int[] pixels;
public Screen screen;
public LadderSnack() {
screen = new Screen(width, height);
img = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
pixels = ((DataBufferInt) img.getRaster().getDataBuffer()).getData();
}
public void start() {
if (run)
return;
run = true;
thread = new Thread(this);
thread.start();
}
public void stop() {
if (!run)
return;
try {
thread.join();
} catch (Exception e) {
e.printStackTrace();
System.exit(0);
}
}
public void run() {
while (run) {
trick();
render();
}
}
private void trick() {
}
private void render() {
screen = new Screen(width, height);
BufferStrategy bs = this.getBufferStrategy();
if (bs == null) {
createBufferStrategy(3);
return;
}
for (int i = 0; i < width * height; i++)
pixels[i] = screen.pixels[i];
Graphics g = bs.getDrawGraphics();
g.drawImage(img, 0, 0, width, height, null);
g.dispose();
bs.show();
}
public static void main(String[] args) {
LadderSnack ladderSnack = new LadderSnack();
frame.setSize(width, height);
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
frame.add(ladderSnack);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
ladderSnack.start();
}
}
Render.java
public class Render {
public int width, height;
public int[] pixels;
public Render(int width, int height) {
this.width = width;
this.height = height;
pixels = new int[width * height];
}
public void draw(Render render, int xOffset, int yOffset) {
int xPixel, yPixel, y, x;
for (x = 0; x < width; x++) {
xPixel = x + xOffset;
for (y = 0; y < height; y++) {
yPixel = y + yOffset;
pixels[xPixel + yPixel * width] = render.pixels[xPixel + yPixel * width];
}
}
}
}
Screen.java
import java.awt.Toolkit;
import java.util.Random;
public class Screen extends Render {
private Render test;
public Screen(int width, int height) {
super(width, height);
int i;
Random rand = new Random();
test = new Render(Toolkit.getDefaultToolkit().getScreenSize().width, Toolkit.getDefaultToolkit().getScreenSize().height);
for (i = 0; i < width * height; i++)
pixels[i] = rand.nextInt();
}
public void render() {
draw(test, 0, 0);
}
}
At runtime
public void render() {
draw(test, 50, 50);
}
in Screen.java
is never executed to move the image
I want the image to move through the frame, as a step to make animation and an animated random pixels image. Please,Help me.
You may want to use my api: http://www.threadox.com/projects/random-image-api/
You receive a buffered image and then you just have to draw it to the canvas.
Your code is a total mess. Here is something you might want to look out : Painting pixels images in Java
And here are the problems :
private void LadderSnack() I think this should be the constructor so should be written private LadderSnack()
Your Runnable implementation should be thought again. Avoid using while(true){} but rather use while(true) {Thread.sleep(xxx)} to avoid your application to freeze.
You create a pixels array of random values but then use pixels = ((DataBufferInt) img.getRaster().getDataBuffer()).getData(); which override your values with whatever is in the databuffer.
You never use your pixels array.
I think you should review the whole concept.
LadderSnack.java
import java.awt.*;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import javax.swing.*;
public class LadderSnack extends Canvas implements Runnable {
public static JFrame frame = new JFrame("EmiloLadderSnack v. 1.0");
public static int width = Toolkit.getDefaultToolkit().getScreenSize().width, height = Toolkit.getDefaultToolkit().getScreenSize().height;
public boolean run = false;
public Thread thread;
public BufferedImage img;
public int[] pixels;
public Screen screen;
public LadderSnack() {
screen = new Screen(width, height);
img = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
pixels = ((DataBufferInt) img.getRaster().getDataBuffer()).getData();
}
public void start() {
if (run)
return;
run = true;
thread = new Thread(this);
thread.start();
}
public void stop() {
if (!run)
return;
try {
thread.join();
} catch (Exception e) {
e.printStackTrace();
System.exit(0);
}
}
public void run() {
while (run) {
trick();
render();
}
}
private void trick() {
}
private void render() {
BufferStrategy bs = this.getBufferStrategy();
if (bs == null) {
createBufferStrategy(3);
return;
}
for (int i = 0; i < width * height; i++)
pixels[i] = screen.pixels[i];
Graphics g = bs.getDrawGraphics();
g.drawImage(img, 0, 0, width, height, null);
g.dispose();
bs.show();
}
public static void main(String[] args) {
LadderSnack ladderSnack = new LadderSnack();
frame.setSize(width, height);
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
frame.add(ladderSnack);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
ladderSnack.start();
}
}
Render.java
public class Render {
public int width, height;
public int[] pixels;
public Render(int width, int height) {
this.width = width;
this.height = height;
pixels = new int[width * height];
}
public void draw(Render render, int xOffset, int yOffset) {
int xPixel, yPixel, y, x;
for (x = 0; x < render.width; x++) {
xPixel = x + xOffset;
for (y = 0; y < render.height; y++) {
yPixel = y + yOffset;
pixels[xPixel + yPixel * width] = render.pixels[xPixel-xOffset + (yPixel-yOffset) * render.width];
}
}
}
}
Screen.java
import java.awt.Toolkit;
import java.util.Random;
public class Screen extends Render {
private Render test;
public Screen(int width, int height) {
super(width, height);
int i;
Random rand = new Random();
test = new Render(333,333);//Toolkit.getDefaultToolkit().getScreenSize().width, Toolkit.getDefaultToolkit().getScreenSize().height);
for (i = 0; i < 333 * 333; i++)
test.pixels[i] = rand.nextInt();
render();
}
public void render() {
draw(test, 50, 50);
}
}