Using Graphics to display shapes on a JFrame - java

I am learning how to program a graphical user interface in Java. I pretty much know some basics but in this program, I am trying to draw onto a JFrame with a black background, but as soon as I run the program the JFrame only displays a white line on a white background. I would appreciate it very much if anyone knew how to fix this, I have been trying myself but I can't seem to figure it out.
Thanks for your attention. I’m looking forward to a reply.
public class test1 {
public static void main (String[] args)
{
JFrame frame = new JFrame();
frame.setSize(1835,1019);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.setResizable(false);
frame.getContentPane().setBackground(Color.BLACK);
JPanel raum = new JPanel()
{
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
g2.setPaint(Color.WHITE);
g2.drawLine(500,500,500,800);
}
};
frame.add(raum);
}
}

There a number of issues which are going to cause you endless amount of problems going into the future.
The obvious one is the fact that the background color of the panel is very close to WHITE, so it makes it very difficult to see the line. You could change the background color of the panel or the line and it should solve the immediate issue.
You really need to take a look at Performing Custom Painting and Painting in AWT and Swing to get a better understanding of how painting works in Swing.
It is generally recommended to override paintComponent and avoid overriding paint. paint does a lot work and unless you're willing to take over ALL it's workload, you're better off avoiding it.
As a general rule, you should also call the super.paintXxx method before you do any custom painting. Again, painting is generally a complex workflow, best to just let the parent class do its job.
A component should also provide sizing hints back to the parent container, the parent container can then make better decisions (via the LayoutManager) as to how all the components should be laid out. Because different platforms (and even same platforms with different settings) can generate different size window decorations, you're better off managing the size of the "content" over the size of the "window". Again, this is going to save you no end of headaches into the future.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setBackground(Color.BLACK);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(1080, 1920);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setPaint(Color.WHITE);
g2d.drawLine(500, 500, 500, 800);
g2d.dispose();
}
}
}

Related

Add GUI with Swing, but unsure of the sequence to take

I'm building a GUI for a data processing algorithm. I can instantiate the window, give it a background, title, etc., but when I try adding panels to it, I run into trouble. What I'm really looking for more than a proofreader is a suggestion for the sequence in which to build, configure, and add objects in Java Swing so that they behave correctly, in a generic sense. So, is this the best way to build a JFrame with a different-colored panel in it?
Declare JFrame
Set JFrame color (background color)
Declare JPanel (box to represent data graphically)
Set JPanel color (box color)
Add JPanel to JFrame
Set JFrame to visible = true
It makes sense intuitively but it doesn't seem to work, no matter what I do. I've found step-by-step instructions elsewhere but they tend to explain what to type more than why you're typing it, so you get a very narrow understanding of what's going on. Thanks for any help!
Below is the full code; I hesitated to post it because I'd begun experimenting with Graphics2D and it isn't well-commented, but if it helps:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import javax.swing.*;
import javax.swing.border.TitledBorder;
public class GUI extends JFrame
{
JFrame mainWindow = new JFrame();
JPanel backgroundPanel = new JPanel();
JPanel subPanel = new JPanel();
Color background = new Color(40,40,40);
Color subWindow = new Color(255, 255, 255);
TitledBorder title = BorderFactory.createTitledBorder("title");
Rectangle rect1 = new Rectangle(10, 10, 40, 40);
Graphics2D g;
public static void main (String[] args)
{
new GUI();
}
public GUI()
{
initializeGUI();
}
private void initializeGUI()
{
mainWindow.setSize(1340, 880);
backgroundPanel.setBackground(background);
subPanel.setBackground(subWindow);
subPanel.setBorder(title);
mainWindow.setTitle("Ed");
mainWindow.setLocationRelativeTo(null);
mainWindow.setDefaultCloseOperation(EXIT_ON_CLOSE);
mainWindow.add(backgroundPanel);
backgroundPanel.add(subPanel);
updateGUI();
}
public void updateGUI()
{
mainWindow.setVisible(false);
mainWindow.setVisible(true);
}
public void paintComponent(Graphics g)
{
this.g.setColor(subWindow);
this.g.fill(rect1);
this.g = (Graphics2D) g;
}
}
Let's break this down....
public class GUI extends JFrame {
JFrame mainWindow = new JFrame();
There is no need to extend from JFrame as you are neither using it nor are you adding any value to the class.
This...
public void paintComponent(Graphics g) {
this.g.setColor(subWindow);
this.g.fill(rect1);
this.g = (Graphics2D) g;
}
is doing nothing and will never be called, as nothing you've extended from implements a paintComponent method (that is, JFrame does not have a paintComponent methd) (and you class is not attached to anything displayed on the screen anyway). Also, you should NEVER maintain a reference to ANY Graphics context you did not create yourself.
The reason that subPanel is appearing so "small" is because it has not definable size, aside from the border.
You could rectify this in one of three ways...
You could change the layout manager of backgroundPanel to something like BorderLayout
You could override the getPreferredSize method of the subPanel to return a more suitable size or
You could add other components to it and let the layout manager figure it out...
In any case, you should have a look at Laying Out Components Within a Container.
You should also have a look at Painting in AWT and Swing and Performing Custom Painting for more details about how painting is done in Swing

Java Canvas Won't Paint

I want my program to display the canvas that is repainted once at the start and then whenever a change is made afterwards. I thought I had everything coded correctly, but for some reason nothing that is painted onto the canvas actually shows (I know it's repainting, I tested that).
Here are the code segments:
public TileMapCreator()
{
currentView = new BufferedImage(640, 640, BufferedImage.TYPE_INT_ARGB);
currentView.getGraphics().setFont(new Font("Arial", Font.BOLD, 100));
currentView.getGraphics().drawString("No Map Yet Open", currentView.getWidth()/2, currentView.getHeight()/2);
this.setJMenuBar(createMenuBar());
this.setContentPane(createMapPanel());
}
private JPanel createMapPanel()
{
JPanel p = new JPanel();
p.add(setUpCanvas());
p.setVisible(true);
return p;
}
private Canvas setUpCanvas()
{
mapCanvas = new Canvas(){
private static final long serialVersionUID = 1L;
public void repaint()
{
mapCanvas.getGraphics().drawImage(currentView, 0, 0, this);
}
};
mapCanvas.setIgnoreRepaint(true);
Dimension size = new Dimension(currentView.getWidth(), currentView.getHeight());
mapCanvas.setSize(size);
mapCanvas.setPreferredSize(size);
mapCanvas.setMaximumSize(size);
mapCanvas.setMinimumSize(size);
mapCanvas.setFocusable(true);
mapCanvas.addMouseListener(this);
mapCanvas.addMouseMotionListener(this);
mapCanvas.setVisible(true);
return mapCanvas;
}
Currently the area where the canvas should be painting is just the regular grey color of the Java GUI. Thanks for your help!
You appear to be mixing Swing with AWT components, and drawing in a very strange way, one that I've honestly never seen before (and I've seen a lot). Why not simply do your drawings in the paintComponent(Graphics g) method of a JPanel using the Graphics object given by the JVM, like you'll find in the Swing graphics tutorials and 98% of the Swing graphics answers on this site? Also for my money, I'd avoid using Canvas or trying to mix heavy and light weight components together. Stick with Swing all the way, and things should go more smoothly.
I'd be happy to give you more specific advice and perhaps some if you could create and post a minimal example program. Please have a look at the link and let us know if you need more information.
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class TestImagePanel extends JPanel {
private static final int BI_WIDTH = 640;
BufferedImage currentView = new BufferedImage(BI_WIDTH, BI_WIDTH, BufferedImage.TYPE_INT_ARGB);
public TestImagePanel() {
Graphics g = currentView.getGraphics();
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.setColor(Color.black);
g.setFont(new Font("Arial", Font.BOLD, 60));
g.drawString("No Map Yet Open", 20, currentView.getHeight()/2);
g.dispose();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (currentView != null) {
g.drawImage(currentView, 0, 0, this);
}
}
#Override
public Dimension getPreferredSize() {
if (currentView != null) {
return new Dimension(BI_WIDTH, BI_WIDTH);
}
return super.getPreferredSize();
}
private static void createAndShowGui() {
TestImagePanel mainPanel = new TestImagePanel();
JFrame frame = new JFrame("TestImagePanel");
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've found the Swing tutorials to be a great asset, and bet you would too. Please have a look at them.
Edit
You ask:
Hmm, so apparently getting the graphics object of my buffered image twice did not result in anything actually being painted... I had to get the graphics object, assign it to a variable, and then paint.
I wouldn't say that. I'd say that your way of getting the Graphics object should work the same as mine, but yours is not safe since the Graphics objects obtained in this way cannot be disposed of, and you risk running out of resources. I think that your image didn't show up due to your very convoluted and unusual way of trying to display your image. You override repaint(), put some weird code inside of it, tell the JVM to ignore repaint calls, never call the repaint super method inside of your override, so that repaint does in fact nothing. I have to wonder -- where did you get this code? Was it from a tutorial? If so, please share the link here. And never get advice from that site again.

Graphics dont appear

Whatever I do, I can not display rectangle/line/oval on the screen. I checked other sources where they paint graphics, but when I even execute those codes, I don't get any graphics displayed on the windows. Below is the example from the text book.
import java.awt.*;
import javax.swing.*;
class PlotGraph
{
public static void main (String [] args) {
JFrame win;
Container contentPane;
Graphics g;
win = new JFrame("testing");
win.setSize(300,200);
win.setLocation(100,100);
win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
win.setVisible(true);
contentPane = win.getContentPane();
g = contentPane.getGraphics();
g.drawRect(10, 30, 50, 50);
}
}
Ouch. You should change your text book then. First of all, all the accesses to Swing components must be done in the event dispatch thread.
Second, you should not get the graphics of a component and paint on it. Instead, you should extend a JComponent or JPanel, override its paintComponent(Graphics) method, and paint using the Graphics object passed as argument (and which is in fact a Graphics2D instance).
That's not how graphics work in Swing.
You need to add a component to your frame, not just draw on it. You never want to draw directly on the frame. The reason why it's not doing anything is because your drawing code is being overridden.
If you want your component to have custom drawing code, make a subclass of JComponent and override the paintComponent(Graphics) method. An example of how you should do this is as follows:
import java.awt.*;
import javax.swing.*;
class PlotGraph {
public static void main(String[] args) {
JFrame win;
win = new JFrame("testing");
win.setSize(300, 200);
win.setLocation(100, 100);
win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
win.setVisible(true);
win.setContentPane(new MyComponent());
}
}
class MyComponent extends JComponent {
#Override
public void paintComponent(Graphics g) {
g.drawRect(10, 30, 50, 50);
}
}
I would highly encourage you to check out the Java GUI tutorial online.

Can not draw oval on a JPanel

I have a JFrame created with GUI builder of Netbeans, which contains a JPanel only. I have created a method getPanel for getting a reference to this JPanel:
public class ShowDrawings extends JFrame {
public ShowDrawings() {
initComponents();
}
public JPanel getPanel(){
return panel;
}
private JPanel panel;
}
In my main function I am doing:
public class Main {
public static void main(String[] args){
ShowDrawings sd = new ShowDrawings();
sd.setSize(800, 600);
Graphics g = sd.getPanel().getGraphics();
g.setColor(Color.BLACK);
g.drawOval(400, 300, 50, 50);
sd.getPanel().paint(g);
sd.repaint();
sd.setVisible(true);
}
}
But it does not draw anything. Please help me.
I have looked some related questions but they are all suggesting extending JPanel and overriding its paint method. But I did not want to do that way.
Thanks.
I have looked some related questions but they are all suggesting
extending JPanel and overriding its paint method. But I did not want
to do that way
You should not override JPanel paint() method, rather paintComponent(..). This is best practice and should be done if you want code that will not produce anomalies. Also doing it in your current approach (as you have seen) makes creating persistent drawings a lot harder as they are wiped away on repaint()
Rather extend JPanel and override paintComponent(Graphics g) not forgetting to call super.paintComponent(g) as first call in overridden paintComponent(..) method. Also dont forget to override getPreferredSize() of JPanel so that we can return correct dimensions and pack() may be called on JFrame (+1 to #mKorbels comment):
Here is some example code:
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() {
initComponents();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Test();
}
});
}
private void initComponents() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel testPanel = new JPanel() {
#Override
protected void paintComponent(Graphics grphcs) {
super.paintComponent(grphcs);
Graphics2D g2d = (Graphics2D) grphcs;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.GREEN);
//g2d.drawOval(10,10,100,100);//I like fill :P
g2d.fillOval(10,10,100,100);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(150, 150);
}
};
frame.add(testPanel);
frame.pack();
frame.setVisible(true);
}
}
The first time you repaint() your ShowDrawings sd frame anything you've painted like this (sd.getPanel().getGraphics().drawOval(...)) would be erased by the original JPanel#paintComponent() method.
As Andrew Thompson has written:
Do not use Component.getGraphics(). Instead, subclass and override the paint() (AWT), or paintComponent() (Swing) method.
Component.getGraphics() simply can't work. Java uses a callback mechanism for drawing graphics. You are not supposed to "push" graphics information into a component using getGraphics(). Instead you are supposed to wait until Java calls your paint()/paintComponent() method. At that moment you are supposed to provide the Component with the drawings you would like to do.
If you're just checking/debugging something you could even do something like this:
class Test {
private JPanel panel = new JPanel() {
public void paintComponent(Graphics g) {
g.setColor(Color.BLACK);
g.drawOval(400, 300, 50, 50);
}
};
}

Java repaint method does not work when called from another class

I have been coding up Java with Netbeans for about a year now, and have written a lot of data manipulation code which plots graphs on-screen. I generally plant a JPanel object in my main window, write custom painting code, and call the repaint() method as needed.
But today, for the first time, I tried to invoke a repaint on a panel from a class (object) other than the one that contained the panel. Although the compiler found nothing wrong with this, and in debugging mode, it single-stepped properly to the exterior call to the repaint, no repaint actually occurred, and the code did not actually step into the repaint method.
I wrote a minimalist program to demonstrate the problem, as shown below (Main is ommitted, since it only contains code to set up the two on-screen panels.)
--- Description of classes, first contains the drawing surface, other the repaint call ---
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Panel1 extends JComponent
{
GraphPnl graphPnl;
boolean colorFlag;
public Panel1()
{
setLayout(null);
colorFlag = true;
graphPnl = new GraphPnl();
graphPnl.setBounds(10, 10, 110, 110);
graphPnl.setBackground(Color.black);
add(graphPnl);
}//Panel1()
public class GraphPnl extends JPanel
{
//just draws a line segment, toggling color
#Override
public void paint(Graphics g)
{
super.paint(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
if (colorFlag) {g2.setColor(Color.red);} else {g2.setColor(Color.green);}
g2.drawLine(10, 10, 50, 50);
}//paint
}//GraphPnl
}//Panel1
import javax.swing.*;
import java.awt.event.*;
public class Panel2 extends JComponent
{
JButton testBtn;
TestAction testAction;
Panel1 p1;
public Panel2()
{
p1 = new Panel1();
testBtn = new JButton("Click");
testBtn.setBounds(10, 10, 80, 30);
add(testBtn);
testAction = new TestAction();
testBtn.addActionListener(testAction);
}//Panel2()
public class TestAction implements ActionListener
{
public void actionPerformed(ActionEvent evt)
{
p1.colorFlag = ! p1.colorFlag;
p1.graphPnl.repaint();
}
}//TestAction
}//Panel2
If anyone has any insights into this, or knows of a workaround, I'd be very happy to hear
from you.
Thanks in advance for any insights.
John Doner
Main is ommitted, since it only contains code to set up the two on-screen panels.)
Well, by definition when you have a problem you don't know what code is or isn't relative until the problem is solved. So a complete SSCCE should be posted.
As a wild guess I would say your component has a size of 0 so there is nothing to paint.
I generally plant a JPanel object in my main window, write custom painting code, and call the repaint() method as needed
You probably got lucky because you added the panel to the center of a BorderLayout which automatically gives the panel all the space available to the frame.
trashgod's example shows one way to set the preferred size of a custom component. Another way is to override the getPreferredSize() method to return the proper value.
You really should learn how to use layout manager rather than using null layouts and you will avoid problems like this in the future. There is no need to use a null layout unless you have a drag/drop type of application.
"Swing programs should override paintComponent() instead of overriding paint()."—Painting in AWT and Swing: The Paint Methods.
main is ommitted, since it only contains code to set up the two on-screen panels.
Verify that you construct your GUI on the EDT, as shown in the article Initial Threads.
Addendum: Here's an example showing both principles:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
/** #see http://stackoverflow.com/questions/4282159 */
public class GraphPanel extends JPanel {
private boolean colorFlag;
public GraphPanel() {
this.setPreferredSize(new Dimension(640, 480));
}
public void toggle() {
colorFlag = !colorFlag;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
if (colorFlag) {
g2.setColor(Color.red);
} else {
g2.setColor(Color.blue);
}
g2.drawLine(0, 0, getWidth(), getHeight());
}
private void display() {
JFrame f = new JFrame("GraphPanel");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this, BorderLayout.CENTER);
f.add(new ControlPanel(this), BorderLayout.SOUTH);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new GraphPanel().display();
}
});
}
}
class ControlPanel extends JPanel {
public ControlPanel(final GraphPanel gp) {
this.add(new JButton(new AbstractAction("Click") {
#Override
public void actionPerformed(ActionEvent e) {
gp.toggle();
gp.repaint();
}
}));
}
}
Addendum: As noted in #camickr's comment, A Visual Guide to Layout Managers may help guide your layout selection.
I believe that when you are painting a JComponent, the clip region is set to that JComponent. So if other components try to paint (or if you call their repaint), they won't, because of the clipping.

Categories

Resources