kinda new to swing and making guis in general, I have been using the drawLine method to create a "floors" and it has been working for the variables of the coordinates are local the the paintComponent class, however I want to change the coordinates using an actionListerner / Timer, so I need the variables to be accessible to the whole class.
This is probably a real simple fix (??) and I'll look like a fool for asking it here, but I can't work it out.
Here is the code.
class Elevator extends JPanel implements ActionListener {
private int numberOfLines = 0;
private int j = 60;
int yco = j - 125;
final private int HEIGHT = 50;
final private int WIDTH = 80;
final private boolean UP = true;
final private int TOP = 60;
final private int BOTTOM = j - 125;
private int mi = 5;
private Timer timer = new Timer(100, this);
public Elevator(int x) {
this.numberOfLines = x;
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (int i = 0; i < numberOfLines; i++) {
g.drawLine(0, j, getWidth(), j);
j += 75;
}
g.drawRect(getWidth() / 2, yco, WIDTH, HEIGHT);
}
#Override
public void actionPerformed(ActionEvent e) {
}
}
Any help is appreciated thanks in advance.
The reason your lines are not showing now that your variables (specifically j in your example code) are class variables, is that they are "remembering" state between calls to paintComponent(). Specifically, when j was local, it always got set back to its initial value (presumably j=60). Now however, it gets incremented in your for loop in the line
j += 75;
and never reset to a lower value. This means the second or third time that paintComponent() gets called, the value of j is too large, and your lines are being drawn outside the visible area (off screen). A Java Swing component can easily have its paintComponent() method called two, three, or more times before the component is even rendered on the screen, which is why your lines are no longer getting drawn (well, technically they are getting drawn, just not anywhere you can see them).
To fix this, you can add to your paintComponent() method the single line
j = 60;
just before the for loop. But at this point, you might as well just keep j local (unless you need to read its value but not change it with the timer).
Alternatively, if j needs to change over time, just make sure it gets set inside actionPerformed() by the timer, thenwork with a copy of the value inside of paintComponent() instead of directly with the value of j. As an example:
public void paintComponent(Graphics g) {
...
int jCopy = j;
for (int i = 0; i < numberOfLines; i++) {
g.drawLine(0, jCopy, getWidth(), jCopy);
jCopy += 75;
}
...
}
public void actionPerformed(ActionEvent e) {
...
j += 5;
//If you don't cap it at some max,
//it will go off the bottom of the screen again
if (j > 300) {
j = 60;
}
...
}
This will help stop consistency issues that could be caused by modifying j in both paintComponent() and actionPerformed().
Related
For a school project, I need to create an applet that produces a 10 x 10 grid in which each cell will change color in accordance to what some threads are doing in the background. I have all of the rest figured out, but I don't have the slightest clue as to how to display this grid. This is the only example code we were given:
import java.awt.*;
import java.applet.Applet;
public class Array2 extends Applet {
private final ststic int LIMIT = 9;
private int[][] results;
public void init() {
int count = 1;
results = new int [LIMIT][LIMIT];
for (int i = 0; i < LIMIT; i++) {
for (int j = 0; j < LIMIT; j++) {
results[i][j] = count % 2;
count++;
}
}
}
public void paint (Graphics g) {
int xLoc = 25;
int yLoc = 25;
for (int i = 0; i < LIMIT; i++) {
for (int j = 0; j < LIMIT; j++) {
g.drawString(Integer.toString(results[i][j]), xLoc. yLoc);
xLoc += 20;
}
xLoc = 25;
yLoc += 20;
}
}
}
This ends up printing a blank 2 x 2 grid. This is easy enough to modify into a 10 x 10. However, what I DON'T know how to do is color the squares. Everything I've searched mentions using jPanels or jFrames or something, but this HAS to be an applet. I was just looking for some suggestions as to what I should look into for the coloring process, as this is literally all I have to go on.Thanks!
The applet draws with the class Graphics and passes you an instance in the paint method. You can use Graphics to do many cool things on the screen, so check its methods out! But to draw a colored square, first set the color using g.setColor(color) and then use g.fillRect(xLoc, yLoc, size, size) with xLoc and yLoc being the top-left coordinates of the square.
Albert provided me with the Graphics methods needed to finish this up as an applet. However after reading through the comments and links provided, it looks like I'll just be using Swing instead of AWT.
Im doing an assignment for school. I have to create 30 randomly colured GameObjects in random locations. I have to use 2 classes, a GameObject class which contains the GameObject data, x and y co-ordinates and colour, plus the move and paint method... and a main MovingSquaresApplication which puts the GameObjects into an array and calls the paint() and move() methods... the current program compiles, runs, paints 60 squares (paint() and repaint()) but no animation. I've looked at a lot of different posts but still cant get it right. Any help would be great. Here is the code.....
*edited new code
import java.awt.*;
import javax.swing.*;
public class MovingSquaresApplication extends JFrame implements Runnable {
//member data
private static final Dimension WindowSize = new Dimension(600,600);
private static final int NUMGAMEOBJECTS = 30;
private GameObject[] gameObjectsArray = new GameObject[NUMGAMEOBJECTS];
private int i,j;
//constructor
public MovingSquaresApplication(){
this.setTitle("MovingSquaresApplication");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Display the window, centered on the screen
Dimension screensize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
int x = screensize.width/2 - WindowSize.width/2;
int y = screensize.height/2 - WindowSize.height/2;
setBounds(x, y, WindowSize.width, WindowSize.height);
setVisible(true);
for (i=0; i<gameObjectsArray.length; i++){ //fills array with GameObjects
GameObject NewSquare = new GameObject();
gameObjectsArray[i] = NewSquare;
}
Thread t = new Thread(this); //creates and stars a thread
t.start();
}
//threads entry point
public void run(){
while (true){
try {
Thread.sleep(20);
for (j=0; j<gameObjectsArray.length; j++){
gameObjectsArray[j].move();
repaint();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//applications paint method
public void paint (Graphics g){
super.paintComponents(g);
for (i=0; i<gameObjectsArray.length; i++){
gameObjectsArray[i].paint(g);
}
}
//applications entry point
public static void main(String[] args){
MovingSquaresApplication w = new MovingSquaresApplication();
}
}
and the GameObject class
import java.awt.*;
public class GameObject {
//member data
private int x,y,xvel=2,yvel=2;
private Color c;
public GameObject(){
x = (int) (Math.random( )*600);
y = (int) (Math.random( )*600);
int R = (int) (Math.random( )*256);
int G = (int)(Math.random( )*256);
int B= (int)(Math.random( )*256);
c = new Color (R, G, B);
}
//public interface
public void move(){
x += xvel;
y += yvel;
if(x<10)
{
xvel = 2;
}
else if(y<30)
{
yvel = 2;
}
else if(x>=560)
{
xvel = -2;
}
else if(y>=560)
{
yvel = -2;
}
}
public void paint(Graphics g){
g.setColor(c);
g.fillRect(x, y, 30, 30);
}
}
Thanks for all the help, much appreciated
Thanks for the help, I didnt create a class that extends JPanel, i simply put
super.paintComponent(g);
in the paint method, not sure if thats good practice but it worked....also on a sidenote i haven't seen this before
for (GameObject gameObject : gameObjectArray)
what exactly does this do compared to the loop i've used?
You need to paint within the paintComponent method of a JPanel (as I'm sure that you've read,.... and so I recommend that you do just that. That you
Fill your GameObjectsArray with GameObjects, and do so not within any painting method, but on GUI construction
Create a JPanel and override its paintComponent method just as the Swing painting tutorials tell you to do
Call the super's paintComponent method within your override (again as the tutorials will tell you to do)
iterate through your GameObjectsArray (but rename it gameObjectsArray since its a variable not a class) within the paintComponent method
call each GameObject's paint method within that same for loop.
edit: check out this code of yours:
GameObject MoveSquare = new GameObject();
for (y = 0; y < GameObjectsArray.length; y++) {
MoveSquare.move();
}
What you're doing is creating a completely new GameObject object, MoveSquare, and are trying to move it within the for loop, meanwhile you're not touching any of the GameObjects held within the gameObjectsArray. Do you see your mistake here?
Edit 2
Also you're using the y variable as the array index, the same variable that you're using to figure out the y-axis bounds of your GUI -- don't do this. Use a completely independent variable.
Edit 4
And here:
public void paint(Graphics g) {
for (y = 0; y < GameObjectsArray.length; y++) {
GameObject NewSquare = new GameObject();
if (GameObjectsArray[y] == null) {
GameObjectsArray[y] = NewSquare;
NewSquare.paint(g);
}
}
}
You're creating new GameObject variables with each call to paint, ignoring any that already may be present in the array(??). Painting methods should be for painting and painting only. Again, fill your GameObject array with new GameObject items once and in your class constructor, not in a painting method.
You're doing a lot of guessing here, and throwing code at the wall and seeing what sticks is not a good heuristic for creating a program. Instead plan each step on paper before committing code to IDE.
Edit 5
Your if conditions within the GameObject move method need to be fixed. But once you get the code running, you'll see exactly what I mean, as you'll see all your GameObjects running off of the page.
Edit 6
I'm not going to show you all your code, but again, you'll want to create a class that extends JPanel, override its paintComponent method, and that method will be quite simple and look like this:
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g); // do housekeeping painting
// note that I've renamed the array to gameObjectArray
for (GameObject gameObject : gameObjectArray) {
gameObject.paint(g); // this is all you need to call
}
}
Likewise, the run method would be something like:
#Override
public void run() {
while (true) {
try {
Thread.sleep(SLEEP_TIME); // constant for the sleep time
} catch (InterruptedException e) {
e.printStackTrace();
}
// again note that the array has been renamed
for (GameObject gameObject : gameObjectArray) {
gameObject.move(); // this is all that needs to be called here
}
repaint();
}
}
Edit next :)
You're creating two Thread objects and starting them both -- don't do that. Only one will do.
class DrawPane extends JPanel
{
//size is the size of the square, x and y are position coords
double size = 1, x = 0, y = 0;
double start = (-1) * size;
public void paintComponent(Graphics shape)
{
for(x = start; x <= scWidth; x += size)
{
shape.drawRect((int)x, (int)y , (int)size, (int)size);
//When a row is finished drawing, add another
if(x >= scWidth)
{
x = start; y += size;
}
//Redraws the entire grid; makes the for loop infnite
else if(y >= scHeight)
{
x = start; y = start;
}
}
}
}
I'm confused as to why JPanel refuses to work with the loop once I make it infinite. How would I go about allowing it to do so?
When you make the loop "infinite" you effectively tie up and freeze the Swing event thread preventing Swing from doing anything. Instead use a Swing Timer to drive your animation.
e.g.,
class DrawPane extends JPanel {
//size is the size of the square, x and y are position coords
double size = 1, x = 0, y = 0;
double start = (-1) * size;
public DrawPane() {
int timerDelay = 200;
new Timer(timerDelay, new ActionListener(){
public void actionPerformed(ActionEvent e) {
x += size;
if (x >= scWidth) {
x = start;
y += size;
}
if (y >= scHeight) {
x = start;
y = start;
}
repaint();
}
}).start();
}
public void paintComponent(Graphics g) {
super.paintComponent(g); // Don't forget to call this!
g.drawRect((int)x, (int)y , (int)size, (int)size);
}
}
The paint function is supposed to update the Paint and get out of the way. You really shouldn't be putting in complex logic and definitely not infinite loops there.
Just do what you have (except get rid of the reset stuff that makes your loop infinite) and put repaint() in an infinite loop (preferably with some timer logic) somewhere else in your program.
It will never break out of the paintComponent loop and update the GUI. The GUI will only update once the paintComponent method finishes. If you want to make the loop infinite, you need to take the code out of your event handler and be calling repaint() from elsewhere, possibly using a timer to do so.
So I've got JPanel inside JScrollPane. Is there any possible way to display only visible area on JPanel? Right now my piece of code looks like that:
public void paintComponent(Graphics g)
{
super.paintComponent(g);
for(int i = 0; i < m_xTiles; i++)
{
for(int j = 0; j < m_yTiles; j++)
{
m_mapTiles.get(i).get(j).DrawImage(g);
}
}
}
It's not something that I want because it's display everything and when I want to make a very big map the movment are very slow. I want to get visible rectangle on screan - positions of four pixels and then I will calculate to my own x and y :)
Normally the graphic system cares about clipping operations outside the visible bounds so these operation become no-ops and are not expensive. So in most cases you don’t need to deal with these information as the graphics operations are the expensive part when painting.
However, sometimes you may encounter the situation that the operations you perform before the calls on the Graphics object become expensive, i.e. if you have a really large but only partially visible component (as you described). In this situation it might be useful to access the clipping information to perform manually skipping, especially if you are tiling your area, thus calculating which items to process is rather easy:
public void paintComponent(Graphics g) {
super.paintComponent(g);
// I assert equal tiling of the actual size here, otherwise you may define
// the tile sizes as constants and calculate preferredSize as tileWidth*m_xTiles
// and tileHeight*m_yTiles, respectively
final int tileWidth=getWidth()/m_xTiles;
final int tileHeight=getHeight()/m_yTiles;
Rectangle clip = g.getClipBounds();
int firstX, lastX, firstY, lastY;
if(clip == null) {
firstX=0; lastX=m_xTiles-1;
firstY=0; lastY=m_yTiles-1;
}
else {
firstX=clip.x/tileWidth; lastX=(clip.x+clip.width)/tileWidth;
firstY=clip.y/tileHeight; lastY=(clip.y+clip.height)/tileHeight;
}
// note that the loop condition is <= now to handle partially visible tiles
for(int i = firstX; i <= lastX; i++)
{
for(int j = firstY; j <= lastY; j++)
{
m_mapTiles.get(i).get(j).DrawImage(g);
}
}
}
What I'm trying to accomplish is adding 1 to the all the numbers in shipX array list, question is, how? I want to do this when the method move() is called, but how would I make this happen, as I'm new with arrays
class Ship
{
public void paint(Graphics g)
{
int shipX[] = {500,485,500,515,500};
int shipY[] = {500,485,455,485,500};
g.setColor(Color.cyan);
g.fillPolygon(shipX, shipY, 5);
}
public void move()
{
}
}
To start, you will have to move your arrays for points outside the local scope of paint() and into the class so that move() has access to the current values. You would increment in the move() method and call whatever routine you use to redraw your component.
class Ship
{
//make your polygon points members of the class
//so that you can have state that changes
//instead of declaring them in the paint method
int shipX[] = {500,485,500,515,500};
int shipY[] = {500,485,455,485,500};
//set these to the amount you want per update. They can even be negative
int velocityX = 1;
int velocityY = 1;
public void paint(Graphics g)
{
g.setColor(Color.cyan);
g.fillPolygon(shipX, shipY, 5);
}
public void move()
{
//add 1 to each value in shipX
for (int i=0; i<shipX.length; i++)
{
shipX[i] += velocityX;
}
//add 1 to each value in shipY
for (int i=0; i<shipY.length;i++)
{
shipY[i] += velocityY;
}
//call whatever you use to force a repaint
//normally I would assume your class extended
//javax.swing.JComponent, but you don't show it in your code
//if so, just uncomment:
//this.repaint();
}
}
Although I should point out that the repaint() method on JComponent does need to be called from the correct Swing thread, as pointed out in this answer.
If you are also trying to animate the movement, you can check out the Java Tutorial on Swing timers to call your move() method on a schedule. You could also use an ActionListener on a button to either control the Timer or on a button to move the object manually once per click.
All you have to do is iterate through the array and modify the value of each index:
for (int i = 0; i < shipX.length; i++)
{
shipX[i]++;
}
Increase the numbers one by one...
for (i=0; i<shipX.length; i++)
{
shipX[i]++; // same as shipX[i] = shipX[i] +1
}
for (i=0; i<shipY.length;i++)
{
shipY[i]++;
}