Hello guys I am a rookie programmer so please excuse me if I am trying something stupid.
I started learning about GUI applications today, and I wanted to do a practice to check if I learned it properly. When I run the program, there is a dot on the screen, and I want it to move right when I click on the start button. I accomplished this, however I wanted it to be an animation, I wanted the dot to look as if it was moving slowly. But When I click to button, it just teleports.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Guila extends JFrame {
private JPanel panel;
private JButton myButton;
private final int WINDOW_WIDTH = 300;
private final int WINDOW_HEIGHT = 600;
private JPanel[] array = new JPanel[900];
private int i = 0;
int j = 0;
int m = 0;
int k = 0;
public Guila() {
setTitle("Simple Animation");
setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel = new JPanel();
panel.setLayout(new GridLayout(30, 30));
while(i < 900) {
array[i] = new JPanel();
if(i == 460) {
array[i].setBackground(Color.BLACK);
}
else {
array[i].setBackground(Color.WHITE);
}
panel.add(array[i]);
i++;
}
JPanel panel2 = new JPanel();
myButton = new JButton("Start");
myButton.addActionListener(new myActionListener());
setLayout(new GridLayout(2,1));
add(panel);
panel2.add(myButton);
add(panel2);
setVisible(true);
}
private class myActionListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
array[460+j].setBackground(Color.WHITE);
array[461+j].setBackground(Color.BLACK);
repeat();
}
}
public void repeat() {
if (j<11) {
try {
Thread.sleep(10);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
j++;
myButton.doClick();
}
}
public static void main(String[] args) {
new Guila();
}
}
Don't use Thread.sleep() on code that executes on the Event Dispatch Thread.
Use a Swing Timer for animation. Every time the Timer fires you update a property of the component you want to change and then you invoke repaint() on the component.
I agree with using the Swing Timer for animation. Although it might be a challenge to create your desired slow motion effect.
Use the following code to help you get started, if needed:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Bullet extends JPanel implements ActionListener
{
private static final long serialVersionUID = 1L;
private static final int SCREEN_WIDTH = 500;
private static final int increments = 5;
int[] xPoints = new int[5];
public void paintComponent(Graphics g)
{
g.setColor(Color.WHITE);
g.fillRect(0,0,800,800);
g.setColor(Color.BLACK);
for (int i = 0; i < xPoints.length; i++)
{
g.fillRect(xPoints[i]+150, 210, 20, 20);
}
Timer timer = new Timer(100, new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
for (int i = 0; i <= 1; i++)
{
if (xPoints[i] + increments < SCREEN_WIDTH)
{
xPoints[i] += increments;
} else {
xPoints[i] = 0;
}
}
repaint();
}
});
timer.start();
}
public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.setSize(500,500);
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(new Bullet());
frame.setVisible(true);
}
}
Related
I am learning Swing from past week and I want desing some game like puzzle, 2048 for fun i have written puzzle sucessfully but when writting 2048 i encountered some problem, see below for sample code that is not orginal but it states the problem.
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
#SuppressWarnings("serial")
class Panel extends JPanel implements ActionListener
{
JButton[] button = new JButton[4];
boolean flag = true;
public Panel()
{
this.setLayout(new GridLayout(1,4));
for(int i = 0; i < 4; ++i)
{
button[i] = new JButton("");
button[i].setBackground(Color.WHITE);
button[i].addActionListener(this);
button[i].setPreferredSize(new Dimension(100,100));
add(button[i]);
}
button[0].setText("2");
button[0].setBackground(Color.GREEN);
}
#Override
public void actionPerformed(ActionEvent Ae)
{
if(flag)
{
flag = false;
button[0].setText("");
button[0].setBackground(Color.WHITE);
//for(long i = 0; i < 100000000L; ++i);
button[1].setText("2");
button[1].setBackground(Color.GREEN);
//for(long i = 0; i < 100000000L; ++i);
button[1].setText("");
button[1].setBackground(Color.WHITE);
//for(long i = 0; i < 100000000L; ++i);
button[2].setText("2");
button[2].setBackground(Color.GREEN);
//for(long i = 0; i < 100000000L; ++i);
button[2].setText("");
button[2].setBackground(Color.WHITE);
//for(long i = 0; i < 100000000L; ++i);
button[3].setText("2");
button[3].setBackground(Color.GREEN);
}
else
{
flag = true;
button[0].setText("2");
button[0].setBackground(Color.GREEN);
button[1].setText("");
button[1].setBackground(Color.WHITE);
button[2].setText("");
button[2].setBackground(Color.WHITE);
button[3].setText("");
button[3].setBackground(Color.WHITE);
}
}
}
public class Grid
{
public Grid()
{
// I cannot ascess JFrame reference in real code too but it is not
// must, it would be better that i can do the thing without
// JFrame reference.
JFrame jFrame = new JFrame("Grid");
jFrame.add(new Panel());
jFrame.pack();
jFrame.setResizable(false);
jFrame.setVisible(true);
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(() -> new Grid());
}
}
In above code the change of button text and color is not smooth that is first button suddenly becomes white and last button suddnley becomes Green, so i have tested that second and third button is actullay changing or not by the delay loop but still second and third button is not changing but it waits some time to change first button and last button. I Hope that the problem is understandable.
Note: In real i use keybord by keybinding not button click i used it here for state the problem.
How To switch text and color of button with smooth look like 2048 game ?
Thanks.
Hopefully I understood your problem. What you want is animation. To do this you need to use a timer.
The following code demonstrates. Note that I changed the class name to MyPanel so as not to clash with class java.awt.Panel.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class MyPanel extends JPanel implements ActionListener {
JButton[] button = new JButton[4];
boolean flag = true;
Timer timer;
int currentIndex;
int delta;
public MyPanel() {
this.setLayout(new GridLayout(1, 4));
for (int i = 0; i < 4; ++i) {
button[i] = new JButton("");
button[i].setBackground(Color.WHITE);
button[i].addActionListener(this);
button[i].setPreferredSize(new Dimension(100, 100));
add(button[i]);
}
button[0].setText("2");
button[0].setBackground(Color.GREEN);
timer = new Timer(500, event -> {
button[currentIndex].setText("");
button[currentIndex].setBackground(Color.WHITE);
currentIndex += delta;
button[currentIndex].setText("2");
button[currentIndex].setBackground(Color.GREEN);
if (currentIndex == 0 || currentIndex == 3) {
timer.stop();
}
});
timer.setInitialDelay(0);
}
#Override
public void actionPerformed(ActionEvent e) {
if (flag) {
flag = false;
delta = 1;
}
else {
flag = true;
delta = -1;
}
timer.start();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame jFrame = new JFrame("Grid");
jFrame.add(new MyPanel());
jFrame.pack();
jFrame.setResizable(false);
jFrame.setVisible(true);
});
}
}
I have 2 classes: GameOfLife() and PanelGrid. When a new object of panelgrid is created, the (overwritten) method paintComponent is not called. Putting "repaint()" in the constructor doesn't work either.
import java.util.Scanner;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
class GameOfLife {
JFrame frame = new JFrame("Game of life");
PanelGrid panelGrid;
void buildIt() {
frame.setSize(600, 600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.add(buttonStart, BorderLayout.SOUTH);
frame.add(buttonStop, BorderLayout.NORTH);
panelGrid = new PanelGrid();
panelGrid.setOpaque(true);
frame.add(panelGrid);
}
public static void main(String[] args) {
new GameOfLife().buildIt();
}
}
class PanelGrid extends JPanel implements ActionListener {
Timer timer;
int delay;
JLabel label;
int height; // get length from the file
int width; //get width of array from the file
//constructor
public PanelGrid() {
delay = 1000;
timer = new Timer(delay, this);
width = 4;
height = 5;
//if there exists a file with an initial configuration, initial[][], width and height are updated.
//if not, the default array is used
readInitial();
//repaint(); putting repaint() here din't make a difference.
}
#Override
public void paintComponent(Graphics g) {
System.out.println("if you read this, the method is called");
super.paintComponent(g); //erases panel content
this.setLayout(new GridLayout(width, height));
for (int r = 0; r < height; r++) {
for (int c = 0; c < width; c++) {
JPanel panel = new JPanel();
if (grid[r][c].isAlive() == true) {
panel.setBackground(Color.BLACK);
} else {
panel.setBackground(Color.WHITE);
}
this.add(panel);
}
}
//the rest of this class I have left out for clarity
}
}
I think you need to add your PanelGrid to theJFrame. If it's not in a visible top-level container paint() and therefore paintComponent() won't be called. Maybe. Worth a shot...
I am new to java and I am making a Whack a Mole game using a JFrame with JButtons. Currently, I have a 5x5 grid of buttons and that is as far as I got. I am having 3 of the buttons be X (to represent the mole) and 22 be O (to represent an empty hole). I would like for the buttons values to shuffle so that every 2 seconds the values are randomized. How might I go about doing this? Sorry for being such a novice, I literally started java a couple weeks back and JFrames still confuse me lol. Here is the code I currently have, thanks;
import javax.swing.*;
import java.awt.*;
public class Whack_A_Mole extends JFrame {
JButton[][] square = new JButton[5][5];
JButton button1, button2;
static JLabel label = new JLabel();
Whack_A_Mole() {
super("Whack a Mole");
JPanel p = new JPanel(new GridLayout(5,5));
for(int i = 0; i < 5; i++) {
for(int j = 0; j < 5; j++) {
square[i][j] = new JButton();
p.add(square[i][j]);
}
}
add(p, BorderLayout.CENTER);
p = new JPanel(new GridLayout(1,2));
setSize(600, 600);
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setVisible(true);
setLocationRelativeTo(null);
}
public static void main(String[] args) {
new Whack_A_Mole();
}
}
If you put the objects in an ArrayList, you can use the shuffle() method to shuffle their order. As to timing, either use Thread.sleep(millisecondsAmt) or a Timer. I prefer the util.Timer, especially when the action repeats indefinitely or for many repetitions.
Create a thread that will add update function into queue every 2 seconds.
import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowEvent;
import java.util.Random;
public class Wack_A_Mole extends JFrame {
JButton[][] square = new JButton[5][5];
JButton button1, button2;
static JLabel label = new JLabel();
private Thread updateWoker;
Whack_A_Mole() {
super("Whack a Mole");
JPanel p = new JPanel(new GridLayout(5,5));
for(int i = 0; i < 5; i++) {
for(int j = 0; j < 5; j++) {
square[i][j] = new JButton();
p.add(square[i][j]);
}
}
add(p, BorderLayout.CENTER);
p = new JPanel(new GridLayout(1,2));
setSize(600, 600);
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setVisible(true);
setLocationRelativeTo(null);
}
void start(){
updateWoker=new Thread(new Runnable(){
public void run(){
Runnable r=new Runnable(){
public void run() {
buttonUpdate(); // call buttonUpdate every two seconds
}
};
while (true){
javax.swing.SwingUtilities.invokeLater(r);
try{Thread.sleep(2000);} catch (InterruptedException ex) {
return;
}
}
}
}
);
updateWoker.start();
}
public void buttonUpdate(){ // random update can be done in this function
Random r=new Random();
for(int i=0;i<square.length;i++){
for(int j=0;j<square[i].length;j++){
if(r.nextInt() %2==0)
square[i][j].setText("O");
else
square[i][j].setText("X");
}
}
}
public void processWindowEvent(WindowEvent e) {
if (e.getID() == WindowEvent.WINDOW_CLOSING) { // making sure to stop the thread after gui closes
if(updateWoker.isAlive()){
updateWoker.interrupt();
}
dispose();
}
}
public static void main(String[] args) throws InterruptedException {
final Whack_A_Mole theTest=new Whack_A_Mole();
theTest.start();
}
}
I have the following problem, when i want to add 2JPanels to my JFrame only one is visible, depending on which I added to the frame last. I overrided the JPanels default paintComponent() method on both of the JPanels. How can i fix this?
Code snippet:
Border:
public class BorderDrawer extends JPanel{
private int _width,_height;
BorderDrawer(int width,int height)
{
setOpaque(false);
_width = width;
_height = height;
}
#Override
protected void paintComponent(Graphics g) {
final int BUTTON_WIDTH = 20,BUTTON_HEIGHT = 20;
int MINES_HORIZONTALLY = _width;
int MINES_VERTICALLY = _height;
super.paintComponent(g);
try{
BufferedImage topLeftCorner = ImageIO.read(this.getClass().getResource("topLeftCorner.png"));
g.drawImage(topLeftCorner, 0, 0, null);
....// drawing other border components
}
}
Clock:
public class GraphicTimer extends JPanel{
Timer _aktTimer = null;
int seconds;
int _width = 0;
GraphicTimer(int width)
{
setSize(52, 31);
setOpaque(false);
_width = width;
int delay = 1000; //milliseconds
_aktTimer = new Timer(delay, taskPerformer);
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
try
{
final int BUTTON_WIDTH = 20,BUTTON_HEIGHT = 20;
int MINES_HORIZONTALLY = _width;
int HORIZONTAL_ENDING = 15+BUTTON_WIDTH*MINES_HORIZONTALLY;
BufferedImage clock = ImageIO.read(this.getClass().getResource("clock.png"));
g.drawImage(clock,HORIZONTAL_ENDING-54,22, null);
}
catch(IOException ex)
{
ex.printStackTrace();
}
}
....
}
JFrame:
public class DrawerField extends JFrame implements Serializable{
//...
public DrawerField()
{
super("MineSweeper");
_FIELD = new Field();
constructorInit();
}
public void constructorInit()
{
_buttons = new FieldButton[_height][_width];
_isMouseEventEnabled = true;
fieldPanel = new JPanel();
smilePanel = new JPanel();
//INITS
int fieldSizeWidth = (_width)*20;
int fieldSizeHeight = (_height)*20; // Magic size
fieldPanel.setSize(fieldSizeWidth,fieldSizeHeight); // 20x20
fieldPanel.setLocation(15, 70);
fieldPanel.setLayout(new GridLayout(_width,_height));
int fullWindowWidth = fieldSizeWidth+36;
int fullWindowHeight = fieldSizeHeight+142;
setSize(fullWindowWidth,fullWindowHeight);
setResizable(false);
setDefaultCloseOperation(EXIT_ON_CLOSE);
restartButton = new RestartButton(this);
smilePanel.setSize(34,34);
smilePanel.add(restartButton);
smilePanel.setLayout(new GridLayout(1,1));
smilePanel.setLocation((int)fullWindowWidth/2-(34/2)-1,20);
///INITIALS
for(int i = 0; i < _height; i++)
{
for(int j = 0; j < _width ; j++)
{
_buttons[i][j] = new FieldButton(_hidden[i][j],true,i,j,this);
fieldPanel.add(_buttons[i][j]);
}
}
add(smilePanel);
add(fieldPanel);
borderDrawer = new BorderDrawer(_width,_height);
_graphicTimer = new GraphicTimer(_width);
_graphicTimer.start();
add(_graphicTimer); // This is the two lines which change the result
add(borderDrawer);
MenuBar menuBar = new MenuBar(this);
setJMenuBar(menuBar);
setVisible(true);
}
//...
}
Easier and compilable example:
public class Main {
public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.setSize(500, 500);
MyPanel panel1 = new MyPanel(30,30);
frame.add(panel1);
MyPanel panel2 = new MyPanel(70,30);
frame.add(panel2);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.repaint();
frame.setVisible(true);
}
public static class MyPanel extends JPanel
{
int _x,_y;
MyPanel(int x, int y)
{
_x = x; _y = y;
}
#Override
public void paint(Graphics g) {
g.drawOval(_x,_y,20,20);
}
}
}
My main goal is to add 2 circles to the JFrame without using any Layout.( As you can see in the example above I already have a lot of things on my JFrame that's why I don'T want to use layouts). The problem is the same in this example, when i add the 2nd circle the first is disappearing.
Its simple. Adding a panel in a frame adds it to the content pane of the frame. This contentpane has a default layout of BorderLayout which means, every time you add a panel, it gets added to the center of the content pane and gets replaces the previous one. This is the reason why you see only the last one. Its always good to use a Jpanel set to the layout of your choice put everything that needs to be shown on the screen in that panel. If you don't want to do that, you may also call getContentPane() from the frame and play with the returned instance of JPanel.
Here is the example code for you:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Frame extends JFrame {
public Frame() {
setTitle("Two panels");
setSize(new Dimension(500,500));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Here goes your code
JPanel p= (JPanel) getContentPane();
p.setLayout(new GridLayout(1,2)); //set your own layout
p.add(new MyPanel(Color.BLUE)); //add panel with blue border
p.add(new MyPanel(Color.GREEN));//add panel with green border
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
Frame f= new Frame();
f.setVisible(true);
}
});
}
}
class MyPanel extends JPanel {
public MyPanel(Color color) {
setBorder(BorderFactory.createLineBorder(color));
}
}
Run it and see... You should be able to see something like this:
I'm trying to refresh a panel of Icons with a "Refresh" button, but can't figure out what to put in the event handler to perform this task. I just have the code to perform the task once (below). Can someone help me with this?
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class FourRandomCards extends JFrame {
JButton jbtRefresh = new JButton("Refresh");
CardsPanel cardsPanel = new CardsPanel();
public FourRandomCards() {
add(cardsPanel, BorderLayout.CENTER);
add(jbtRefresh, BorderLayout.SOUTH);
//=============================================================
/** can't figure out what to put in event-handler **/
jbtRefresh.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
}
});
//=============================================================
}
public static void main(String[] args) {
JFrame frame = new FourRandomCards();
frame.setTitle("Four Random Cards");
frame.pack();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
private class CardsPanel extends JPanel {
public CardsPanel() {
for (int i = 0; i < 4; i++) {
int card = (int)(Math.random() * 54 + 1);
ImageIcon cardIcon = new ImageIcon
("image/card/" + card + ".png");
JLabel jlblCard = new JLabel(cardIcon);
add(jlblCard);
}
}
}
}
Basically, you need to remove all the existing components and re-add them. This would suggest you would require some method capable of fulfilling this requirement.
Because you're using local variables, you won't be able access the cardsPanel variable unless you make it final or use class instance variables...
public class FourRandomCards extends JFrame {
JButton jbtRefresh = new JButton("Refresh");
final CardsPanel cardsPanel = new CardsPanel();
public FourRandomCards() {
add(cardsPanel, BorderLayout.CENTER);
add(jbtRefresh, BorderLayout.SOUTH);
//=============================================================
/** can't figure out what to put in event-handler **/
jbtRefresh.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
cardsPanel.refresh();
}
});
//=============================================================
}
public static void main(String[] args) {
JFrame frame = new FourRandomCards();
frame.setTitle("Four Random Cards");
frame.pack();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
private class CardsPanel extends JPanel {
public CardsPanel() {
refresh();
}
public void refresh() {
removeAll();
for (int i = 0; i < 4; i++) {
int card = (int)(Math.random() * 54 + 1);
ImageIcon cardIcon = new ImageIcon
("image/card/" + card + ".png");
JLabel jlblCard = new JLabel(cardIcon);
add(jlblCard);
}
revalidate();
repaint();
}
}
}