I'm trying to make the image icon of ballpos move between two JButtons in my JButton array. These are buttons 99 and 108 on every press of a separate JButton jBAct, so that on every press of jBAct the image will move one down to 99 then once reached 99 will go up to 108 then go back down to 99 again and so on.
Here is what I've tried, i know it's wrong but i think it's along these lines.
if (event.getSource() == jBAct)
{
if (ballpos > 99)
{
jBGame[ballpos-1].setIcon(new ImageIcon("src/ball.png"));
jBGame[ballpos].setIcon(new ImageIcon());
ballpos--;
} else {
if (ballpos < 108){
jBGame[ballpos+1].setIcon(new ImageIcon("src/ball.png"));
jBGame[ballpos].setIcon(new ImageIcon());
ballpos++;
}
}
}
This code segment is in class public class CBabyBallBounce extends JFrame implements ActionListener
and part of method public void actionPerformed(ActionEvent event)
The direction in which the ball moves is not kept anywhere in your code, so when a ball reaches a position between 99 and 108 you can't know in which direction to move it. I suggest keeping this information in the ActionListener responsible for this behavior.
In the following example I have an array of 4 buttons between which the ball moves. I represent the ball with a String, but the concept is the same for an image.
public class Buttons {
JButton[] buttons = new JButton[4];
JButton move = new JButton("Move");
private final String text = "O";
Buttons() {
JPanel panel = new JPanel();
for (int i = 0; i < buttons.length; i++)
panel.add(buttons[i] = new JButton());
buttons[2].setText(text);
move.addActionListener(new ActionListener() {
private int ballpos = 2;
private int dir = 1;
#Override
public void actionPerformed(ActionEvent e) {
buttons[ballpos].setText(null);
ballpos += dir;
buttons[ballpos].setText(text);
if (ballpos == buttons.length - 1)
dir = -1;
else if (ballpos == 0)
dir = 1;
}
});
JFrame frame = new JFrame();
frame.getContentPane().add(panel);
frame.add(move, BorderLayout.PAGE_START);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new Buttons());
}
}
I would highly recommend to add an ActionListener per button or group of buttons that share the same behavior as I did above. It allows you to encapsulate the behavior of the button. Do not have the frame implement a "global" ActionListener and then find the source of the event by if-else statements. This leads to a mess and more bugs.
The code inside actionPerformed can be slightly cleverer, but I went for readable for this example. If you understand it you can change it in a number of different ways.
As a final note, don't create a new image icon each time. Crate the image once and store it as I did for my string (which, for a string, is redundant, since Strings are special). You can also use null instead of an empty image, again, as I did for the string case.
Related
I have four images that I am cycling between to create a simple animation. Each image is a frame of a repeating animation and I have a timer change the image to the next frame to make it animated. Every time I change the image there is a flicker where the window is all white in between displaying the next image. How do I remove this flicker?
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Reveal extends JFrame
{
private JPanel panel = new JPanel(); //a panel to house the label
private JLabel label = new JLabel(); //a label to house the image
private String[] image = {"Jack in the Box 1.png","Jack in the Box 2.png","Jack in the Box 3.png","Jack in the Box 4.png","Jack in the Box 5.png","Jack in the Box 6.png","Jack in the Box 7.png"}; //an array to hold the frames of the animation
private ImageIcon[] icon = new ImageIcon[7]; //an array of icons to be the images
private Timer timer;
private Timer timer2;
int x = 0;
int y = 4;
int counter = 0;
/**
* Constructor for objects of class Reveal
*/
public Reveal()
{
for (int h = 0; h < 7; h++){
icon[h] = new ImageIcon(image[h]);
icon[h].getImage().flush();
label.setIcon(icon[h]);
}
//Display a title.
setTitle("Raffel");
//Specify an action for the close button.
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Sets the size of the window
setSize(800,850);
panel = new JPanel();
label = new JLabel();
panel.add(label);
add(panel);
setVisible(true);
}
public void display(String name, int number){
timer = new Timer(150, new ActionListener(){
public void actionPerformed(ActionEvent e) {
if (counter > 48){
timer.stop();
timer2.start(); //starts the second half of the animation
}else{
label.setIcon( icon[x] );
if (x != 3){
x++;
}else{
x = 0;
}
counter++;
} //ends if-else
} //ends action method
}); //ends timer
timer2 = new Timer(150, new ActionListener(){
public void actionPerformed(ActionEvent e) {
if (y > 6) {
timer2.stop();
}else{
label.setIcon( icon[y] );
y++;
} //ends if-else
} //ends action method
}); //ends timer2
timer.start();
}
}
icon[x] = new ImageIcon(image[x]);
icon[x].getImage().flush();
label.setIcon( icon[x] );
I would suggest that you don't keep reading the images from disk.
Load all the ImageIcons into an array at the start of your class and then just cycle through the array to get the next Icon to update the label.
Edit:
The animation has only 4 unique frames.
But you have an array of 7 icons.
icon[h].getImage().flush();
There is no need for the flush. You are just creating ImageIcons.
label.setIcon(icon[h]);
Why do you keep setting the icon of the label each time you create a new Icon? This means the last Icon created will be the first Icon displayed.
I would expect you should assign icon[0] to the label AFTER the loop is finished. I would guess this is the cause of the flicker because the last icon is briefly displayed before the first. So if you default to the first you won't have a problem.
The animation has only 4 unique frames. The animations is 64 frames long playing each image as a frame 16 times
You don't need 2 Timers for this. You need two variables.
one that keeps incrementing until 64 and then stops the animation
one that keeps incrementing to 3 and then resets to 0
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.
I am creating a Swing Game GUI, which contains multiple panels that are part of a frame. Each panel contains a number of ImageIcon JButtons. When a JButton is clicked it will randomly position itself on the screen. For this particular panel i am not using a certain LayoutManager(understand the criticism here, but also the frame is not resizable as it fits the entire screen) and I use the randomness of setBounds(x, y, 100, 100) for this image JButton to appear on a random spot inside that panel. My only trouble is to figure it out, how to save the previous position of a certain JButton, so if the user repeatedly clicks it, it will duplicate and will appear on the screen multiple times on a random position, leaving all others displayed.
class ButtonListener implements ActionListener
{
Random randomCoord = new Random();
//random location in on the screen 30-150 x 200-400
int x = randomCoord.nextInt(150-30) + 30;
int y = randomCoord.nextInt(400-200) + 200;
int butnCOUNT = 0;
public void actionPerformed (ActionEvent event)
{
if (event.getSource() == button1)
{
butnCOUNT ++;
JButton[] addButtons = new JButton[butnCount];
for (int i = 0; i < addButtons.length; i++)
{
addButtons[i] = butnCloud; // ImageIcon JButton
addButtons[i].addActionListener(new ButtonListener ());
panel5.add(addButtons[i]);
panel5.setVisible(true);
panel5.setOpaque(false);
panel5.setBounds(x, y, 20, 20);
}
}
}
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.
I am currently designing an image editor in a GUI which has several buttons which, when clicked, should apply different effects on the loaded image. I have the effects working with an ActionListener but if I try to click an effect after already clicking another effect, the image turns black instead of applying the desired effect. How do I apply multiple effects to the same image? Here is my code for the ButtonPanel class, which includes the ActionListener and code for the effects(only three of the effects have been implemented as of yet):
class ButtonPanel extends JPanel
{
//JButton makeBlue;
BufferedImage img;
ImagePanel ip = new ImagePanel();
ButtonPanel(ImagePanel x)
{
final JButton makeBlue = new JButton ("Make Blue");
final JButton makeRed = new JButton ("Make Red");
final JButton makeGreen = new JButton ("Make Green");
final JButton invertColors = new JButton ("Invert Colors");
final JButton makeGreyscale = new JButton ("Greyscale");
final JButton makeWarhol = new JButton ("Warhol");
final JButton flipVertical = new JButton ("Flip Vertical");
final JButton flipHorizontal = new JButton ("Flip Horizontal");
final JButton rotateClockwise = new JButton ("Rotate Clockwise");
setBackground(Color.RED);
ip = x;
//int width, height;
//setBackground(Color.RED);
//int height = 0;
add(makeBlue);
add(makeRed);
add(makeGreen);
add(invertColors);
add(makeGreyscale);
add(makeWarhol);
add(flipVertical);
add(flipHorizontal);
add(rotateClockwise);
ActionListener action =
new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
BufferedImage img = ip.getImg();
if (e.getSource()== makeBlue)
{
int width = img.getWidth(); //# of pixel columns
int height = img.getHeight(); //# of pixels rows
for(int i=0; i<width;i++)
{
for(int j=0; j<height; j++)
{
int value = img.getRGB(i,j);
int y = 0xFF0000FF;
value = value & y;
img.setRGB(i,j,value);
}
}
ip.setImage(img);
setBackground(Color.GREEN);
repaint();
}
else if(e.getSource()== makeRed)
{
int width = img.getWidth(); //# of pixel columns
int height = img.getHeight(); //# of pixels rows
for(int i=0; i<width;i++)
{
for(int j=0; j<height; j++)
{
//fill in code here
int value = img.getRGB(i,j);
int y = 0xFFFF0000;
value = value & y;
//System.out.println(value);
img.setRGB(i,j,value);
}
}
ip.setImage(img);
repaint();
}
else if(e.getSource()== makeGreen)
{
int width = img.getWidth(); //# of pixel columns
int height = img.getHeight(); //# of pixels rows
for(int i=0; i<width;i++)
{
for(int j=0; j<height; j++)
{
//fill in code here
int value = img.getRGB(i,j);
int y = 0xFF00FF00;
value = value & y;
img.setRGB(i,j,value);
}
}
ip.setImage(img);
repaint();
}
}
};
makeBlue.addActionListener(action);
makeRed.addActionListener(action);
makeGreen.addActionListener(action);
makeWarhol.addActionListener(action);
flipVertical.addActionListener(action);
flipHorizontal.addActionListener(action);
rotateClockwise.addActionListener(action);
makeGreyscale.addActionListener(action);
invertColors.addActionListener(action);
}
}
Any help would be appreciated. Thanks.
Your problem is that you're extracting all of one color out of the image, and then replacing the original image in your ImagePanel with the new extracted image that is, say if the green button is pressed, all green. Then when you try to extract red from the ImagePanel's image, you get all black because the image is now only green.
You need to save the original image somewhere if you want to extract the original colors. Perhaps you should give ImagePanel a method, getOriginalImage() or some such, and have it store two images, one the displayed image and one the changed one.
Better still, use an Model-View-Control pattern and store your images elsewhere off the GUI.
There are two ways to accomplish this. You can either have your class be the ActionListener (which is the way you have it set up now), or you can have a unique ActionListener for each button. As a design decision I prefer the latter, as you can have more precise control and easier to read code this way.
Each of your buttons, menu items, and any other Object you want to have a unique effect should each have it's own ActionListener. You can either write a new class file for each ActionListener, an inner class, or an anonymous inner class for each. I prefer anonymous inner classes placed in line of your constructor or setup method like this:
JButton testButton = new JButton("Test");
testButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
// do stuff here
System.out.println("You clicked me!");
}
});
This method allows you to ignore huge conditional branches and unnecessary comparisons. Implement the interface uniquely for each component that needs the listener.