I am working on an animation kind of program in java and for visualization. I am using the swing
and awt
libraries. I looked at a dozen implementations of this and all of them use a paint
Component method and then repaint()
when they want to draw shapes on the JFrame.
Is there some method that I can call from any class that I want that can for example change a single pixel value?
I also want to make an array list with shapes:
ArrayList<Rectangle> rectangles = new ArrayList<Rectangle>();
And I am curious how to add things to it.
I was thinking:
Rectangle rect = "something"
rectangles.add(rect);
But I'm not sure what something should be.
I am relatively new to this, so feel free to express your views if I'm missing something obvious.
Is there some method that I can call from any class that I want that can for example change a single pixel value?
You could create a BufferedImage, which has a setRGB(...)
method that would achieve what you wanted by drawing the image within the paintComponent(...)
method with Graphics drawImage(...)
method.
With regard to Rectangle rect = "something"
-- just look up the Rectangle constructors available to you in the Rectangle API and use the most appropriate one for you.
It's been suggested the you should use getGraphics
over paintComponent
, let's take a look at that for a second...
Opps, oh, that didn't quite work the way we'd expect! Let's even ignore why you shouldn't paint directly to top level containers (cause that's painting under the title bar)
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JButton draw = new JButton("Draw");
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridBagLayout());
frame.add(draw);
frame.setSize(150, 150);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
draw.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
drawARectangle(frame.getGraphics());
}
});
}
});
}
public void drawARectangle(Graphics g) {
g.setColor(Color.RED);
g.fillRect(20, 20, 100, 100);
}
}
Okay, let's have a look at paintComponent
and repaint
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.GridBagLayout;
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.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridBagLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private boolean drawBox;
public TestPane() {
setLayout(new GridBagLayout());
JButton draw = new JButton("Draw");
add(draw);
draw.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
drawBox = !drawBox;
repaint();
}
});
}
@Override
public Dimension getPreferredSize() {
return new Dimension(150, 150);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (drawBox) {
g.setColor(Color.RED);
g.fillRect(20, 20, 100, 100);
}
}
}
}
Swing has well documented painting process, have a look at Painting in AWT and Swing and Performing Custom Painting for more details and one should work within the design of the API.
Before anyone jumps down my throat and suggest you could use a WindowListener
or ContainerListener
or some other listener, the answer is, why? paintComponent
works just fine.
Swing uses a passive rendering engine, meaning that a paint might occur for any number of reasons, most of the time which you don't instantiate or know about, so unless your linked into the painting process, you won't know that it's occurred, resizing is just a really good way to demonstrate it
So can we please stop suggesting bad approaches like using getGraphics
or using null
layouts or KeyListener
or DocumentListener
for real time filtering of text components and maybe we can save a bunch of people a bunch of head aches
Apparently I was ranting when I called everyone an idiot for using repaint(); here's the better way.
Say I wanted to create a rectangle or image or line or oval on the jframe,
public JFrame frame = new JFrame();
public void paintingHome(){
//set up the jframe and stuff
System.out.println("This is a rectangle");
paintARectangle(frame.getGraphics(), 42, 256);
}
public void paintARectangle(Graphics g, int xCoord, int yCoord, int width, int height){
g.fillRact(xCoord, yCoord, width, height);
//simple as that
}
//and you can have as many as you want
public void paintWhatever(Graphics g, Something whatever){
g.doSomethingWith(whatever);
//that won't actually work until oracle releases version 238249.3432.4
}
//the key is using the frame.getGraphics() method
//repaint is unnecessary and restrictive