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");
Related
I've been trying to make this work so that there are 20 boxes each with 3 different sizes and 3 different colours that chosen at random, but i cant make them come out at different times and they just glitch into eachother and the colours are glitching together and stuff like that, anyone know how to fix it? Heres what i got so far:
import java.awt.*;
import javax.swing.*;
public class testwork extends JPanel { //JPanel is a class
int l = 0;
private int x = 10;
private int y = 500;
private void move()
{
x++;
}
boolean red = false;
boolean blue = false;
boolean green = false;
#Override
public void paint(Graphics g) { //JPanel is a class defined in
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
Color rectColor = new Color(0, 66, 89);
g2d.setColor(rectColor);
//belt
g2d.setColor(Color.lightGray);
g2d.fillRect(0,450,1500,200);
g2d.fillRect(700,0,200,1000);
g2d.setColor(Color.orange);
for (int i = -10000; i<10000; i=i+50) {
int m= i++;
g2d.fillRect(m, 450, 25, 200);
}
g2d.setColor(Color.DARK_GRAY);
g2d.fillRect(700, 450, 200, 200);
//boxes
while (l<=20) {
if (Math.random() < 0.5)
{g2d.setColor(Color.RED);;}
else if (Math.random() < 0.5) {g2d.setColor(Color.GREEN);}
else {g2d.setColor(Color.BLUE);}
if (Math.random() < 0.5)
{g2d.fillRect(x,y,50,50);}
else if (Math.random() < 0.5) {g2d.fillRect(x,y,50,100);}
else {g2d.fillRect(x,y,100,50);}
l++;
}
}
public static void main(String[] args) throws InterruptedException {
JFrame frame = new JFrame("Frame"); //Add our JPanel to the frame
frame.add(new attempt());//instantiate a new object
frame.setSize(1500, 1000);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
testwork p = new testwork();
frame.add(p);
while (true)
{
p.move(); //Updates the coordinates
p.repaint();
Thread.sleep(10); //Pauses for a moment
}}
}
Unfortunately, you are doing a number of things incorrectly.
Override paintComponent and not paint.
Don't use Thread.sleep. Use a Swing timer and an ActionListener
You are doing too much in the painting method. All event handling including calls to repaint() is done on the Event Dispatch Thread(EDT). So all your updating are done inside of paintComponent so only the last painted objects will be shown when you exit. Update your coordinates, data structure and anything else that needs to be painted outside of your paint method.
Put your boiler plate code inside your Testwork class constructor. Here is an example.
public Testwork() {
setPreferredSize(new Dimension(1000, 700));
Timer timer = new Timer(0, this);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(this);
// sizes the frame and jpanel and organizes the components.
frame.pack();
// centers the window in the screen
frame.setLocationRelativeTo(null);
// sets the delay in milliseconds
timer.setDelay(100);
// starts the timer
timer.start();
}
Here would be your actionListener code.
public void actionPerformed(ActionEvent ae) {
// update any variables that need to be used int he
// paint routine here. That means if you want to move something
// update the coordinates here and then use them in the paint method.
}
when you start up, your app, do it like this
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new Testwork());
}
There is much more to painting. I recommend checking out Custom Painting in the Java tutorials. Here is another example on this (SO) site.
So in the past few days I've tried to implement an easier version of a graph plotter.
One big problem I was confronted with was a bug that occured on repainting.
Basically I've designed my program in one class which is responsible for drawing the whole coordinate system and the given function after clicking a JButton in an other class. The other class contains the JButton which is pressed. After pressing the JButton it calls a function in the coordinate system class which repaints the picture. Both of those classes are extending JPanel.
The bug was that when I was doing the repainting on pressing the button, the button was drawn on the coordinate System and not in its original place, so in other words on the other JPanel even though I didn't change a thing about placements and stuff. Both classes were added to a JFrame which use a GridLayout.
Can anyone tell me why super.paintComponent(g); solved that bug?
Edit: Added Code
Window class
public class main {
public static void main(String[] args) throws SemanticFailureException {
int x = 800;
int y = 600;
JFrame frame = new JFrame();
frame.setLayout(new GridLayout());
frame.setTitle("Function plotter");
frame.setSize(2*x, 2*y);
Surface test = new Surface(x, y, 10);
CommandDraw test1 = new CommandDraw(x/2,y,test);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.add(test);
frame.add(test1);
frame.setVisible(true);
}
}
Coordinate System class: (changed drawing the coordinate system to a rectangle for simplicity, the bug still occures with only drawing a rectangle)
public class Surface extends JPanel {
/**
*
*/
private static final long serialVersionUID = 1L;
boolean drawFunct;
public Surface(int x1, int y1, int coordLength) {
setSize(x1,y1);
drawFunct = false;
}
public void paintComponent(Graphics g) {
super.paintComponent(g); // without this the jbutton occures on the left
// create Graphics object to get more functions
Graphics2D g2 = (Graphics2D) g;
// draw Plotter
drawFunction(g2);
if (drawFunct)
g2.drawLine(0, 0, 80, 80);
}
public void drawFunction(Graphics2D g) {
g.drawRect(40, 40, 30, 30);
}
public void redraw() {
drawFunct = true;
repaint();
}
}
The Class with the JButton:
public class CommandDraw extends JPanel {
/**
*
*/
private static final long serialVersionUID = 1L;
JButton makeDraw;
JTextField inputPoly;
Surface surf;
public CommandDraw(int x, int y, Surface surf) {
this.surf = surf;
setSize(x,y);
setLayout(new FlowLayout());
makeDraw = new JButton("draw Function");
makeDraw.setBackground(Color.LIGHT_GRAY);
makeDraw.setFocusable(false);
makeDraw.addActionListener( new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
surf.redraw();
}
});
add(makeDraw);
inputPoly = new JTextField("Input polynomial");
inputPoly.setHorizontalAlignment(JTextField.CENTER);
add(inputPoly);
}
}
Can anyone tell me why super.paintComponent(g); solved that bug?
Because the call to paintComponent(g) of the superclass (JPanel) will guarantee that panel will be rendered as expected before you do your paint operations. You need to make sure that your JPanel will behave, in the Graphics perspective, like any other JPanel.
A template for a customized JPanel to draw should be something like:
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JPanel;
public class MyDrawPanel extends JPanel {
#Override
protected void paintComponent( Graphics g ) {
super.paintComponent( g );
// create a new graphics context based on the original one
Graphics2D g2d = (Graphics2D) g.create();
// draw whatever you want...
g2d.dispose();
}
}
EDIT
You need to call super.paintComponent(g) to tell that the JPanel paintComponent(Graphics) version should be execute before you start doind your customized things. It's something like to ask the JPanel to prepare its paint capabilities to be used. A good starting point to these kind of doubts is the documentation: https://docs.oracle.com/javase/10/docs/api/javax/swing/JComponent.html#paintComponent(java.awt.Graphics)
The dispose() method is being called in the copy of the original Graphics. If you dispose the Graphics that is passed to the method, you may have some issues too. You could use the original Graphics to perform you painting operations, but you shouldn't, so a better practice is to make a copy of the original Graphics and dispose it after using.
Take a look here too: How does paintComponent work?
so I am new to designing graphics in Java I was wondering if anyone could help me here. I have two classes and I want to display both of them at the same time in a JFrame. But only one or the other get displayed.
public class Tutorial extends JPanel implements ActionListener {
Background bc = new Background();
Timer tm = new Timer(5,this);
int x =0, velX = 2;
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.RED);
g.fillRect(x, 30, 50, 30);
tm.start();
}
#Override
public void actionPerformed(ActionEvent e) {
if(x<0 || x>550){
velX = -velX;
}
x = x+ velX;
repaint();
}
public static void main(String [] args){
Background bc = new Background();
Tutorial t = new Tutorial();
JFrame jf = new JFrame();
jf.setTitle("Tutorial");
jf.setSize(600,400);
jf.setVisible(true);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.add(t);
jf.add(bc);
}
My second class
public class Background extends Canvas {
public void paint(Graphics g){
g.setColor(Color.GREEN);
g.fillRect(0,0,600,125);
g.fillRect(0,250,600,125);
g.setColor(Color.black);
g.fillRect(0,125,600,125);
}
For some reason I can only get either Background to be displayed or Tutorial?
Can anyone point me in the right direction or tell me where I am going wrong. I want to be able to display multiple things like these classes in the one window
The default layout manager of a JFrame is a BorderLayout.
By using the single-argument JFrame.add() function, you're adding both of the components to the BorderLayout.CENTER portion of your JFrame. This means that you'll only see one of the components.
The solution is to either use a different layout manager, or to add the components to different sections of your BorderLayout.
More info here: http://docs.oracle.com/javase/tutorial/uiswing/layout/visual.html
I am trying to draw an image on a JPanel that requires thousands of calculations, and I want to animate the progression of the drawing. I.e., instead of doing all 100K iterations of drawing in one go, and then repainting the JPanel, I want to repaint after each iteration, then pause for a fraction of a second, so the user sees the image gradually appearing. However, each refresh of the JPanel erases previous drawings, so my method doesn't work. How can I do this without replicating all (1..N-1) calculations on the Nth iteration?
Consider this example: I want "snow" to gradually appear on the screen. However, this code will only show the 100,000th "snowflake" as all previous ones get erased each time repaint() is called.
import javax.swing.*;
import java.awt.*;
import java.util.Random;
class spanel extends JPanel{
public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawLine(snow.x, snow.y, snow.x, snow.y);
}
}
class snow extends Thread {
static int x,y;
Random r = new Random();
public void run(){
JFrame sboard = new JFrame();
sboard.setSize(600,600);
sboard.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
spanel mypanel = new spanel();
sboard.add(mypanel);
sboard.setVisible(true);
for (int i=0;i<100000;i++){
x=r.nextInt(600);
y=r.nextInt(600);
sboard.repaint();
try {
snow.sleep((long)10);
} catch (InterruptedException e) {};
}
}
}
public class SnowAnim {
public static void main(String[] args) {
(new snow()).start();
}
}
Custom Painting Approaches shows how to draw on a BufferedImage. There are also plenty of other examples in the forum.
Also, when doing animation you should use a Swing Timer to schedule the animation.
How can I do this without replicating all (1..N-1) calculations on the Nth iteration?
Add them to a BufferedImage as seen in this example.
You should probably do your painting on a buffer, then draw the current state of the buffer in paintComponent();
You could also skip the call to super.paintComponent(g);, but then you would have to worry about other elements getting visually "stuck" in your panel.
Thanks all! I figured it out. BufferedImage solves it. For the record, here is my updated code. It also implements camickr's suggestion to use a Swing timer instead of a thread to schedule the animation. I also made some aesthetic changes to make the output look more like snow :-) Thanks again!
import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
import java.util.Random;
import java.awt.event.*;
class spanel extends JPanel{
int x,y,rad,i;
static Random r = new Random();
BufferedImage image;
Graphics2D g2d;
Timer timer;
spanel(){
image = new BufferedImage(600, 600, BufferedImage.TYPE_INT_ARGB);
g2d = (Graphics2D)image.getGraphics();
setBackground(Color.black);
g2d.setColor(Color.white);
i=0;
ActionListener listener = new ActionListener() {
public void actionPerformed(ActionEvent ae) {
iterate();
}
};
timer = new Timer(10, listener);
timer.start();
}
public void iterate(){
x=r.nextInt(600);
y=r.nextInt(600);
rad=r.nextInt(5)+5;
g2d.fillOval(x, y, rad, rad);
repaint();
i++;
if (i==1000){timer.stop();}
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawImage(image,0,0,null);
}
}
public class SnowAnim {
public static void main(String[] args) {
JFrame sboard = new JFrame();
sboard.setSize(600,600);
sboard.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
spanel mypanel = new spanel();
sboard.add(mypanel);
sboard.setVisible(true);
}
}
I want to change a JPanel-object in an applet at startup/init. I can't figure out how to do this. I've made a simple example of my problem in which I clear the JPanel. It does not work when it is called by the init() method but it works when I press the button. What can I do to change the JPanel at startup/init?
import javax.swing.JApplet;
import javax.swing.JPanel;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class TestStartUpApplet extends JApplet {
JPanel panel;
#Override
public void init() {
System.out.println("Init");
erasePanel();
}
private void erasePanel() {
Graphics g = panel.getGraphics();
g.clearRect(0, 0, 117, 48);
}
public TestStartUpApplet() {
getContentPane().setLayout(null);
panel = new JPanel();
panel.setBackground(Color.RED);
panel.setBounds(35, 36, 117, 48);
getContentPane().add(panel);
JButton btnTest = new JButton("Test");
btnTest.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
erasePanel();
}
});
btnTest.setBounds(35, 108, 117, 25);
getContentPane().add(btnTest);
}
}
Works just for me:
public class AppletPaintTest extends JApplet {
#Override
public void init() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
setLayout(new BorderLayout());
ImagePane pane = new ImagePane();
pane.setBackground(Color.RED);
pane.setOpaque(false); // Little trick
add(pane);
}
});
}
#Override
public void start() {
super.start();
}
public class ImagePane extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g.create();
Insets insets = getInsets();
int x = insets.left;
int y = insets.top;
int width = getWidth() - 1 - (insets.left + insets.right);
int height = getHeight() - 2 - (insets.top + insets.bottom);
g2d.setColor(getBackground());
g2d.fillRect(x + 10, y + 10, width - 20, height - 20);
g2d.dispose();
}
}
}
question
no idea why do you want to clear empty JPanel without any custom painting
what's wrong with JPanel with red Background
you clear JApplet before became visible on the screen
doesn't works correctly, because doesn't works anything
suggestions
don't use AbsoluteLayout, use LayoutManager instead
Graphics g = panel.getGraphics(); is usefull for printing to the printer, or save Graphics to the image, don't use this method for another reason
read how JApplet works
maybe to use JFrame in the case that you don't want to publish your GUI to the web browser
maybe to read tutorial about 2D Graphics
Whenever you do any custom painting outside of the usually system calls to your paint() or paintComponent() methods, you must call invalidate() on any components you wish to repaint. So for your erasePanel() method, I would suggest setting some flag then calling panel.invalidate(). Then inside your panel's paintComponent() method, you can check the flag to see if you need to draw the introductory picture or just leave the panel blank.
Ok, it seems that the problem was that my coding was just bad. I wanted to change a panel object by a method from another class and that's not the way to do it. I rewrote my code and made a panel class in which the painting is done using paintcomponent. I now use objects of this panel class and it shows the graphics I want at startup.
Thanks for the help!