the paint method is called multiple times... how to restrict that? - java

// A program for drawing simple graphs
public class Graphpane extends JPanel {
int node,i,j,flag; // node is for storing no. of nodes... i,j are counters.. flag is for storing option yes or no
ArrayList<Integer> xaxis= new ArrayList<Integer>(); // arraylist for storing x-cordinates
ArrayList<Integer> yaxis= new ArrayList<Integer>(); //arraylist for storing y ordinates
Scanner in= new Scanner(System.in);
public Graphpane(){
super();
System.out.println("Create your own graph!!! :)");
System.out.println("Enter the number of nodes in your graph:");
node= in.nextInt();
System.out.println("Enter the x and the y vertices respectively");
System.out.println("<<<x cordinate shouldn't exceed 600 and y ordinate shouldn't exceed 400>>>");
for(i=0;i<node;i++)
{
System.out.println("Enter the x co-ordinate for"+ i+ "node");
xaxis.add(in.nextInt());
System.out.println("Enter the y ordinate for"+ i+ "node");
yaxis.add(in.nextInt());
}
repaint();
}
public void paint(Graphics g) // paint method
{
for(i=0;i<node;i++)
{
g.fillArc(xaxis.get(i)- 3, yaxis.get(i)-3,6,6,0,360);// drawing points on panel
}
for(i=0;i<node;i++){
for(j=0;j<node;j++) // asking whether the two points are connected or not.. if connected then drawing a line between the two
{
if(i==j) // would skip for the same points as simple graph.. no loops
{
continue;
}
else{
System.out.println("Are the points of index "+i+ " and "+j+" connected?? Say 1 for yes or else for no");
flag=in.nextInt(); // flag for recording whether connected or not
if(flag==1)
{
g.drawLine(xaxis.get(i),yaxis.get(i),xaxis.get(j),yaxis.get(j));
}
//xaxis is arraylist containing x cordinates and yaxis for containing y ordinates.. i and j are indices
//drawing lines between the two points if connected
}
//**Here I am asked whether my two points are connected question multiple times but I want it just once**
}
}
}
}

the paint method is called multiple times… how to restrict that?
You can't. A component will repaint itslef whenthe program invokes repaint on itself or the Swing RepaintManager determines when a component need to be repainted.
//**Here I am asked whether my two points are connected
//question multiple times but I want it just once**
A painting method is for painting only. There should be no user interaction. That is you should not be displaying an option pane or any other kind of message for the user to respond to.
So the code that interacts with the user need so be coded outside of the paintComponent() method.
Also, custom painting should be done in the paintComponent() method, not the paint() method.

Related

Why is JOptionPane.showInputDialog is called more than it should, why and how to prevent it

I am trying to learn some basic java by following a book, one of the exercises requires that I display a set of bar graphics based on user input.
I have to query the user to input the no. of bars to be shown and the length of each.
I use the following:
1`st is the Bar class I defined to draw rectangles corresponding to the numbers inputed
import java.awt.Graphics;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class Bar extends JPanel
{
private int noOfBars; // number of bars to display
int i = 0;
// constructor with choice input
public Bar (int noOfBars)
{
this.noOfBars = noOfBars;
}
// draw desired shapes starting from default position (0, 5) and incrementing for each new bar
public void paintComponent(Graphics g)
{
super.paintComponent(g);
do
{
String input = JOptionPane.showInputDialog("Enter number: ");
int length = Integer.parseInt(input);
for (int j = 1; j <= length; j++)
g.drawRect(0, 5 + i * 20 ,j * 15 , 15);
i++;
} while (i < noOfBars);
}
}
2`nd is the main class:
import javax.swing.JFrame;
import javax.swing.JOptionPane;
public class ShapesExercise
{
public static void main(String[] args)
{
int noOfBars = 0;
// obtain user choice
String input = JOptionPane.showInputDialog("Enter total bars to display:");
noOfBars = Integer.parseInt(input);
if (noOfBars == 0)
{
JOptionPane.showMessageDialog(null,"Invalid number.");
input = JOptionPane.showInputDialog("Enter total bars to display::");
noOfBars = Integer.parseInt(input);
}
JFrame application = new JFrame();
application.setSize(300, 40 + 25 * noOfBars);
Bar panel = new Bar(noOfBars);
application.add(panel);
application.setVisible(true);
application.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
My issue is as follows:
The first message_dialog (the one created in the main class) works correctly, popping up only once and querying for input.
But the message_dialogs generated by the Bar class keep popping up even after reaching the end of the "do while" loop (this was initially a "for" loop but I changed it to "do while" in an attempt to troubleshoot the code).
I have no idea why this happens. While researching online I could not find something related.
Thank you in advance.
paintComponent is called by Swing’s painting architecture, which in turn is largely dependent on the native system. paintComponent can be called several times per second, depending on the circumstances; it may be called once, or several times, when a window is moved or brought to the front. It may be called for each movement of the mouse over it.
You have no control over when paintComponent is called. You must only draw in that method. You MUST NOT call JOptionPane in it. You must not change any state, and you must not change the component or any other components in that method.
If you want to call JOptionPane, do it elsewhere, and then call repaint() to request that the Swing system eventually call your paintComponent method.
You can learn more at https://docs.oracle.com/javase/tutorial/uiswing/painting/.

Print a box with the single center # replaced by a space

I don't know what to do for this. Print a box with a hole in its center. That is, print a box as in normally but with the single # in the center (or one close to the center for boxes with an even width) replaced with a space.
I have the box. How do I calculate the center of the box?
import java.util.Scanner;
public class square {
public square(){
Scanner H=new Scanner(System.in);
int y=H.nextInt();//int for width
int x=H.nextInt();//int for height
int [][]shape=new int[x][y];//2d array stores the above
for(int i=0; i<shape.length; i++){//loops through height and width below
for(int j=0; j<shape[i].length; j++){
System.out.print("#");//prints box of #
}
System.out.println();
}
}
public static void main(String[]args){
new square();
}
}
First of all, not sure why you are making an array (shape) at all, unless that is part of your assignment?
If you use a for loop that steps from 0 to y inside one that steps from 0 to x instead of using the lengths of the array, you will get the same results without needing the array.
As for the center, its location is x/2, y/2. If you change your inner loop to check your variables i and j to see if the current position is x/2, y/2 and print a space instead of an # in that case, I think you will have the results you're looking for.

Overriding paintComponent(); Drawing multiple buffered images

I'm trying to make a tower defense game. So far I've made the JFrame (called GameFrame) where everything is displayed, added a background image (.PNG) and I'm trying to create the monsters and make them "move" over the path I've created. So, I created a GlassPane class which I set as glassPane in the GameFrame.
Here is the code for the GlassPane:
public class GlassPane extends JComponent implements ActionListener{
private ArrayList<MyPoint> path; //list of Points-pixel positions- (x,y pair) that monsters will follow
private ArrayList<Monster> wave; //list of monsters that will try to reach the rift
private Timer timer; //javax.swing.Timer
private boolean waveEnd;//signing that the wave has ended(ignore it)
public GlassPane(){
super();
createPath();//method is below
wave = new ArrayList<Monster>();//monsters added in nextWave()
timer = new Timer(4,this);//timer in order to slow down and smooth the movement of monsters
waveEnd = false;
}
#Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
for(Monster m:wave){
if(m.isVisible())//visible is a private variable in Monster class(defines whether i want to paint that monster or not)
{
g.drawImage(m.getImage(), m.getX(), m.getY(), this);
}
}
}
public void nextWave(){
waveEnd = false;
for(int i =0;i<20;i++)
wave.add(new Monster());
wave.get(0).setVisible(true);//sets the first monster to be visible(paintable)
int visibles = 1;//index 0 is visible,so index 1 is next to become visible
while(!waveEnd){
if(visibles<19 && wave.get(0).getPathIndex()%50 == 0){
//meaning until all 20 monsters are visible(indexes 0-19),"every 50 steps(pixels) the first monster moves"
wave.get(visibles).setVisible(true);
//make another monster visible and start moving it
visibles++;//visible(moving) monsters increased by 1
timer.start();//"move all visible monsters one step forward with a small delay until the next movement to make it soft"
}
JOptionPane.showMessageDialog(new JFrame(), "Finished!","All monsters reached the rift!",JOptionPane.INFORMATION_MESSAGE);//let me know then wave is finished
}
private void createPath(){
//just making a lsit of pixels that I want monsters to follow
path = new ArrayList<MyPoint>();
for(int x=0;x<175;x++){
path.add(new MyPoint(x,0));
}
for(int y=0;y<175;y++){
path.add(new MyPoint(174,y));
}
for(int x=175;x<325;x++){
path.add(new MyPoint(x,174));
}
for(int y=175;y<425;y++){
path.add(new MyPoint(299,y));
}
for(int x=325;x<575;x++){
path.add(new MyPoint(x,424));
}
for(int y=424;y>175;y--){
path.add(new MyPoint(574,y));
}
for(int x=575;x<1001;x++){
path.add(new MyPoint(x,174));
}
}
#Override
public void actionPerformed(ActionEvent arg0) {
for(Monster m:wave)
if(m.isVisible())
m.move(path);
//"move every visible monster by one step"
if(m.get(19).getPathIndex() == 1674)//1674 is the path's last index
waveEnd = true;//if the last monster reaches the end,then all have
//meaning the wave is finished
this.repaint();//refresh any changes to the monsters' positions
}
}
The problem is that only the first monster is moving through the path, meaning only that one is painted when I invoke repaint(). What am I doing wrong in paintComponent(Graphics g)? Because I believe that's where my mistake is... How can I draw all "available" images each time one after another?
Note: They are buffered images in .PNG format if that matters at all.
I though about adding the Monster class code but I believe explaining the methods' function is enough to understand what's going on. Should someone need more details let me know. Thanks in advance.
I think you have to put parantheses around your loops.
The shorthand only loops over the first line after the loop
//Output:
//AAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAA
//BBBBBBBBBBBBBBBBBB
for(Monster m:wave)
System.out.println("AAAAAAAAAAAAAA");
System.out.println("BBBBBBBBBBBBBBB");
//Output:
//AAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAA
//AAAAAAAAAAAAAAAAAA
//BBBBBBBBBBBBBBBBBB
//BBBBBBBBBBBBBBBBBB
//BBBBBBBBBBBBBBBBBB
//BBBBBBBBBBBBBBBBBB
for(Monster m:wave)
{
System.out.println("AAAAAAAAAAAAAA");
System.out.println("BBBBBBBBBBBBBBB");
}

Programming Video Games for the Evil Genius:Project 10:Radical Racing-The Cars

Iv'e looked all over for answers to this book. And I know anyone else who has tried to read this book feels the same way. It's called "Programming Video Games for The Evil Genius" Is there anyone who has read this book? I'm on project 10:Radical Racing-The Cars. Everything compiles correctly but for some reason my cars are not showing up in the correct spot on the JFrame. They should be showing up under the two white lines. I'm positive the code is exactly the same as in the book, but the book is wrong. I have already tried changing the HEIGHT part of the point of origin but no matter what I do it does not budge. I can't attach an image because I don't have a rep of at least 10 .This is the code that the deals with the placement of the cars.
public class TheCars extends JFrame
{
final int WIDTH = 900; int HEIGHT = 650;
double p1Speed = .5, p2Speed = .5;
Rectangle p1 = new Rectangle(WIDTH/9,HEIGHT/2, WIDTH/30,WIDTH/30);
Rectangle p2 = new Rectangle(((WIDTH/9)+((int)((WIDTH/9)*1.5)/2)),(HEIGHT/2)+
(HEIGHT/10),WIDTH/30,WIDTH/30);
//the constructor
public TheCars()
{
//the following code creates the JFrame
super("Radical Racing");
setSize(WIDTH,HEIGHT);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
//start the inner class (which works on it's own because it is a thread)
Move1 m1 = new Move1();
Move2 m2 = new Move2();
m1.start();
m2.start();
}
//this will draw the cars and the racetrack
public void paint(Graphics g)
{
super.paint(g);
//set the color to blue for p1
g.setColor(Color.BLUE);
//now draw the actual player
g.fill3DRect(p1.x,p1.width,p1.width,p1.height,true);
//set the color to red for p2
g.setColor(Color.red);
//now draw the actual player
g.fill3DRect(p2.x,p2.width,p2.width,p2.height,true);
}
private class Move1 extends Thread
{
public void run()
//This should all be in an infinite loop so that the process repeats.
{
while(true)
{
//now put in the try block. This will let
//the program exit if there is an error
try
{
//first refresh the screen
repaint();
//increase speed a bit
if(p1Speed<=5)
p1Speed+=.2;
p1.y-=p1Speed;
//this delays the refresh rate
Thread.sleep(75);
}
catch(Exception e)
{
//if there is an exception (an error), exit the loop
break;
}
}
}
}
private class Move2 extends Thread
{
public void run()
{
//this should all be in an infinite loop so the process repeats
while(true)
{
//now put the code in a "try" block.
//this will let the program exit if there is an error
try
{
//first refresh the screen
repaint();
//increase the speed a bit
if(p2Speed<=5)
p2Speed+=.2;
p2.y-=p2Speed;
//this delays the refresh rate
Thread.sleep(75);
}
catch(Exception e)
{
//if there is an exception (an error), exitthe loop
break;
}
}
}
}
public static void main(String[]args)
{
new TheCars();
}
}
Assuming you're painting those Rectangle objects directly onto the screen, we have to assume that the expressions "HEIGHT/2" and "HEIGHT/2 + HEIGHT/10" are coming out equal, and nonzero but small. That would be the case if HEIGHT is an int, and the value is more than 2 and less than 10. Presumably the value would need to be a couple hundred, at least, for those boxes to show up in the middle of the screen. Check the value of HEIGHT (just using a System.out.println() would be fine) and make sure it's the actual height of the window.
EDIT
Now that we see the rest of the code: the second argument to each call to fill3DRect() is wrong. It should be the y member of the Rectangle objects, which is a large, varying number, but you're passing the width member, which is a small fixed number. Change both the calls to look like this and you'll be back on track:
g.fill3DRect(p1.x, p1.y, p1.width, p1.height, true);

Guessing game using JOptionPane - Java

So I have been studying Java for about 8 weeks and for class I had to design a shape guessing game. Yes it is Homework. So I have built my four shape classes with an example of one below.
public class square extends shape {
//square member variables
boolean equalSides = true;
// check if sides are equal
public boolean isEqual() {
return equalSides;
}
//constructor
public square(int numsides, String shapeName, boolean b, String shapehint) {
super(numsides, shapeName, shapehint);
}
}
I then created a shape.java class
public class shape {
int numSides;
String shapeName;
String shapeHint;
public shape(int numsides, String shapename, String shapehint) {
numSides = numsides;
shapename = shapeName;
shapehint = shapeHint;
}
//getter methods
public int getSides() {
return numSides;
}
public String getName(){
return shapeName;
}
public String getHint(){
return shapeHint;
}
}
It's now that I have reached the shapeGuesser class that I am starting to struggle just a little. I'm unsure how to incorporate a guard for my game and the JOptionPane side of it. I need shapeGuesser to run until the user guesses the correct shape.
I have been instructed to present the user with this option at the start.
What question shall I ask?
Enter Number:
1.How many sides?
2.Are your sides the same length?
3. Hint
Based on the number you enter 1, 2 or 3.That question will be asked of that
shape. So your Shape must have an appropriate response ready.
import javax.swing.JOptionPane;
import java.util.Random;
public class shapeGuesser {
public static void main(String[] args, Object Do) {
// TODO Auto-generated method stub
// the shape the program uses
int random;
shape shapeChoice;
// create shapes
square s = new
square(4, "Square", true, "Geeks were called this in the 80s");
Rectangle r = new Rectangle(4, "Rectangle", false, "Not Pentangle");
Triangle t = new Triangle(3, "Triangle",false, "Toblerone");
Circle c = new Circle(0, "Circle",true, "Circle Circle Circle");
//declare shape array
shape[] Shapes;
//create shape array
Shapes = new shape[4];
//put shape objects in shape array
Shapes[0] = s;
Shapes[1] = r;
Shapes[2] = t;
Shapes[3] = c;
// generate random number
random = (int) (1 + (Math.random() * 3));
//pick shape from shape array based on random number
shapeChoice = Shapes[random];
}
}
Anyone who read this far and might have the time to enlighten me in anyway. It would be much appreciated.
Thanks,
isEqual() needs an implementation in the base class, shape, as with all methods you want to call on all your shapes. Have the base shape return false. (Ideally shape should be abstract so you can't have a basic shape object, only squares, rectangles, etc. but its okay, you're new, and nobody else will use this. So you yourself can just never create a base shape. But for the future, that's what abstract is for ^^) Then, have all your other shapes override that base isEqual() the way your square already does.
You're doing good! You've selected a random shape, and created many shapes.
Now create a loop that prints the options,
system.out.println("Enter Number: 1.How many sides? 2.Are your sides the same length? 3. Hint");
Then take the user input, and parse it to an integer. Have an if/else/else or a switch/case using that integer. (alternatively, use if/else/else with the string as it is, but make sure to use .equals() not ==)
So now you've asked a question, and selected one. Now you print out
if(userInput.equals("1")){
system.outprintln("How many sides? " + shapeChoice.getSides());
}
Do the same thing for 2 and 3. You'll be dealing with a shapeChoice, so you have to call the base methods of shape. However, at runtime, if the object is a square or a rectangle, when you call shapeChoice.getSides() it will invoke the square or rectangle implementation, giving you the answer you want! :)
Then all you have to do is loop back, ask the question over and over again, and if the user wants to, let him guess, and check his answer! (compare .equals(shapeChoice.getName()))
so have a big while(true) forever loop, inside of which you can ask a question, and then check if they want to answer. If they answer right, you break out. Otherwise, you loop back around, and keep asking them which hint they'd like.
EDIT: Actually, now that I look at it, since you're practicing polymorphism, you should probably use it a little more. Right now, You have your separate classes, but you're passing in all your information when you construct them. Instead of:
square s = new square(4, "Square", true, "Geeks were called this in the 80s");
Rectangle r = new Rectangle(4, "Rectangle", false, "Not Pentangle");
Have it be more like
square s = new square();
and have part of square's definition inherently define
public class square extends shape {
//square member variables
boolean equalSides = true;
int numSides = 4;
//and so on
//OR even better, don't define them, since the base class already does!
//merely set the values in the constructor
public square(){
numSides = 4;
equalSides = true;
shapeHint = "Geeks were called this in the 80s";
}
}
EVERY square object is going to be this way, so there's no reason it should be a parameter. That is part of the definition of a square.

Categories

Resources