I'm new to java and I'm trying to add an animation to a jFrame that I created using the Netbeans design editor. The idea is that there will be a background image and the animation will play on top of it. I'd also like to have other components that start out hidden and are displayed on top of the finished animation.
After some research on stackoverflow, I managed to create the animation I wanted. However, I can't figure out how to implement it into the Netbeans-generated jFrame I've created. As mainly a python user, all this method and class business is still a little confusing to me.
Here's my code for the animation:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class AnimationTest extends JPanel{
int height = 1;
AnimationTest() {
setPreferredSize(new Dimension(1000,618));
ActionListener al = (ActionEvent ae) -> {
repaint();
if(height < 190) {
height ++;
}
};
Timer timer = new Timer(10,al);
timer.start();
}
public void paintComponent(Graphics g) {
Color c = (Color.BLACK);
g.setColor(c);
g.fillRect(0, 309 - height, 1000, 2*height);
}
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame window = new JFrame("");
window.getContentPane().add(new AnimationTest());
createWindow(window);
}
});
}
public static void createWindow(JFrame window){
window.pack();
window.setLocationByPlatform(true);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
}
}
UPDATE: I tried replacing new JFrame with new AnimationFrame (the JFrame form I made using netbeans). Now the JFrame works but the JPanel isn't added (or isn't displayed). What is the right way to add the JPanel?
Related
I wrote this program in java using swing. When I run this on my Mac each time repaint() is called fifty new blue points are made and the old blue points are erased. I have been doing a lot of research to try and fix this issue and I have had not luck. Then today in my computer science class I find out that the program works on the windows computers that are in the classroom. My question is why is this the case and how can I fix this so that the program works on my Mac? Also on a side note I am relatively new to using swing in java so I was wondering if I am organizing everything correctly and if I can do anything different?
This is the class where I draw everything within the JPanel.
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Color;
import java.awt.Canvas;
import java.lang.Math;
import java.awt.event.*;
import javax.swing.*;
public class BarnsleyFern extends JPanel
{
private double newX,x=0;
private double newY,y=0;
public BarnsleyFern()
{
ActionListener action = new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
repaint();
}
};
Timer timer = new Timer(100,action);
//timer.start();
MouseListener mouse = new MouseListener()
{
public void mouseClicked(MouseEvent event)
{
//repaint();
}
public void mousePressed(MouseEvent event)
{
}
public void mouseReleased(MouseEvent event)
{
}
public void mouseEntered(MouseEvent event)
{
}
public void mouseExited(MouseEvent event)
{
}
};
MouseMotionListener mouseMotion = new MouseMotionListener()
{
public void mouseDragged(MouseEvent event)
{
repaint();
}
public void mouseMoved(MouseEvent event)
{
repaint();
}
};
addMouseListener(mouse);
addMouseMotionListener(mouseMotion);
}
public void paintComponent(Graphics window)
{
Graphics2D g2d = (Graphics2D)window;
g2d.translate(360,800);
fern(window);
}
public void fern(Graphics window)
{
Color newColor = new Color((int)(Math.random()*256),(int)(Math.random()*256),(int)(Math.random()*256));
for(int i=0;i<50;i++)
{
window.setColor(Color.BLUE);
int rand = (int)(Math.random()*100);
if(rand<1)
{
newX=0;
newY=0.16*y;
}
else if(rand<86)
{
newX=0.85*x + 0.04*y;
newY=0.85*y - 0.04*x + 1.6;
}
else if(rand<93)
{
newX=0.20*x - 0.26*y;
newY=0.23*x + 0.22*y + 1.6;
}
else
{
newX=0.28*y - 0.15*x;
newY=0.26*x + 0.24*y + 0.44;
}
window.fillOval((int)(newX*165.364),-(int)(newY*80.014),2,2);
x=newX;
y=newY;
}
}
}
This is the class that sets up the JFrame and adds the JPanel.
import javax.swing.*;
import java.awt.*;
public class BarnsleyFernRunner
{
public BarnsleyFernRunner()
{
JFrame frame = new JFrame();
frame.setTitle("Barnsley Fern");
frame.setSize(800,800);
frame.setLocation(300,0);
frame.setResizable(false);
frame.setBackground(Color.BLACK);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(null);
BarnsleyFern panel= new BarnsleyFern();
panel.setSize(800,800);
panel.setOpaque(true);
panel.setBackground(Color.BLACK);
frame.add(panel);
frame.setVisible(true);
}
public static void main(String[] args)
{
BarnsleyFernRunner runner = new BarnsleyFernRunner();
}
}
The "major" issue is, you're breaking the paint chain requirements...
public void paintComponent(Graphics window)
{
Graphics2D g2d = (Graphics2D)window;
g2d.translate(360,800);
fern(window);
}
The super implementation of paintComponent does something, something important, unless you're prepared to take over that responsibility, you should make sure you are calling super.paintComponent first
You issue isn't helped by...
frame.setSize(800,800);
frame.setLocation(300,0);
frame.setResizable(false);
frame.setLayout(null);
panel.setSize(800,800);
panel.setOpaque(true);
You should keep the default BorderLayout, it will make your life a lot simpler.
Simply by updating the BarnsleyFern to override the getPreferredSize you gain a much more flexible solution. This means you pack the window and the available content size will be the preferred size of the contents, as apposed to the size of the window MINUS the frame decorations.
public class BarnsleyFern extends JPanel {
//...
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 800);
}
Based upon...
ActionListener action = new ActionListener() {
public void actionPerformed(ActionEvent event) {
repaint();
}
};
Timer timer = new Timer(100, action);
//timer.start();
I suspect you're hoping to get a compounding paint, where the x/y properties are updated on each paint cycle.
Well, that's not going to work, for a number of reasons. You've found one. Graphics is a shared resource, every component painted in a given paint cycle will use the same Graphics instance, this means you could end up with dirty paints (and as a side effect, the result you seem to be looking for), but as you've discovered, it doesn't always work.
Painting can also occur for any number of reasons, many without you control or knowledge.
Painting should paint the current state, it shouldn't modify it. This is what your Timer should be doing.
You need to devise a model which will allow a incremental change each time the Timer ticks, the component will then use the model to simply paint the current state
I would recommend reading:
Performing Custom Painting
Painting in AWT and Swing
Laying Out Components Within a Container
for more details
I'm trying this very simple code. It runs but doesn't show the animation. I'm new to animations, so I don't know what I'm missing.
package sample;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class Sample extends JPanel implements ActionListener {
Timer tm = new Timer(5, this);
int x = 0, Velx = 5;
public void paint(Graphics g) {
g.setColor(Color.RED);
g.fillRect(x, 30, 50, 50);
tm.start();
}
public void actionPerformed(ActionEvent e) {
x = x + Velx;
repaint();
}
public static void main(String[] args) {
Sample X = new Sample();
JFrame a = new JFrame();
a.setTitle("Rectangle RED");
a.setSize(500,500);
a.setVisible(true);
}
}
Sample X = new Sample();
X is never added to the frame. See first tip (the bold part) for how to add X to the frame.
Other tips:
Sample should #Override the getPreferredSize() method to return a sensible size for the canvas. Then we can dispense with a.setSize(500,500); and instead a.add(X); a.pack(); to get the frame to be the exact right size to display the rendering.
The Timer should be started in some place other than the paint methods! I'd go for the constructor.
Custom painting in any JComponent should be done in the paintComponent(Graphics) method.
In all custom painting, we should immediately call the super method to ensure that previous drawings are erased by painting the BG and border of the container.
Please learn common Java nomenclature (naming conventions - e.g. EachWordUpperCaseClass, firstWordLowerCaseMethod(), firstWordLowerCaseAttribute unless it is an UPPER_CASE_CONSTANT) and use it consistently.
JFrame a = new JFrame(); a.setTitle("Rectangle RED"); could be shortened to JFrame a = new JFrame("Rectangle RED");
I'm creating a simple Break Out style game. The main game extends JFrame and I'm adding a JPanel to the frame.
When I was using paint() to draw the game graphics the items sat within the window as expected (i.e by their x, y coordinates).
I've updated the code to use BufferStrategy as I was getting flickering. Since the, the graphics that are rendered are offset by 22px.
This means the Bricks are off the top of the screen!
The code is as follows:
package BreakOut;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferStrategy;
public class Game extends JPanel implements KeyListener{
GameStateManager gsm = new GameStateManager();
BufferStrategy strategy;
public Game() {
//add menu state to GameStateManager
gsm.add(new MenuState(gsm));
createFrame();
while(true)
{
gsm.update();
//repaint();
render();
try{
Thread.sleep(10);
}
catch(InterruptedException e)
{
}
}
}
public void createFrame()
{
JFrame frame = new JFrame("Mini Tennis");
frame.setLayout(new BorderLayout());
this.setPreferredSize(new Dimension(400,400));
frame.add(this);
frame.pack();
frame.setBackground(Color.BLACK);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
addKeyListener(this);
this.setFocusable(true);
frame.createBufferStrategy(2);
strategy = frame.getBufferStrategy();
frame.setVisible(true);
System.out.println(frame.getInsets());
}
public void render()
{
Graphics g = strategy.getDrawGraphics();
super.paint(g);
gsm.render(g);
g.dispose();
strategy.show();
}
public void keyPressed(KeyEvent k) {
switch(gsm.getState())
{
case MAIN_MENU:
if(k.getKeyCode()==KeyEvent.VK_ENTER)
{
//add the PlayState to the Stack and update enum value
gsm.add(new PlayState(gsm, this));
}
break;
case PLAYING:
if(k.getKeyCode()==KeyEvent.VK_P)
{
//add the PlayState to the Stack and update enum value
gsm.add(new PauseState(gsm));
}
break;
case PAUSE:
if(k.getKeyCode()==KeyEvent.VK_P)
{
gsm.pop();
}
break;
case GAME_OVER:
if(k.getKeyCode()==KeyEvent.VK_ENTER)
{
gsm.pop();
}
break;
}
//send input to GameStateManager
gsm.keyPressed(k.getKeyCode());
}
public void keyReleased(KeyEvent k) {
gsm.keyReleased(k.getKeyCode());
}
public void keyTyped(KeyEvent k) {
gsm.keyTyped(k.getKeyCode());
}
public static void main(String[] args) {
new Game();
}
}
When I output System.out.println(frame.getInsets()); I get
java.awt.Insets[top=22,left=0,bottom=0,right=0]
I'm obviously doing something wrong but can't figure out why adding BufferStrategy would create the JPanel to be offset by 22px
Any help would be appreciated :)
Frames have borders and decorations, which is included within the bounds of the frame (they don't get added to the outside), from the looks of things you're using MacOS and the 22pixels to the top is the window title.
Best solution is, don't use the frame as the render surface, instead, use the Game class. This will mean it will need to extend from java.awt.Canvas instead of javax.swing.JPanel and you will need to create the BufferStrategy from it
If you override the Canvas's getPreferredSize method, you can use pack on the frame it will pack the window around this, so the physical frame will be larger then the content, but the content will be the size you would prefer
You will also want to move you main/game loop to separate thread, as this is current in the risk of blocking the Event Dispatching Thread, which could cause you no end of issues (like never getting a key event)
Im currently trying to make my first game and i am having trouble getting the repaint() method to work. I have checked my keyListeners and have confirmed that they are working a ok! The ship that i have created moves but only if i forcibly resize the window by dragging on the sides. If anyone has any tips i would be very greatful!
If you need any more information feel free to ask!
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class Main extends Canvas implements KeyListener{
public static Ship playerShip = new Ship(150,450,"F5S4-Recovered.png");
public int numberOfEnemies = 0;
public static void createFrame(){
Window frame1 = new Window();
final JPanel pane = new JPanel(){
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillRect(0, 0, 1000, 1000);
g.drawImage(playerShip.image, playerShip.xPos1, playerShip.yPos1, this);
}
};
frame1.add(pane);
}
public void keyTyped(KeyEvent e){System.out.println("KeyTyped");}
public void keyPressed(KeyEvent e){
switch(e.getKeyCode())
{
case KeyEvent.VK_LEFT :
playerShip.xPos1-=2;
break;
case KeyEvent.VK_RIGHT:
playerShip.xPos1+=2;
break;
}
repaint();
}
public void keyReleased(KeyEvent e){}
public static void main(String args[]){
createFrame();
}
}
Window class ----------------------------------------------------------
import javax.swing.*;
public class Window extends JFrame{
public Window()
{
setTitle("Space Game");
setSize(800,800);
setDefaultCloseOperation(EXIT_ON_CLOSE);
addKeyListener(new Main());
setVisible(true);
}
}
Your call to repaint() is repainting the class Canvas, but the painting is done on the JPanel pane. The resizing causes an automatic repaint of the panel. So to fix you want to pane.repaint(), but you can't do that unless you put the panel as a class member, so you can access it from the listener method. Right now, it's currently locally scoped in the createFrame() method.
Also, you should probably add the listener to the panel instead, and not even extend Canvas, since you're not even using it
Other Notes:
Look into using Key Bindings instead of low-level KeyListener
Swing apps should be run from the Event Dispatch Thread (EDT). You can do so by simply wrapping the code in your main in a SwingUtilities.invokeLate(..). See more at Initial Threads
Again, I'll just add, Don't extends Canvas
I have this basic Java application in witch dim_x and dim_y represent the dimensions of the window and the canvas within it. How can I get these values to change as the user changes the size of the window so that what is drawn on the canvas shrinks/expands accordingly?
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class MLM extends Canvas {
static int dim_x = 720;
static int dim_y = 480;
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Canvas canvas = new MLM();
canvas.setSize(dim_x, dim_y);
frame.getContentPane().add(canvas);
frame.pack();
frame.setVisible(true);
}
public void paint(Graphics g) {
// some stuff is drawn here using dim_x and dim_y
}
}
EDIT:
following Binyamin's answer I've tried adding this which works, but is there a better way to do it? As in, with not making canvas static, maybe?
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class MLM extends Canvas {
static int dim_x = 720;
static int dim_y = 480;
static Canvas canvas;
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
canvas = new MLM();
canvas.setSize(dim_x, dim_y);
frame.getContentPane().add(canvas);
frame.pack();
frame.setVisible(true);
frame.addComponentListener(new ComponentListener(){
public void componentResized(ComponentEvent e) {
Dimension d = canvas.getSize();
dim_x = d.width;
dim_y = d.height;
}
public void componentHidden(ComponentEvent e) {}
public void componentMoved(ComponentEvent e) {}
public void componentShown(ComponentEvent e) {}
});
}
public void paint(Graphics g) {
// some stuff is drawn here using dim_x and dim_y
}
}
Add a component listener, and implement componentResized. Look here.
frame.addComponentListener(new ComponentListener(){
#Override
public void componentResized(ComponentEvent e) {
//Get size of frame and do cool stuff with it
}
}
Don't mix AWT & Swing components without good reason (this use is not good reason). Instead of the Canvas you might use a JComponent or JPanel instead.
There is no use-case here for detecting resize. If the UI is resized, the paint() or paintComponent() of the custom rendered component will be called, and you can simply getWidth()/getHeight() to discover the size of the rendering area.
In my experience, when an AWT Canvas is nested within a JPanel, the paint() method of the Canvas is called when the window is expanded but not when it is contracted. Thus the Canvas can grow but never shrink back down. I refactored the subclassed Canvas with subclassing from JComponent instead.