Adding buttons on a paintComponent drawn shape - java

My program is consist of two classes(test and paintClass) in different files. In the paintClass class I draw a 5x5 square board by using paintComponent method. I want to add buttons in each small square in the big square. When I run the code I don't get any buttons. I want to have 25(5x5) buttons by using jpanel on a shape drawn by paintComponent. Is this possible? If it is, how I can do it?
EDIT : The problem was the loop. Number had a default value of 0 so the loop didn't work. I defined number at the beginning. It solved the problem. Also one of the invervals were wrong. I changed j = 0 with j = 1.
import javax.swing.*;
import java.awt.*;
public class test
{
public static void main(String[] args)
{
JFrame frame = new JFrame("buttons");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(250,250);
PaintClass paint = new PaintClass();
paint.repaint();
f1.getContentPane().add(paint);
frame.pack();
frame.setVisible(true);
}
}
import javax.swing.*;
import java.awt.*;
public class PaintClass extends JPanel
{
private Graphics g;
private int interval,side,number;
private JButton button;
public PaintClass()
{
number = 5;
button = new JButton();
setLayout(new GridLayout(5,5));
for(int i = 0; i <= number - 1; i++)
{
for(int j = 1; j <= number - 1; j++)
{
button = new JButton();//ADDED
button.setBounds(i * interval, 0, interval, interval);
add(button);
}
button = new JButton();//ADDED
button.setBounds(0, i * interval, interval, interval);
add(button);
}
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
this.repaint();
side = 250;
number = 5;
interval = side / number;
g.drawRect(0,0, side, side);
for(int i = 0; i <= number - 1; i++)
{
for(int j = 0; j <= number - 1; j++)
{
g.drawLine(i * interval, 0, i * interval, side);
}
g.drawLine(0, i * interval, side, i * interval);
}
}
}

Choose one or the other: either add the buttons using the GridLayout, or paint the buttons using paintComponent. If the former, you should a) define the loop constraint (right now it is 0) b) create a new JButton for every loop (your code currently reuses the instance) and c) register the appropriate ActionListener to respond to events. If the latter, you need to register the appropriate listener (like MouseListener) to respond to user generated events.

private int interval,side,number;
Number has a default value of 0.
for(int i = 0; i <= number - 1; i++)
Since number is 0, your loop will never execute.
Once you do this the buttons will be added to the panel but they will cover your custom painting. To see background lines you just need to set the background of the panel to Color.BLACK and then create your GridLayout with a gap between the components. Read the API for the method to use.

Related

Chess GUI - Getting 2 user clicks before calling a funtion - Java

I am trying to make a simple chess GUI, that consists only in a gridLayout where each square is made of a button, each button has an ActionListener and works independantly of the actual game, only "printing out" what is happening in the Board class.
private JButton[][] squares = new JButton[8][8];
private Color darkcolor = Color.decode("#D2B48C");
private Color lightcolor = Color.decode("#A0522D");
public ChessGUI(){
super("Chess");
contents = getContentPane();
contents.setLayout(new GridLayout(8,8));
ButtonHandler buttonHandler = new ButtonHandler();
for(int i = 0; i< 8; i++){
for(int j = 0;j < 8;j++){
squares[i][j] = new JButton();
if((i+j) % 2 != 0){
squares[i][j].setBackground(lightcolor);
}else{
squares[i][j].setBackground(darkcolor);
}
contents.add(squares[i][j]);
squares[i][j].addActionListener(buttonHandler);
squares[i][j].setSize(75, 75);
}
}
setSize(600, 600);
setResizable(true);
setLocationRelativeTo(null);
setVisible(true);
}
private class ButtonHandler implements ActionListener{
public void actionPerformed(ActionEvent e){
Object source = e.getSource();
for(int i = 0;i< 8; i++){
for(int j = 0;j < 8;j++){
if(source == squares[i][j]){
//Pass set of coordinates to game.Move(...)
return;
}
}
}
}
}
I need to pass 2 sets of coordinates, the "from" and the "to" to the game.Move(...) function(function that aplies game movement logic), where each set of coordinates is given by a click in a button.
How should i handle the fact that i need to wait for the user to make 2 clicks before calling the game.Move(...) function? It seems it can only pass 1 set of coordinates at a time.
Any help would be apricieated.
In game object you should probably have field variables that will store the from and to coordinates instead of passing them into game.move function. You can make a counter that starts at 0. When it is 0, it will process the "from" click (set the "from" variable in Game to the coordinate that was clicked), and when it is 1, it will process the "to" click (set "to" variable in Game), call move, and reset counter back to 0.

Draw random circles, coloring in red any circle not intersecting another circle

I have a Java Swing assignment with the following objectives:
When the program starts, it draws 20 unfilled circles, with radius and location of each determined at random.
If the perimeter line of a circle does NOT intersect any other circle, draw the outline of the circle in RED. If it does intersect at least one other circle, draw it in BLACK.
Add a JButton that, each time it is pressed, creates a new set of circles as described above.
I've completed objectives #1 and #3 above, but I'm stumped on objective #2.
Before I present the code, let me give my understanding of the math behind it. There are two ways a circle can NOT intersect another circle:
The circles are too far apart to share a perimeter point, i.e. the distance between their centers is greater than the sum of their radii (d > r1 + r2). Example.
One circle is completely inside another circle, and their perimeters do not touch, i.e. the distance between their centers is less than the difference between their radii (d < |r1 - r2|). Example.
What I've got so far:
To compare circles, they must be specified before they are drawn, so I used a for-loop to store 20 values in arrays for the center coordinates (int[] x, int[] y) and the radius (double[] radius).
Next, I used nested for-loops to iterate through the array and compare two circles, except when a circle is compared with itself (index j = index k). If the circles intersect, g.setColor(Color.RED). If not, g.setColor(Color.BLACK).
When I execute my code, the circles without any overlap are properly colored red. However, some of the overlapping circles are colored red as well. I assume that they were non-overlapping at the time they were drawn, but were intersected thereafter. How do I fix the code to account for this discrepancy in time? (Problem area located near the bottom, in IntersectingCircles class)
import java.awt.BorderLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
import javax.swing.JFrame;
import javax.swing.JButton;
public class ButtonFrame extends JFrame
{
private final JButton resetButton = new JButton("Reset");
public ButtonFrame()
{
super("Drawing Random Circles");
setLayout(new BorderLayout());
IntersectingCircles intersectingCircles = new IntersectingCircles();
this.add(intersectingCircles, BorderLayout.CENTER);
this.add(resetButton, BorderLayout.SOUTH);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setSize(1400, 1400);
ButtonHandler handler = new ButtonHandler();
resetButton.addActionListener(handler);
}
private class ButtonHandler implements ActionListener
{
#Override
public void actionPerformed(ActionEvent event)
{
reset();
}
}
public static void main(String[] args)
{
ButtonFrame buttonFrame = new ButtonFrame();
buttonFrame.setVisible(true);
}
public void reset()
{
ButtonFrame buttonFrame = new ButtonFrame();
buttonFrame.setVisible(true);
}
}
class IntersectingCircles extends JPanel
{
private static final JButton resetButton = new JButton("Reset Circles");
private static final JFrame frame = new JFrame("Intersecting Circles");
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
this.setBackground(Color.WHITE);
int[] x = new int[20];
int[] y = new int[20];
int[] diameter = new int[20];
double[] radius = new double[20];
for (int i = 0; i < 20; i++)
{
int xCoord = (int)(Math.random() * 600);
int yCoord = (int)(Math.random() * 600);
int circleSize = (int)(Math.random() * 550);
x[i] = xCoord;
y[i] = yCoord;
diameter[i] = circleSize;
radius[i] = circleSize / 2.0;
}
for (int j = 0; j < 20; j++)
{
for (int k = 0; k < 20; k++)
{
if (k != j)
{
if (((Math.sqrt((x[k] - x[j]) * (x[k] - x[j]) + (y[k] - y[j])
* (y[k] - y[j]))) > (radius[j] + radius[k])) ||
((Math.sqrt((x[k] - x[j]) * (x[k] - x[j]) + (y[k] - y[j])
* (y[k] - y[j]))) < (Math.abs(radius[j] - radius[k]))))
g.setColor(Color.RED);
else
g.setColor(Color.BLACK);
g.drawOval(x[j], y[j], diameter[j], diameter[j]);
}
else
continue;
}
}
}
}
You have logic mistake in if statement inside the cycle - you can set black color then revert to red for some other pair circle. Possible solution draft:
for (int j = 0; j < 20; j++)
{
g.setColor(Color.RED); //set non-intersect state
for (int k = j + 1; k < 20; k++) //avoid excessive work
{
if (intersect test)
{
g.setColor(Color.BLACK);
break; //can stop here
};
g.drawOval(x[j], y[j], diameter[j], diameter[j]);
}
}

Java GUI - Adding text field

I'm not really keen on Java GUI, but am learning as I go. I am in the process of making a very simple and basic Sudoku puzzle. Right now I am just on the basic layout of it.
I wanted to see if there was a simple way of adding a text field to each little rectangle that I have drawn out (81 total rectangles - 9x9 puzzle). So that a user can type something in there.
I am delving into it, but wanted to get the code up here to see if anyone had any tips, cause truth be told, I am mega lost with this.
Here is what I have so far...
import java.awt.*;
import javax.swing.*;
class MyCanvas extends JComponent {
public void paint(Graphics g) {
int coordinateX = 0;
int coordinateY = 0;
// maximum 9 rows
int rows = 9;
int columns = 1;
// make a 9x9 puzzle
while (columns <= rows) {
//for loop to increment the boxes
for (int j = 1; j <= rows; j++) {
// add and assign coordinte x... equivalent to x = x + 30
coordinateX += 30;
// where x and y determine start of the box and 30 determines the size
g.drawRect(coordinateX, coordinateY, 30, 30);
} //end of for loop
//reset the value of x to start a new row
coordinateX = 0;
coordinateY += 30;
columns++;
} //end of while loop
} //end of void paint
} //end of class
public class DrawRect {
public static void main(String[] a) {
JFrame window = new JFrame();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setBounds(100, 100, 500, 500);
window.getContentPane().add(new MyCanvas());
window.setVisible(true);
} // end of void main
} // end of class
Hopefully someone has some pointers that could help me out, cause boy oh boy do I need it. Was kinda thrown into the lion's den without prior knowledge or practice, but I'm trying hard.
Thanks guys!!
You could use a GridLayout(9,9) and an array of arrays of JTextField's
This is, of course, just an example of how I would do it. There are other ways to do this.
Find below a generic example.
Solution
public static void main(String[] args) {
JTextField[][] boxes = new JTextField[9][9];
JFrame frame = new JFrame();
frame.setLayout(new GridLayout(9,9));
frame.setSize(500, 500);
for (int i = 0 ; i < 9 ; i++){
for (int j = 0 ; j < 9 ; j++){
boxes[i][j] = new JTextField("0");
frame.add(boxes[i][j]);
}
}
frame.setVisible(true);
}
Output

Changing the position of images on the JFrame

So I am making this ABC learning game , What I want to do is if I click on the A button more than once then , the three Images will change their position, What I want to do is this
![enter image description here][1]
When I click on A button, the three image will appear on the screen, the first is apple as I set it that way in the loop, but the second two images will appear randomly, though sometimes one o them is apple again, I could fix that.
My Question is, how can I change that position of the Apple to the second and second image to the first and third image to the second position if the "A" button is clicked more than once.
SO, the result will be the apple will change position based on the click "A" button and other two picture changes their position and chosed randomly from the array.
So, here is my code for the JPanel, where everything takes place.Most of the code is explained in the comments
import java.awt.*;
import java.awt.event.ActionListener;
import javax.swing.*;
import java.awt.event.*;
import java.text.AttributedCharacterIterator;
import java.util.Random;
import javax.swing.ImageIcon;
/**
*
* #author Dip
*/
public class AbcGeniusPanel extends JPanel implements ActionListener {
//Declare the necessary Variables here
private JButton[] buttons; //create an array for buttons
private BorderLayout layout; //Declare object of BorderLayout
private Image image = null;
private boolean showImage = false;
//Initialize all the variables here
static int index = 0;
int randNumber = 0, id = 0;
int q = 0, w = 0;
int buttonClick = 0;
//Store all the imahges that will appear on the screen into an String type array
private static String[] imageList = {"src/Images/1.png", "src/Images/2.png", "src/Images/3.png", "src/Images/4.png", "src/Images/5.png", "src/Images/6.png", "src/Images/7.png", "src/Images/8.png", "src/Images/9.png", "src/Images /10.png",
"src/Images/11.png", "src/Images/12.png", "src/Images/13.png", "src/Images /14.png", "src/Images/15.png",
"src/Images/16.png", "src/Images/17.png", "src/Images/18.png", "src/Images /19.png", "src/Images/20.png",
"src/Images/21.png", "src/Images/22.png", "src/Images/23.png", "src/Images /24.png", "src/Images/25.png",
"src/Images/26.png"
};
//Define the constructor here
public AbcGeniusPanel() {
ImageIcon[] alphabets = new ImageIcon[26];
setBackground(Color.yellow);
//Load the images for alphabet images into the alphabets array using a for loop
for (int i = 0; i < alphabets.length; i++) {
alphabets[i] = new ImageIcon("C:\\Users\\Dip\\Desktop\\Java Projects\\AbcGeniusApp\\src\\Alphabets\\" + (i + 1) + ".png");
}
//Create a JPnael object
JPanel panel = new JPanel();
//Set a layoutManager on the panel
//panel.setLayout(new FlowLayout(FlowLayout.CENTER)); //This is not workling good
panel.setLayout(new GridLayout(2, 13, 5, 5)); //This is good for now
//Create an array for holdoing the buttons
buttons = new JButton[26];
//This Loop will Store the buttons in the buttons array attatching each image for each button
//Try passing Images inside the JButton parameter later.
for (int i = 0; i < 26; i++) {
buttons[i] = new JButton(alphabets[i]);
}
// Now Setting up a new Borderlayout so that we can set the whole gridLayout at the botton of the panel
setLayout(new BorderLayout(2, 0));
//add the panel to the Border layout
add(panel, BorderLayout.SOUTH);
//Add evenHandling mechanism to all the buttons
for (int k = 0; k < 26; k++) {
buttons[k].addActionListener(this);
}
for (int count1 = 0; count1 < 26; count1++) {
panel.add(buttons[count1]);
}
}
//This Method will generate a random Number and return it
public int random_number() {
int rand_num;
Random generator = new Random(System.currentTimeMillis());
rand_num = generator.nextInt(26);
return rand_num;
}
//This method will draw the font on the Panel
public void paintComponent(Graphics g) {
Font font; //Declare Font object here
font = new Font("Wide Latin", Font.BOLD, 22); //Set font
super.paintComponent(g); //Ensure the drawing in super class
g.setFont(font); //Set the font
g.setColor(Color.RED);
String text = "CLICK ON THE RIGHT IMAGE!"; //Display the text
g.drawString(text, 255, 20);
}
//To draw the picture on the screen we need to override the paint Method
#Override
public void paint(Graphics g) {
super.paint(g);
//Here, x and y will determine the x and y position os each image
int x = 0, y = 0;
// the varibale q is declared above
for (q = 0; q < 3; q++) //This loop will generate three images on the screen
{
if (showImage) {
x = x + 265; //X-Position of the image
y = 90; //Y-Position of the image
//q is declared as q=0, so this will always be true
if (w == 1 || q == 0) {
g.drawImage(image, x, y, image.getWidth(null), image.getHeight(null), null); //This method will put the image on the screen
showImage = true;
w = 0;
}
while (true) //this loop will run anyway
{
//go inside this loop only when the generated random
//doesn't match with the index of the button that was pressed
while ((randNumber = random_number()) != index) {
index = randNumber; //Now put the randomVlaue in the index
this.image = new ImageIcon(imageList[randNumber]).getImage();
showImage = true;
//make w=1 so that we can break from the outer loop
w = 1;
//break from the inner loop
break;
}
//Since we have made the w=1, so we are breaking out of the outer loop
if (w == 1) {
break;
}
}
}
}
}
#Override
public void actionPerformed(ActionEvent event) {
Object source = event.getSource();
id = 0;
while (true) {
//id is set to zero, for example if the button A (buttons[0])is not pressed then it will go below
//to increase id until it matches the index of the button that we pressed
if (source == buttons[id]) {
//get the image of that same index of the buttons and then set the showImage true
//SO the the paint function above can draw the image
this.image = new ImageIcon(imageList[id]).getImage();
showImage = true;
//save the index of the button that is presed in another variable
//then break from the while loop
index = id;
break;
} else {
id++;
//This is necessary to make sure that id will cross 26
//becasue we have only 26 letters or the array index is 26
//so highest value can be 26 only
id = id % 26;
}
}
repaint();
}
}
Add 3 JLabels or JButtons (whatever will be displaying the images) into a JPanel container. The JPanel will likely use a GridLayout(1, 3, horizontal_gap, 0) layout.
Place all images as ImageIcons into an ArrayList.
Shuffle the ArrayList when needed
After shuffling place the Icons into the JLabels/JButtons in a for loop using the setIcon(...) method.
Note that
your JPanel should override paintComponent, not paint. The paint method is responsible for painting a component's children and borders, and it does not use double buffering by default, making a more dangerous method to override.
Putting in a while (true) loop into your Swing GUI without regard to threading is extremely dangerous.
Putting this into a painting method such as paint is GUI suicide. Never do this since a painting method is a major determinant in the perceived responsiveness of your program. If you slow it down, the program will be perceived as being slow and poorly responsive, and thus it must be lean and fast as possible, and you should have painting and only painting code within it.
Your paint method has program logic in it, something that also shouldn't be done. You don't have full control over whether or even if a painting method will be called, and so program logic should never be placed inside one of these.
As MadProgrammer well notes, don't use the src path for your images as this won't exist once you build your program into a jar file. Better to Create a resource directory in the jar file, and to refer to your images as resources, not as files.

ActionListener: Disabling Buttons

I am currently working on a Java class that produces a simple JFrame/JButton layout of Tic-Tac-Toe. Implementing ActionListener, I intended on having the selected JButton set its title to "X" or "O" (based on a boolean statement of whether or not it is X's turn to pick a JButton) and become disabled (so it cannot be played on top of in following turns). The current application I have created does this, but it will sometimes not change the JButton text or disable the button until I click another button. There does not seem to be any kind of cohesive order at which this happens when I click one of the JButtons. I have spent hours trying to fix this issue with no avail. Is there an issue with how I coded my actionPerformed method or how I added it to my JButtons?
Here is the code to my class:
import javax.swing.*;
import java.awt.event.*;
import javax.swing.*;
public class TTT extends JFrame implements ActionListener{
// private fields
private JButton[] buttonArray;
private JLabel prompt;
private boolean turnX;
private String letter;
public TTT() {
// Instantiates JFrame window and adds lines to board
super.setSize(235, 280);
super.setTitle("Tic-Tac-Toe");
// Instantiates JButton array
buttonArray = new JButton[9];
// Loop that creates the JButton squares
for(int y = 30; y <= 140; y += 55) {
for(int x = 30; x <= 140; x += 55) {
for(int index = 0; index < buttonArray.length; index++) {
buttonArray[index] = new JButton();
buttonArray[index].setSize(50, 50);
buttonArray[index].setLocation(x, y);
buttonArray[index].addActionListener(this);
super.add(buttonArray[index]);
}
}
}
prompt = new javax.swing.JLabel("X's TURN");
prompt.setVerticalAlignment(JLabel.BOTTOM);
super.add(prompt);
turnX = true;
super.setVisible(true);
}
public void actionPerformed(java.awt.event.ActionEvent a) {
// Calculate whose turn it is
if(turnX){
letter = "X";
prompt.setText("O's TURN");
turnX = false;
} else if(!turnX){
letter = "O";
prompt.setText("X's TURN");
turnX = true;
}
JButton pressedButton = (JButton)a.getSource();
pressedButton.setText(letter);
pressedButton.setEnabled(false);
super.repaint();
}
public static void main(String[] args) {
new TTT();
}
}
With this code:
for(int y = 30; y <= 140; y += 55) {
for(int x = 30; x <= 140; x += 55) {
for(int index = 0; index < buttonArray.length; index++) {
You are adding 82(!) JButtons (one on top of the other in groups of 9). So, what was actually happening was that one was changed (disabled etc), but on calling repaint, the order of painting them might change, so the one whose ActionListener was triggered was painted underneath one of the 8 that were still enabled and blank.
You can correct it like this:
...
int index = 0; // <-- Add this line
for(int y = 30; y <= 140; y += 55) {
for(int x = 30; x <= 140; x += 55) {
// <-- remove the third for-loop
buttonArray[index] = new JButton();
buttonArray[index].setSize(50, 50);
buttonArray[index].setLocation(x, y);
buttonArray[index].addActionListener(this);
super.add(buttonArray[index]);
index++; // <-- increment 'index'
}
}
...
You could also remove the super.repaint() line, since it is redundant.
Not directly related to your problem, but Swing related stuff should (for various reasons) be called from the Event Dispatching Thread (EDT). One way to achieve this, is by using SwingUtilities.invokeLater(), so it might be a good idea to replace new TTT(); with this:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new TTT();
}
});

Categories

Resources