This question already has answers here:
Layered panels in Swing with custom painting
(2 answers)
Closed 9 years ago.
I am trying to make a GUI using Java graphics, but for some reason it won't work. Here is the code:
public class ScreenCap extends Canvas {
/**
* #param args the command line arguments
*/
#SuppressWarnings("ResultOfObjectAllocationIgnored")
public static void main(String[] args) {
new ScreenCap();
}
public ScreenCap() {
Window window = new Window(this);
window.setVisible(true);
this.addMouseListener(new MouseHandler());
drawComponents();
}
private void drawComponents() {
System.out.println("in draw");
createBufferStrategy(3);
BufferStrategy bs = getBufferStrategy();
Graphics g = bs.getDrawGraphics();
g.setColor(Colors.BG);
g.fillRect(0, 0, getWidth(), getHeight());
}
}
I would consider using Swing instead of AWT. AWT is pretty out-dated. If using swing, you would do something like the below code
Subclass JPanel
Override paintComponent(Graphics g)
Cast to Graphics2D (optional) for more versatilitiy
Draw in the paintComponent method
Add an instance of your panel class to a container.
Read more on Graphics here. Loaded with tutorials. More on Swing here.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class SwingDemo extends JPanel {
private static final int DIM_WIDTH = 500;
private static final int DIM_HEIGHT = 500;
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setColor(Color.BLUE);
g2.fillRect(100, 100, 200, 200);
}
public static void createAndShowGui(){
JFrame frame = new JFrame();
frame.add(new SwingDemo());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(true);
frame.pack();
frame.setVisible(true);
}
public Dimension getPreferredSize(){
return new Dimension(DIM_WIDTH, DIM_HEIGHT);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
public void run(){
createAndShowGui();
}
});
}
}
Nothing spectacular :)
Related
If I create a JFrame 800x600 pixels and draw a line from (0,0) to (800,600) it doesn't go from corner to corner, so, where is the (0,0) and where is the (800,600)?
Here is the code
import java.awt.Graphics;
import javax.swing.JFrame;
public class Point0_0test extends JFrame {
public Point0_0test() {
setTitle("Test");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(800, 600);
setLocationRelativeTo(null);
}
#Override
public void paint(Graphics g) {
super.paint(g);
g.drawLine(0, 0, 800, 600);
}
public static void main(String[] args) {
Point0_0test test = new Point0_0test();
test.setVisible(true);
}
}
Here you can see what appears when the program is running
If you want a drawing area that's 800 x 600 pixels, then set a drawing area that's 800 x 600 pixels. Who cares how big the frame is?
Here's a simple drawing GUI that I created. I made it 400 x 300 pixels so it would fit in the answer easier.
Here's the code. It's a minimal, runnable example for setting the size of the drawing area.
import java.awt.BasicStroke;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class SimpleDrawingArea implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new SimpleDrawingArea());
}
#Override
public void run() {
JFrame frame = new JFrame("Simple Drawing Panel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new DrawingPanel());
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
public DrawingPanel() {
this.setPreferredSize(new Dimension(400, 300));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setStroke(new BasicStroke(4f));
g2d.drawLine(0, 0, 400, 300);
}
}
}
The JFrame size and coordinates count the size of the decorations, such as the top part of the window contains the bar with the close button, the rest contain the extra outline that is added on Windows(Ubuntu, at least, doesn't seem to add an extra outline). In order to get a line like you would want to, you should use JFrame.getInsets(), which returns the size of the GUI that decorates the JFrame, like this:
import java.awt.Insets;
#Override
public void paint(Graphics g) {
super.paint(g);
Insets in = getInsets();
g.drawLine(in.left, in.top, 800-in.right, 600-in.bottom);
}
Edit: this means that you don't have an actual 800x600 space. The Insets class seems to be "created" when setVisible(true) is called, as far as I can tell. So this would be how the code for that looks like:
import javax.swing.JFrame;
import java.awt.Insets;
import java.awt.Graphics;
public class InsetsTest extends JFrame {
public InsetsTest() {
super();
setTitle("Test");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(800,600);
setVisible(true);
Insets insets= getInsets();
setSize(800+insets.right+insets.left,600+insets.top+insets.bottom);
setLocationRelativeTo(null);
}
#Override
public void paint(Graphics g) {
super.paint(g);
Insets in = getInsets();
g.drawLine(in.left, in.top, 800+in.left, 600+in.top);
}
public static void main(String[] args) {
InsetsTest test = new InsetsTest();
test.setVisible(true);
}
}```
The window size should be defined without OS specific window decoration.
Try to add
this.setUndecorated(true);
before the
this.setVisible(true);
I have three classes, the first one is the main window smthn like
public class Starter extends JFrame{
JButton b=new JButton("Game");
Starter(){
setSize(200,200);
add(b);
b.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
TryGraph gg=new TryGraph();
}
});
setVisible(true);
}
public static void main(String [] args){
Starter g= new Starter();
}
}
Then the second class is a window that has a panel where the graphics is going to be drawn on
public class TryGraph {
static int w=640,h=480;
TryGraph(){
JFrame mF=new JFrame();
GPan pan=new GPan();
mF.setLocationRelativeTo(null);
mF.setResizable(false);
mF.setSize(w,h);
mF.add(pan);
mF.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mF.setVisible(true);
pan.playGame();
}
public static void main(String []args){
TryGraph t=new TryGraph();
}
}
Then lastly the panel class which does the drawing
public class GPan extends JPanel{
private boolean running;
private BufferedImage image;
private Graphics2D g;
int x,y;
public GPan(){
x=TryGraph.w;
y=TryGraph.h;
init();
}
public void init(){
running=true;
image=new BufferedImage(x,y,BufferedImage.TYPE_INT_RGB);
g=(Graphics2D)image.getGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
}
public void playGame(){
while(running){
g.setColor(new Color(102,102,102));
g.fillRect(0,0,x,y);
g.setColor(new Color(255,0,0));
g.fillOval(0, 0, 100,100);
repaint();
}
}
public void paintComponent(Graphics g){
Graphics2D g2 = (Graphics2D)g;
g2.drawImage(image,0,0,x,y,null);
g2.dispose();
}
}
The problem is if I'm using an event from the Starter class window to the TryGraph class window the GPan panel won't draw the graphics when it loops through play game() method. But when the TryGraph class is executed directly it works fine
A couple of problems I noticed.
When you call main in TryGraph, you create a new instance. But you already have an instance of TryGraph when the button was pressed.
Don't extend JFrame. It's bad practice. Just create an instance of it.
Only use one static main entry point.
The big problem is that you put repaint() in a tight loop. Don't do that. Just call repaint(). Remember that repaint is run on the Event Dispatch Thread. So if that thread is tied up, nothing else will work.
Don't dispose of your graphics context. You can do that if you create a new one but otherwise don't get rid of it.
Finally, put super.paintComponent() as the first statement in your JPanel paintComponent method.
I suggest you read about painting and the EDT in the Java Tutorials.
Here is the modified code, all in one file.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Starter {
JButton b = new JButton("Game");
JFrame frame = new JFrame();
Starter() {
frame.setSize(200, 200);
frame.add(b);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
TryGraph gg = new TryGraph();
}
});
frame.setVisible(true);
}
public static void main(String[] args) {
Starter g = new Starter();
}
}
class TryGraph {
static int w = 640, h = 480;
TryGraph() {
JFrame mF = new JFrame();
GPan pan = new GPan();
mF.setLocationRelativeTo(null);
mF.setResizable(false);
mF.setSize(w, h);
mF.add(pan);
mF.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mF.setVisible(true);
pan.playGame();
}
}
class GPan extends JPanel {
private boolean running;
private BufferedImage image;
private Graphics2D g;
int x, y;
public GPan() {
x = TryGraph.w;
y = TryGraph.h;
init();
}
public void init() {
running = true;
image = new BufferedImage(x, y, BufferedImage.TYPE_INT_RGB);
g = (Graphics2D) image.getGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
}
public void playGame() {
repaint();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g.create();
g2.setColor(new Color(102, 102, 102));
g2.fillRect(0, 0, x, y);
g2.setColor(new Color(255, 0, 0));
g2.fillOval(0, 0, 100, 100);
// g2.drawImage(image,0,0,x,y,null);
g2.dispose();
}
}
One additional suggestion. In your original code I believe you implemented a listener by implementing the interface in a private class. Good idea except you should extend the adapter class for the given interface. For example, for mouseListener, extend MouseAdapter. It provides dummy methods so you don't have to create them yourself. It helps make your code more readable. Of course, single method interfaces don't have ( or need) adapters.
so i'm trying to put Rectangle2D.Float in window using JFrame but when i'm compiling code i'm getting just blank window without rectangle. Can you guys take look on it and tell me what i'm doing wrong?
package zestaw8;
import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
class Plansza85 extends JPanel
{
Shape figura;
Plansza85(Shape figura)
{
this.figura=figura;
}
}
public class Zestaw8_cw85
{
public static void main(String[] args)
{
Shape obj1;
obj1=new Rectangle2D.Float(100,100,140,140);
zestaw8.Plansza85 p;
p=new zestaw8.Plansza85(obj1);
JFrame jf=new JFrame();
jf.setTitle("Plansza p");
jf.setSize(400,400);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
jf.add(p);
}
}
You seem to have a misunderstanding of how painting works in Swing.
Start by looking at Performing Custom Painting, Painting in Swing and 2D Graphics. Rectangle2D is a graphics primitive, which needs to be painted through the normal custom painting process
As per the common recommendations of Performing Custom Painting you should override the paintComponent method of the Plansza85 and paint the Shape through the Graphics2D API, something like...
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.Rectangle2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Shape obj1;
obj1 = new Rectangle2D.Float(100, 100, 140, 140);
Plansza85 p;
p = new Plansza85(obj1);
JFrame jf = new JFrame();
jf.setTitle("Plansza p");
jf.add(p);
jf.pack();
jf.setLocationRelativeTo(null);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
}
});
}
class Plansza85 extends JPanel {
Shape figura;
Plansza85(Shape figura) {
this.figura = figura;
}
#Override
public Dimension getPreferredSize() {
if (figura == null) {
return super.getPreferredSize();
}
Rectangle2D bounds = figura.getBounds2D();
double width = bounds.getMaxX();
double height = bounds.getMaxY();
return new Dimension((int)width + 1, (int)height + 1);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(getForeground());
g2d.draw(figura);
g2d.dispose();
}
}
}
for example.
I've also overridden the getPreferredSize method to generate an appropriate sizing hint for the component based on the size of the shape, I've done this because I dislike guess work and the window also includes variable sized borders and title bars which will change the size the panel if you only rely on setSize
You need to override the paintComponent method of Plansza85
import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
class Plansza85 extends JPanel {
private Shape figura;
Plansza85(Shape figura) {
this.figura = figura;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.draw(figura);
}
}
public class Zestaw8_cw85 {
public static void main(String[] args) {
Shape obj1;
obj1 = new Rectangle2D.Float(100, 100, 140, 140);
Plansza85 p;
p = new Plansza85(obj1);
JFrame jf = new JFrame();
jf.setTitle("Plansza p");
jf.setSize(400, 400);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.add(p);
jf.setVisible(true);
}
}
Hope it helps!
package drawinglinebymethods;
import java.awt.*;
import javax.swing.JFrame;
public class DrawingLineByMethods extends Frame {
public JFrame f=new JFrame();
void fra_size()
{
f.setSize(450, 300);
}
void fra_visible()
{
f.setVisible(true);
}
void fra_title()
{
f.setTitle(" java frame");
}
void exit()
{
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void line(Graphics g) {
g.drawLine(10, 10, 20, 300);
}
public static void main(String[] args) {
DrawingLineByMethods obj = new DrawingLineByMethods();
obj.fra_size();
obj.fra_title();
obj.fra_visible();
obj.fra_exit();
obj.line(g); // <-- error here
}
}
Your question suggests that you are not yet clear on how graphics and drawing works in Swing GUI's. Some suggestions for you:
Don't have your class extend java.awt.Frame as this makes no sense. You're not creating an AWT Frame window and have no need of this.
Create a class that extends JPanel and draw in its paintComponent method as the Swing painting tutorials (link now added) show you.
You never call drawing code directly but rather it is indirectly called by the JVM.
If you want to draw from outside of the GUI itself, then draw to a BufferedImage, one that is then displayed within the GUI.
Don't use a Graphics object obtained by calling getGraphics() on a GUI component as the object thus obtained will not persist, and this risks you creating unstable graphics or worse -- throwing a NullPointerException.
For example:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.*;
#SuppressWarnings("serial")
public class LineDraw extends JPanel {
private static final int PREF_W = 450;
private static final int PREF_H = 300;
public LineDraw() {
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// use rendering hints to draw smooth lines
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// do drawing here
g.drawLine(10, 10, 20, 300);
}
private static void createAndShowGui() {
LineDraw mainPanel = new LineDraw();
JFrame frame = new JFrame("Line Draw");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
createAndShowGui();
});
}
}
If you want to add lines as the program runs, again, use a BufferedImage that is drawn within the JPanel's paintComponent method. When you need to add a new line to the GUI, extract the Graphics or Graphics2D object from the BufferedImage using getGraphics() or createGraphics() respectively (it's OK to do this), draw with this Graphics object, dispose of the Graphics object, and repaint the GUI. For example in the code below, I draw a new line when the button is pressed by adding code within the JButton's ActionListener (actually its AbstractAction which is similar to an ActionListener but more powerful):
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import javax.swing.*;
#SuppressWarnings("serial")
public class LineDraw extends JPanel {
private static final int PREF_W = 450;
private static final int PREF_H = 300;
private BufferedImage img;
private int yDistance = 20;
private int deltaY = 10;
public LineDraw() {
img = new BufferedImage(PREF_W, PREF_H, BufferedImage.TYPE_INT_ARGB);
add(new JButton(new DrawLineAction("Draw Line", KeyEvent.VK_D)));
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// draw the buffered image here
if (img != null) {
g.drawImage(img, 0, 0, this);
}
// use rendering hints to draw smooth lines
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// do drawing here
g.drawLine(10, 10, 20, 300);
}
public void drawNewLines() {
Graphics2D g2 = img.createGraphics();
g2.setColor(Color.BLACK);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
yDistance += deltaY;
g2.drawLine(10, 10, yDistance, PREF_H);
g2.dispose();
repaint();
}
private class DrawLineAction extends AbstractAction {
public DrawLineAction(String name, int mnemonic) {
super(name); // give button its text
putValue(MNEMONIC_KEY, mnemonic); // alt-hot key for button
}
#Override
public void actionPerformed(ActionEvent e) {
drawNewLines();
}
}
private static void createAndShowGui() {
LineDraw mainPanel = new LineDraw();
JFrame frame = new JFrame("Line Draw");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
createAndShowGui();
});
}
}
Line is a non-static method, the only way to call it from a static method (main here) is to have an instance of the class.
First coord in this case should be 0,0 and not 8,30. What am i doing wrong(i am using NetBeans)
import java.awt.Color;
import java.awt.Graphics;
public class TEST extends javax.swing.JFrame {
#Override
public void paint(Graphics g){
super.paint(g);
g.setColor(Color.blue);
g.drawRect(8, 30, 200, 200);
repaint();
}}
Add a JPanel to the frame and paint in that. The frame's coordinates include the decorations (title bar, borders, etc.). It would look something like this:
public class Test extends JFrame {
public static void main(String[] args) {
new Test();
}
private Test() {
add(new MyPanel());
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(600, 600);
setVisible(true);
}
private class MyPanel extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.blue);
g.drawRect(8, 30, 200, 200);
}
}
}
Also, don't call repaint(); in paint();. That will cause an infinite loop and will freeze the entire program.
The problem is your paint(..) method is not taking into account the JFrame Insets by calling getInsets which as docs state:
If a border has been set on this component, returns the border's
insets.
this code works fine:
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class Test {
public Test() {
createAndShowGui();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Test();
}
});
}
private void createAndShowGui() {
JFrame frame = new JFrame() {
#Override
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.blue);
g.drawRect(0 + getInsets().left, 0 + getInsets().top, 200, 200);
}
};
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
}
however this is not best practice.
Rather add JPanel to JFrame and override paintComponent(Graphics g) of JPanel dont forget call to super.paintComponent(g) as first call in the overridden method and than draw there (dont forget to override getPreferredSize() and return correct Dimensions so the JPanel will fit its drawing/graphic content) this problem will no longer persist as JPanel is added at correct co-ordinates on contentPane like so:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Test {
public Test() {
createAndShowGui();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Test();
}
});
}
private void createAndShowGui() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.addRenderingHints(new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY));
g2d.setColor(Color.blue);
g2d.drawRect(0, 0, 200, 200);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
};
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
}
The above includes Graphics2D and RenderingHints i.e anti-aliasing. Just for some better looking drawings :)