I've got a class that makes a JFrame and adds a panel on it
and the second one extends the JPanel and paints on it
The first one (JFrame)
class MyWindow {
void qwe() {
JFrame frame = new JFrame("qwe");
frame.setVisible(true);
frame.setLocationRelativeTo(null);
frame.setSize(300, 200);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MyPanel panel = new MyPanel();
panel.setLayout(null);
frame.add(panel);}}
and the second one (JPanel)
class MyPanel extends JPanel {
public void paintComponent(Graphics g) {
g.drawRect(50,50,90,70);
}
public void addShape() {
Graphics g = this.getGraphics();
Graphics2D gg = (Graphics2D) g;
gg.drawString("qwe",20,20);}}
how can i add a String on the JPanel by using the addShape() method ?
As a concrete example of #camickr's point, note that MyPanel already override's paintComponent(), so you can pass a reference to the Graphics context to addShape(). Additionally,
Be sure to invoke super.paintComponent(g).
Override getPreferredSize() to establish the component's preferred size.
Swing GUI objects should be constructed and manipulated only on the event dispatch thread.
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class MyWindow {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new MyWindow().qwe();
}
});
}
void qwe() {
JFrame frame = new JFrame("qwe");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MyPanel panel = new MyPanel();
panel.setLayout(null);
frame.add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private static class MyPanel extends JPanel {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawRect(50, 50, 90, 70);
addShape(g);
}
public void addShape(Graphics g) {
g.drawString("qwe", 20, 20);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 200);
}
}
}
Don't use the getGraphics() method of your component to do custom painting. This type of painting is only temporary and will be lost the next time Swing determines the component needs to be painted.
Custom painting should always be done in the paintComponent() method of your component.
See Custom Painting Approaches for the two commons was to do what you want.
Related
I am using the same numbers to set the size of my frame as I am to paint the rectangle, yet the graphics are larger than my JFrame. Why is this?
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Test {
public static void main(String[] arguments) {
Test test = new Test();
JFrame frame = new JFrame();
DrawPane contentPane = test.new DrawPane();
frame.setContentPane(contentPane);
frame.setSize(300, 400);
frame.setVisible(true);
}
private class DrawPane extends JPanel {
#Override
protected void paintComponent(Graphics g) {
g.setColor(Color.YELLOW);
g.fillRect(0, 0, 300, 400);
}
}
}
It's because of border. And it's a good example why you shouldn't explicitly determine size for your JFrame. Instead calling setSize override getPreferredSize method from your JPanel:
private class DrawPane extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.YELLOW);
g.fillRect(0, 0, 300, 400);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 400);
}
}
Then call pack for your JFrame instead setSize and your JFrame will adjust it's size according to its content.
I am trying to draw a rectangle in the class "Graphics", but for some reason the rectangle does not appear, but the program returns no errors. I have never experience issues such as this before so I am rather confused.
Main()
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Main
{
public Main()
{
JFrame window = new JFrame();
Sound soundCall = new Sound();
Graphics graphicsCall = new Graphics();
final JPanel container = new JPanel();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.getContentPane().add(container);
window.setSize(600, 400);
window.setLocationRelativeTo(null);
window.setVisible(true);
window.setResizable(false);
}
public static void main(String args[])
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
new Main();
}
});
}
Graphics()
import java.awt.Color;
import java.awt.Graphics2D;
import javax.swing.JPanel;
public class Graphics extends JPanel
{
public void paintComponent(java.awt.Graphics g)
{
super.paintComponent(g);
g.setColor(Color.GRAY);
g.drawRect(500, 500, 500, 500);
}
}
EDIT FOR HOVERCRAFT
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Main
{
public Main()
{
JFrame window = new JFrame();
Sound soundCall = new Sound();
Draw drawCall = new Draw();
final JPanel container = new JPanel();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.getContentPane().add(drawCall);
window.setSize(600, 400);
window.setLocationRelativeTo(null);
window.setVisible(true);
window.setResizable(false);
}
public static void main(String args[])
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
new Main();
}
});
}
}
Through adding this window.getContentPane().add(drawCall); asks me to change drawCall to a Component
EDIT 2:
public class Draw
{
public class Graphics extends JPanel
{
public void paintComponent(java.awt.Graphics g)
{
super.paintComponent(g);
g.setColor(Color.GRAY);
g.drawRect(0, 0, 500, 500);
}
}
}
ERROR
The method add(Component) in the type Container is not applicable for the arguments (Draw)
You add your graphicsCall variable to nothing, and so it will not be displayed. Solution: add it to a container such as that JPanel that you just created, or perhaps directly to the JFrame's contentPane.
i.e., change this:
JFrame window = new JFrame();
Sound soundCall = new Sound();
Graphics graphicsCall = new Graphics();
final JPanel container = new JPanel();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.getContentPane().add(container);
to this:
JFrame window = new JFrame();
Sound soundCall = new Sound();
Graphics graphicsCall = new Graphics();
// final JPanel container = new JPanel();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.getContentPane().add(soundCall);
As an aside, you will want to re-name that class from Graphics to something else, or else you risk confusing yourself or your compiler since there already exists a critical Java class with that name.
Also, avoid using setSize(...). Better to have your drawing JPanel override getPreferredSize() and to call pack() on your JFrame.
Edit
As per MadProgrammer's astute observation, you're drawing outside of the bounds of your component.
Edit 2
Regarding your latest code, this:
public class Draw
{
public class Graphics extends JPanel
{
public void paintComponent(java.awt.Graphics g)
{
super.paintComponent(g);
g.setColor(Color.GRAY);
g.drawRect(0, 0, 500, 500);
}
}
}
is useless dreck. Why are you needlessly wrapping a class inside of a class? Instead why not simply:
public class Draw extends JPanel {
public void paintComponent(java.awt.Graphics g)
{
super.paintComponent(g);
g.setColor(Color.GRAY);
g.drawRect(0, 0, 500, 500);
}
#Override
public Dimension getPreferredSize() {
// return an appropriate Dimension here
}
}
I have two classes: one is JFrame and second is JPanel. When i add JPanel object in JFrame class it makes not error but did not show JPanel result. It only shows blank JFrame.
This is my JPanel class:
public class grafix extends JPanel {
#Override
public void paintComponent(Graphics g){
super.paintComponents(g);
Graphics2D g2= (Graphics2D) g;
Rectangle r = new Rectangle(15,10,200,300);
g2.draw(r);
g2.setColor(Color.blue);
g2.fillOval(50, 50, 30,30);
g2.drawString("Hello World", 120, 50);
}
}
And this is my JFrame Class:
public class JFrame extends javax.swing.JFrame {
public JFrame() {
initComponents();
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
grafix gf = new grafix(); // object created of JPanel class
frame.getContentPane().add(gf);// by adding object
frame.pack();
frame.setVisible(true);
}
});
}
}
No error being mentioned in Netbean but it did not show any drawing but frame only.
The graphics are shown but your frame is too small to show the panel graphics. Your Grafix panel component is using the default preferred size of 0x0 to the frame reveals nothing. Override getPreferredSize in the class to allow the correct size to be set when the frame is packed
#Override
public Dimension getPreferredSize() {
return new Dimension(250, 400);
}
Can anyone tell me why the rectangle is not showing up on the frame?
I only see a button on the frame. Please help.
I tried to using the paint method for drawing the rectangle.
Should I use paintComponent() or just paint()?
public class GUI2 {
public static void main(String[] args) {
JFrame frame = new JFrame("Game");
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500, 500);
frame.setLocation(500, 200);
JPanel panel = new JPanel();
frame.add(panel);
JButton button = new JButton("YO");
panel.add(button);
button.addActionListener(new Action());
frame.paint(null);
}
public void paint(Graphics g) {
g.drawRect(250, 250, 200, 100);
}
static class Action implements ActionListener {
public void actionPerformed(ActionEvent e) {
}
}
}
You shouldn't have to explicitly call paint
get rid of the paint method
Make an Inner JPanel class
Yes override paintComponent in the JPanel class
Call super.paintComponent in the paintComponent method.
Add the class JPanel to the JFrame
Don't do everything inside the main, as you'll find out, that static will cause a problem for you. Do everything inside a constructor
Run the program from the EDT SwingUtilitites.invokeLater().
Make the button a global variable so it can be accessed from the ActionListener
setVisible should be the last thing you do, after adding all the component.
When adding multiple components the JFrame you will want to use the BorderLayout positions, or set the layout to the JFrame to something else besides BorderLayout
Override getPrefferedSize in your JPanel when painting, so the JPanel has a respected preferred size.
Don't set the size of the JFrame just call pack();
Here is a refactor of your code
Also see Creating GUI with Swing | Graphics2D | Performing Custom Paintin for further details.
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class GUI2 {
JButton button = new JButton("YO");
public GUI2() {
button.addActionListener(new Action());
JFrame frame = new JFrame("Game");
frame.add(new DrawPanel(), BorderLayout.CENTER);
frame.add(button, BorderLayout.SOUTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private class DrawPanel extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawRect(250, 250, 200, 100);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new GUI2();
}
});
}
static class Action implements ActionListener {
public void actionPerformed(ActionEvent e) {
}
}
}
I can tell you why you'll get a NullPointerException
You've not overridden any paint method of any displayable component
You've not passed a valid Graphics context to you paint method, but I would discourage this any way
You should make use of the #Override annotation which would have prevented the class from compiling. Use it when you think you're overriding a method of a parent class, it will tell you when you're wrong
Start by taking a look at Performing Custom Painting and then take a look at Painting in AWT and Swing for more details about how painting is actually done in Swing
Here is working example of your code:
package test;
import javax.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.image.ImageObserver;
import java.awt.*;
import java.text.AttributedCharacterIterator;
public class GUI2 extends JPanel{
JButton button;
JFrame frame;
public GUI2() {
button = new JButton("YO");
//panel = new JPanel();
frame = new JFrame();
//panel = new JPanel();
this.add(button);
frame.add(this);
//button.addActionListener(new Action());
// this.paint(null);
frame.setSize(500, 500);
frame.setLocation(500, 200);
frame.setVisible(true);
}
public void paint(Graphics g) {
g.setColor(Color.red);
g.drawRect(250, 250, 200, 100);
}
public static void main(String[] args) {
GUI2 test = new GUI2();
}
}
I have removed some statements but you can add them later
In my application I need to draw grid lines just like those like Photoshop has - e.g, an user can drag lines over the document to help him align layers. Now, the problem is that I am able to draw such lines (it's just plain simple Java2D painting using Line2D), but I am not being able to keep such lines on top of everything else, because when children components draw themselves, my grid line is erased.
The program structure is something like this: JFrame -> JPanel -> JScrollPane -> JPanel -> [many others JPanels, which are like layers]
As a test, I added the draw code to JFrame, which correctly shows my Line2D instance on top of everything else. However, when I do anything in an child component that requires that child to repaint itself, the line that was drawn in the JFrame is erased.
I understand that this is the expected Swing behavior - that is, it will only repaint those areas that have changed. However, I am looking for some approach that continuously draws line grid lines on top of everything else.
The only way I was able to get it working was to use a Swing Timer that calls repaint() on my root component every 10ms, but it consumes a lot of CPU.
UPDATE
Working code of an example is below. Please mind that in my real application I have dozens of different components that could trigger a repaint(), and none of them have a reference to the component that does the grid line drawing (of course I can pass it to everyone, but that appears to be the latest option)
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Line2D;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class GridTest extends JFrame {
public static void main(String[] args) {
new GridTest().run();
}
private void run() {
setLayout(null);
setPreferredSize(new Dimension(200, 200));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JPanel p = new JPanel();
p.setBounds(20, 20, 100, 100);
p.setBackground(Color.white);
add(p);
JButton b = new JButton("Refresh");
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// When I call repaint() here, the paint() method of
// JFrame it's not called, thus resulting in part of the
// red line to be erased / overridden.
// In my real application application, I don't have
// easy access to the component that draws the lines
p.repaint();
}
});
b.setBounds(0, 150, 100, 30);
add(b);
pack();
setVisible(true);
}
#Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D gg = (Graphics2D)g.create();
Line2D line = new Line2D.Double(0, 50, getWidth(), 50);
gg.setStroke(new BasicStroke(3));
gg.setColor(Color.red);
gg.draw(line);
gg.dispose();
}
}
if you want to paint over JComponents placed to the JScrollPane then you can paint to the JViewPort, example here
EDIT:
1) beacuse your code painted to the wrong Container, to the JFrame, sure is possible to paint to the JFrame, but you have to extract RootPane or GlassPane
2) you have to learn how to LayoutManagers works, I let your code with original Sizing, not nice and very bad
3) paint to the GlassPane or JViewPort
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Line2D;
import javax.swing.*;
public class GridTest extends JFrame {
private static final long serialVersionUID = 1L;
public static void main(String[] args) {
new GridTest().run();
}
private void run() {
setLayout(null);
setPreferredSize(new Dimension(200, 200));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JPanel p = new JPanel() {
private static final long serialVersionUID = 1L;
#Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D gg = (Graphics2D) g.create();
Line2D line = new Line2D.Double(0, 50, getWidth(), 50);
gg.setStroke(new BasicStroke(3));
gg.setColor(Color.red);
gg.draw(line);
//gg.dispose();
}
};
p.setBounds(20, 20, 100, 100);
p.setBackground(Color.white);
add(p);
JButton b = new JButton("Refresh");
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
p.repaint();
}
});
b.setBounds(0, 150, 100, 30);
add(b);
pack();
setVisible(true);
}
}
EDIT: 2, if you expecting single line, on the fixed Bounds
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Line2D;
import javax.swing.*;
import javax.swing.border.LineBorder;
public class GridTest extends JFrame {
private static final long serialVersionUID = 1L;
public static void main(String[] args) {
new GridTest().run();
}
private void run() {
setPreferredSize(new Dimension(200, 200));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JPanel p = new JPanel() {
private static final long serialVersionUID = 1L;
#Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D gg = (Graphics2D) g.create();
Line2D line = new Line2D.Double(0, 50, getWidth(), 50);
gg.setStroke(new BasicStroke(3));
gg.setColor(Color.red);
gg.draw(line);
gg.dispose();
}
};
JPanel p1 = new JPanel();
p1.setBorder(new LineBorder(Color.black,1));
JPanel p2 = new JPanel();
p2.setBorder(new LineBorder(Color.black,1));
JPanel p3 = new JPanel();
p3.setBorder(new LineBorder(Color.black,1));
p.setLayout(new GridLayout(3,0));
p.add(p1);
p.add(p2);
p.add(p3);
p.setBounds(20, 20, 100, 100);
p.setBackground(Color.white);
add(p, BorderLayout.CENTER);
JButton b = new JButton("Refresh");
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
p.repaint();
}
});
add(b, BorderLayout.SOUTH);
pack();
setVisible(true);
}
}
One possible solution is to override the JPanel's repaint method so that it calls the contentPane's repaint method instead. Another point is that you probably shouldn't draw grid lines directly in the JFrame but rather in its contentPane. Counter to what I usually recommend, I think you're better off overriding the either the contentPane's paint method (or that of some other containing JPanel), not its paintComponent method so that it can call after the children have been painted. For example:
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Stroke;
import java.awt.event.ActionEvent;
import javax.swing.*;
#SuppressWarnings("serial")
public class GridTest2 extends JPanel {
private static final Stroke LINE_STROKE = new BasicStroke(3f);
private boolean drawInPaintComponent = false;
public GridTest2() {
final JPanel panel = new JPanel() {
#Override
public void repaint() {
JRootPane rootPane = SwingUtilities.getRootPane(this);
if (rootPane != null) {
JPanel contentPane = (JPanel) rootPane.getContentPane();
contentPane.repaint();
}
}
};
panel.setBackground(Color.white);
panel.setPreferredSize(new Dimension(100, 100));
JPanel biggerPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
biggerPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 0, 0));
biggerPanel.setOpaque(false);
biggerPanel.add(panel);
JButton resetButton = new JButton(new AbstractAction("Reset") {
public void actionPerformed(ActionEvent arg0) {
panel.repaint();
}
});
JPanel btnPanel = new JPanel();
btnPanel.add(resetButton);
setLayout(new BorderLayout());
add(biggerPanel, BorderLayout.CENTER);
add(btnPanel, BorderLayout.SOUTH);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (drawInPaintComponent ) {
drawRedLine(g);
}
}
#Override
public void paint(Graphics g) {
super.paint(g);
if (!drawInPaintComponent ) {
drawRedLine(g);
}
}
private void drawRedLine(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setStroke(LINE_STROKE);
g2.setColor(Color.red);
g2.drawLine(0, 50, getWidth(), 50);
}
private static void createAndShowGui() {
GridTest2 mainPanel = new GridTest2();
JFrame frame = new JFrame("GridTest2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
I know it's an old post, but I've had the same problem recently...
You should override paintChildren instead of paint or paintComponent.
From the JComponent.paint documentation:
Invoked by Swing to draw components. Applications should not invoke paint directly, but should instead use the repaint method to schedule the component for redrawing.
This method actually delegates the work of painting to three protected methods: paintComponent, paintBorder, and paintChildren. They're called in the order listed to ensure that children appear on top of component itself. Generally speaking, the component and its children should not paint in the insets area allocated to the border. Subclasses can just override this method, as always. A subclass that just wants to specialize the UI (look and feel) delegate's paint method should just override paintComponent.
So if you
#Override
protected void paintChildren(Graphics g){
super.paintChildren(g);
paintGrid(g);
}
the grid will be on top of your children components ^^
Assuming the parent frame already has a list of all the grid lines it has to draw, what you can do is get each child frame to draw its own personal bits of the lines. In pseudocode:
gridlines = getParentsGridLines()
gridlines.offsetBasedOnRelativePosition()
drawStuff()
Swing uses a JLayeredPane within JFrames (and similar components). Using the layered pane, you can position paint-only components over your main content.
This code uses components placed within the JLayeredPane to position (and automatically repaint) arbitrary decorations above the main content of any component, thus obviating the need to override the paint() method of any given component.