Creating and overriding a shape to fit into JPanel? - java

In this program a polygon is created to show up into a JPanel tab.
In-order to make it show I had to over-ride the shape and create a setter method for it. Unfortunately it is not showing and the program is not running either.
The error:
Exception in thread "main" java.lang.IllegalArgumentException: adding
a window to a container
at SelectShape component1 = new SelectShape(x, y, vert); in method
Page1.
The only way it would work is by making a frame and removing the JTab and assigning the shape onto the frame but that is not what I want to make. I want to make a program that can distribute shapes to * different tabs* using one graphics method.
Here is the code:
import java.awt.*;
import java.io.IOException;
import javax.swing.*;
/* This program create a graphics component that draws a polygon
*/
public class SelectShape extends JFrame
{
private JTabbedPane tabbedPane;
private JPanel panel1;
// //////////////////////////
static int[] x = { 20, 40, 50, 65, 80, 95 }; // Co-ords for a polygon
static int[] y = { 60, 105, 105, 110, 95, 95 };
static int vert = 6;
public SelectShape() throws IOException // Builds GUI
{
setTitle("Program");
setSize(900, 600);
setBackground(Color.gray);
JPanel topPanel = new JPanel();
topPanel.setLayout(new BorderLayout());
getContentPane().add(topPanel);
// Create the tab pages
createPage1();
// Create a tabbed pane
tabbedPane = new JTabbedPane();
tabbedPane.addTab("Shape Panel", panel1);
}
public void createPage1() throws IOException // Creates JPanel
{
panel1 = new JPanel();
panel1.setLayout(null);
SelectShape component1 = new SelectShape(x, y, vert); //error
SelectShape component2 = new SelectShape(x, y, vert); //over-rides shape
component1.setBounds(290, 70, 120, 40);
component2.setBounds(290, 70, 120, 40);
panel1.add(component1); // is not displayed!
panel1.add(component2); // component2 overwrites component1!!!
panel1.setVisible(true);
}
// overrides javax.swing.JComponent.paintComponent
public void paintComponent(Graphics g) {
// Recover Graphics2D
Graphics2D g2 = (Graphics2D) g;
// Construct a polygon then draw it
Polygon polygon = new Polygon(x, y, vert);
g2.draw(polygon);
g2.fill(polygon);
}
public SelectShape(int[] x, int y[], int vert) { // setter method
this.x = x;
this.y = y;
this.vert = vert;
}
public static void main(String[] args) throws IOException {
SelectShape mainFrame = new SelectShape(); //Frame
mainFrame.setVisible(true);
}
}

I think you are mixing many concepts together in your code which eventually leads to ununderstandable code.
JFrame does not extends JComponent and does not have a paintComponent method. Consider using the #Override annotation on methods that override another. This will allow you to easily mistakes like this.
No need to extend JFrame anyway and never override the paint() method of a Top-level container (JDialog, JFrame, ...)
Always invoke the super method of paintXXX methods
public SelectShape(int[] x, int y[], int vert) { // setter method is not a setter method. It is a constructor that takes 3 arguments and assign them. In all cases, this does absolutely nothing in your case because you made those variables static. Avoid the use of static unless if you describe constants, in which case it should be also followed by the final keyword.
Start the UI, and perform all modifications to the UI, on the Event Dispatching Thread (EDT). This can easily be done by using SwingUtilities.invokeLater().
The error you are seeing: Exception in thread "main" java.lang.IllegalArgumentException: adding a window to a container is thrown because you are trying to add a JFrame to a JComponent which is forbidden. JFrame cannot be added to anything. If you want to do that, you need to use JDesktopPane and add JInternalFrame (but that is another story).
I am not too sure as to what you are trying to achieve, but here is a working code derived from yours which works much better:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.Ellipse2D;
import java.io.IOException;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
/* This program create a graphics component that draws a polygon
*/
public class SelectShape extends JPanel {
// Constants
private static final int[] x = { 20, 40, 50, 65, 80, 95 }; // Co-ords for a polygon
private static final int[] y = { 60, 105, 105, 110, 95, 95 };
private static final Polygon POLYGON = new Polygon(x, y, Math.min(x.length, y.length));
private static final Ellipse2D CIRCLE = new Ellipse2D.Double(100, 40, 45, 45);
// Class variables
private final Shape shape;
private Dimension preferredSize;
public SelectShape(Shape shape) {
this.shape = shape;
Rectangle bounds = shape.getBounds();
this.preferredSize = new Dimension(bounds.x + bounds.width, bounds.y + bounds.height);
}
#Override
public Dimension getPreferredSize() {
return preferredSize;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g.setColor(Color.BLUE);
g2.draw(shape);
g2.fill(shape);
}
public static void main(String[] args) throws IOException {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame mainFrame = new JFrame("Program");
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
SelectShape polygon = new SelectShape(POLYGON);
SelectShape circle = new SelectShape(CIRCLE);
// Create a tabbed pane
JTabbedPane tabbedPane = new JTabbedPane();
tabbedPane.addTab("Polygon", polygon);
tabbedPane.addTab("Circle", circle);
mainFrame.add(tabbedPane);
mainFrame.pack();
mainFrame.setVisible(true);
}
});
}
}

Related

Add new data to Graphics2D panel in a Java Swing application

I am writing a Java Swing application for data processing.
one of the functions I need to add is to visualize the data in a graphical way.
For this I want to use the Graphics2D class.
I have a GUI created, integrated my program and also a panel that draws the graphics using the Graphics2D class.
But my problem is that I can't figure out how to call the drawLine method after selecting and loading a file from the GUI
Below is short code example, showing my issue.
It just contains a basic GUI with 2 panels and a menu with load option to explain my problem:
In de MyFrame.java file, I put a comment at line 87 to show exactly where I am stuck.
The appl is based on 3 files:
main: here it creates an instance of MyFrame of the GUI
Myframe: creates the GUI and further process of data
MyPanel: makes a Jpanel of the Graphics2D with a base blue rectangle frame as start view.
If Anyone could give me a hint on how to call this drawLine method from outside the MyFrame() constructor...
I still don't fully understand the whole point on how to interact between classes...
here is a picture of what the GUI looks:
Thank you for helping me on this
public class main {
public static void main(String[] args) {
new MyFrame();
}
}
MyFrame.java:
public class MyFrame extends JFrame {
JTextComponent tc;
String fileName;
MyFrame() {
this.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
this.setLayout(null);
this.setBounds(0,0,464,312);
tc = new JTextPane();
tc.setBounds(0,520,450,50);
tc.setPreferredSize(new Dimension(450,50));
JScrollPane sp = new JScrollPane(tc);
JMenuBar mb = new JMenuBar();
JMenu fm = new JMenu("File");
JMenuItem loadItem = new JMenuItem("Load file");
loadItem.addActionListener(e -> {tc.setText("loading"+"\n");
SDprocess();});
fm.add(loadItem);
mb.add(fm);
MyPanel p1 = new MyPanel();
p1.setBounds(0,0,450,200);
p1.setPreferredSize(new Dimension(450,200));
JPanel p2 = new JPanel();
p2.setBounds(0,200,450,50);
p2.setPreferredSize(new Dimension(450,50));
p2.add(sp);
this.setJMenuBar(mb);
this.add(p1);
this.add(p2);
this.setResizable(false);
this.setVisible(true);
}
public void SDprocess() {
File fr = null;
JFileChooser fc = new JFileChooser();
int result = fc.showOpenDialog(this);
if (result == JFileChooser.APPROVE_OPTION) {
fr = fc.getSelectedFile();
fileName=fr.getName();
System.out.println(fileName);
}
try {
Scanner sc = new Scanner(fr);
tc.setText(fileName +" loading\n");
while (sc.hasNextLine()) {
String line = sc.nextLine();
// ...
// rest of code to get the x and y data for drawing
// lines using drawLine(x1,y1,x2,y2) method.
//
// at this point I need to call this drawLine method but how ???
// i just don't know how to call this method from this point and how to
// and update the graphics panel p1 after adding the data....
}
sc.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
MyPanel.java:
public class MyPanel extends JPanel {
Graphics2D g2D;
MyPanel() {
this.setPreferredSize(new Dimension (450,200));
}
public void paint (Graphics g) {
g2D = (Graphics2D) g;
g2D.setStroke(new BasicStroke(1));
g2D.setPaint(Color.blue);
g2D.drawLine(5, 5, 445,5);
g2D.drawLine(445, 5, 445,195);
g2D.drawLine(445, 195, 5,195);
g2D.drawLine(5, 195, 5,5);
}
}
Oracle has a helpful tutorial, Creating a GUI With Swing. Skip the Netbeans section.
Here's the revised GUI before "reading the file".
Here's the revised GUI after "reading the file".
I created an application model to hold the line segments. This model is passed to the drawing JPanel so that the line segments can be drawn in the paintComponent method of the drawing JPanel.
I cleaned up your GUI. I used Swing layout managers to create the GUI. I separated the creation of the JPanels from the creation of the JFrame so the code is easier for people to read and understand.
Here's the complete runnable code. I made the additional classes inner classes so I could post this code as one block.
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import javax.swing.text.JTextComponent;
public class ExampleDrawingGUI {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new ExampleDrawingGUI().new MyFrame());
}
public class MyFrame extends JFrame {
private static final long serialVersionUID = 1L;
private ExampleDrawingModel model;
JTextComponent tc;
MyPanel p1;
public MyFrame() {
super("My Frame");
this.model = new ExampleDrawingModel();
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setJMenuBar(createMenuBar());
p1 = new MyPanel(model);
this.add(p1, BorderLayout.CENTER);
this.add(createTextPanel(), BorderLayout.SOUTH);
this.pack();
this.setLocationByPlatform(true);
// this.setResizable(false);
this.setVisible(true);
}
private JMenuBar createMenuBar() {
JMenuBar mb = new JMenuBar();
JMenu fm = new JMenu("File");
JMenuItem loadItem = new JMenuItem("Load file");
loadItem.addActionListener(e -> {
tc.setText("loading" + "\n");
model.readFile();
p1.repaint();
});
fm.add(loadItem);
mb.add(fm);
return mb;
}
private JPanel createTextPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
tc = new JTextPane();
tc.setPreferredSize(new Dimension(450, 50));
JScrollPane sp = new JScrollPane(tc);
panel.add(sp, BorderLayout.CENTER);
return panel;
}
public void repaint() {
p1.repaint();
}
}
public class MyPanel extends JPanel {
private static final long serialVersionUID = 1L;
private ExampleDrawingModel model;
public MyPanel(ExampleDrawingModel model) {
this.model = model;
this.setPreferredSize(new Dimension(450, 200));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2D = (Graphics2D) g;
paintBorder(g2D);
for (LineSegment line : model.getLines()) {
Point startPoint = line.getStartPoint();
Point endPoint = line.getEndPoint();
g2D.drawLine(startPoint.x, startPoint.y, endPoint.x, endPoint.y);
}
}
private void paintBorder(Graphics2D g2D) {
int margin = 5;
int x1 = margin;
int x2 = getWidth() - margin;
int y1 = margin;
int y2 = getHeight() - margin;
g2D.setStroke(new BasicStroke(3f));
g2D.setPaint(Color.blue);
g2D.drawLine(x1, y1, x1, y2);
g2D.drawLine(x1, y1, x2, y1);
g2D.drawLine(x2, y1, x2, y2);
g2D.drawLine(x1, y2, x2, y2);
}
}
public class ExampleDrawingModel {
private List<LineSegment> lines;
public ExampleDrawingModel() {
this.lines = new ArrayList<>();
}
public void readFile() {
this.lines.clear();
// Here's where you'd read a file and create a list of lines.
lines.add(new LineSegment(new Point(100, 100), new Point(100, 150)));
}
public List<LineSegment> getLines() {
return lines;
}
}
public class LineSegment {
private final Point startPoint, endPoint;
public LineSegment(Point startPoint, Point endPoint) {
this.startPoint = startPoint;
this.endPoint = endPoint;
}
public Point getStartPoint() {
return startPoint;
}
public Point getEndPoint() {
return endPoint;
}
}
}
First off, some problems with your code:
class MyFrame extends JFrame {
//....
MyFrame() {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLayout(null); // !! Good God, no don't do this!
this.setBounds(0, 0, 464, 312); // and don't do this
Avoid null layouts and setBounds like the plague as this makes for very inflexible GUI's that while they might look good on one platform look terrible on most other platforms or screen resolutions and that are very difficult to update and maintain. Instead you will want to study and learn the layout managers and then nest JPanels, each using its own layout manager to create pleasing and complex GUI's that look good on all OS's.
Also, don't forget to call pack() on your JFrame after adding components and before setting visible, in order for the layout managers to do their things.
And then:
class MyPanel extends JPanel {
// ...
Graphics2D g2D; //!! -- no, don't do this
If you create a Graphics or Graphics2D field, you are tempted to use it outside of a painting method, and this is a recipe for disaster since any Graphics obtained from a component is short lived and doing this risks creating a brittle graphic or throwing a NullPointerException
public void paint (Graphics g) {
g2D = (Graphics2D) g;
g2D.setStroke(new BasicStroke(1));
g2D.setPaint(Color.blue);
g2D.drawLine(5, 5, 445,5);
g2D.drawLine(445, 5, 445,195);
g2D.drawLine(445, 195, 5,195);
g2D.drawLine(5, 195, 5,5);
}
Don't override paint but rather paintComponent since this is less risky (paint has greater responsibilities that you don't want to mess with) and smoother animations if needed since paintComponent uses double buffering by default.
Also, you almost always should call the super's painting method in your own override, and so instead do:
#Override
protected void paintComponent(Graphics g) {
// first call the super's method:
super.paintComponent(g);
Graphics2D g2D = (Graphics2D) g.create();
Now as for your actual problem, creating images and drawing after the GUI has been rendered, probably the easiest way to do this is to create a BufferedImage and draw with it in your GUI. You can do this easily by calling Graphics method drawImage(...). And you can pass a BufferedImage into your drawing JPanel any time it is needed. So for instance, your code could look something like...
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class Foo {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MainPanel mainPanel = new MainPanel();
MyMenu myMenu = new MyMenu();
myMenu.setMyPanel(mainPanel.getMyPanel());
myMenu.setMainPanel(mainPanel);
frame.add(mainPanel);
frame.setJMenuBar(myMenu);
frame.pack();
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
class MainPanel extends JPanel {
private static final int GAP = 5;
private JTextArea textArea = new JTextArea(4, 40);
private MyPanel myPanel = new MyPanel();
public MainPanel() {
textArea.setFocusable(false);
JScrollPane scrollPane = new JScrollPane(textArea);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
setLayout(new BorderLayout(GAP, GAP));
add(myPanel);
add(scrollPane, BorderLayout.PAGE_END);
}
public MyPanel getMyPanel() {
return myPanel;
}
public void appendTextAreaText(String text) {
textArea.append(text);
}
public void setBuffImg(BufferedImage bImage) {
myPanel.setBuffImg(bImage);
}
}
class MyMenu extends JMenuBar {
private MainPanel mainPanel;
private MyPanel myPanel;
public MyMenu() {
JMenu fm = new JMenu("File");
JMenuItem loadItem = new JMenuItem("Load file");
loadItem.addActionListener(e -> {
// Emulate reading file here in a background thread
if (myPanel != null) {
int width = MyPanel.MY_WIDTH;
int height = MyPanel.MY_HEIGHT;
BufferedImage bImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = bImg.createGraphics();
// draw with g2 here using data from file
// emulating this:
g2.setColor(Color.RED);
float strokeWidth = (float) (2 + 6 * Math.random());
g2.setStroke(new BasicStroke(strokeWidth));
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int x2 = (int) (MyPanel.MY_WIDTH * (1 + Math.random()) / 2);
int y2 = (int) (MyPanel.MY_HEIGHT * (1 + Math.random()) / 2);
g2.drawLine(5, 5, x2, y2);
g2.dispose();
myPanel.setBuffImg(bImg);
if (mainPanel != null) {
mainPanel.appendTextAreaText("adding image \n");
}
}
});
fm.add(loadItem);
add(fm);
}
public void setMyPanel(MyPanel myPanel) {
this.myPanel = myPanel;
}
public void setMainPanel(MainPanel mainPanel) {
this.mainPanel = mainPanel;
}
}
class MyPanel extends JPanel {
private static final int GAP = 5;
public static final int MY_WIDTH = 450;
public static final int MY_HEIGHT = 200;
private BufferedImage bImg = null;
// Graphics2D g2D; //!! -- no, never do this!!
MyPanel() {
// this.setPreferredSize(new Dimension(450, 200));
setBackground(Color.WHITE);
}
// better to override getPreferredSize
#Override
public Dimension getPreferredSize() {
return new Dimension(MY_WIDTH + 2 * GAP, MY_HEIGHT + 2 * GAP);
}
public void setBuffImg(BufferedImage bImg) {
this.bImg = bImg;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
// first call the super's method:
super.paintComponent(g);
if (bImg != null) {
g.drawImage(bImg, GAP, GAP, null);
}
Graphics2D g2D = (Graphics2D) g.create();
g2D.setStroke(new BasicStroke(1));
g2D.setPaint(Color.blue);
Rectangle rect = new Rectangle(GAP, GAP, getWidth() - 2 * GAP, getHeight() - 2 * GAP);
g2D.draw(rect);
}
// Don't override paint but rather paintComponent
// public void paint (Graphics g) {
}

JFrame program and running another class in Java

So currently I want to use a jframe program that would run another class in a window.
A working example:
Jframe:
import javax.swing.JFrame;
public class FaceJFrame
{
public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.setSize(300,400);
frame.setTitle("A Rectangle Object in a JFrame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Face aRectangle = new Face();
frame.add(aRectangle);
frame.setVisible(true);
}
}
The program:
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.geom.Line2D;
import javax.swing.JComponent;
public class Face extends JComponent
{
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
// Construct face and eyes
Rectangle face = new Rectangle(0, 0, 50, 50);
Rectangle eye = new Rectangle(5, 5, 15, 15);
Rectangle eye2 = new Rectangle(25, 5, 15, 15);
//make some lines for the mouth
Line2D mouth1 = new Line2D.Double(15, 30, 15, 35);
Line2D mouth2 = new Line2D.Double(15, 35, 35, 35);
Line2D mouth3 = new Line2D.Double(35, 35, 35, 30);
// draw the rectangle
g2.draw(face);
g2.draw(eye);
g2.draw(eye2);
g2.draw(mouth1);
g2.draw(mouth2);
g2.draw(mouth3);
}
}
With these two code snippets, running the Jframe one in cmd would give a pop-up window with a little face on it.
I want to do the same with the following code:
Jframe:
import javax.swing.JFrame;
public class Car_JFrame
{
public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.setSize(300,400);
frame.setTitle("a car");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Car aRectangle = new Car();
frame.add(aRectangle);
frame.setVisible(true);
}
}
and the main class:
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.geom.Line2D;
import java.awt.geom.Ellipse2D;
import javax.swing.JComponent;
public class Car
{
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
Rectangle body = new Rectangle(0, 10, 60, 20);
Line2D roof1 = new Line2D.Double(10, 10, 20, 0);
Line2D roof2 = new Line2D.Double(20, 0, 40, 0);
Line2D roof3 = new Line2D.Double(40, 0, 50, 10);
Ellipse2D.Double wheel1 = new Ellipse2D.Double(15, 25, 10,10);
Ellipse2D.Double wheel2 = new Ellipse2D.Double(45, 25, 10,10);
// draw the rectangle
g2.draw(body);
g2.draw(roof1);
g2.draw(roof2);
g2.draw(roof3);
g2.draw(wheel1);
g2.draw(wheel2);
}
}
It doesn't produce the same result. In fact, running car_jframe in cmd would output the face program. (they're in the same blueJ project)
What should I do?
Your main problem is that your Car class does not extend JPanel or JComponent or any class that derives from these, and so it cannot be added to your JFrame, and any attempts to do so will result in a compilation error. You have two main possible solutions:
Make Car a true component by having it extend JPanel (or JComponent, but I recommend JPanel).
Or if you want Car to be a logical class and not a component class, then create a JPanel that contains a Car object and that draw the Car object by directly calling that object's paintComponent method within the JPanel's own paintComponent method.
Other notes:
You almost always want to call the super.paintComponent(g) method within your override so that the JPanel can do housekeeping painting.
You always want to preface any method that you think is an override method with the #Override annotation as this way the compiler will warn you if your assumption is incorrect.
e.g.,
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.geom.*;
import javax.swing.*;
public class TestCar {
private static void createAndShowGui() {
Car mainPanel = new Car();
JFrame frame = new JFrame("A Car");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
// start all code on the Swing event thread
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class Car extends JPanel {
private static final int PREF_W = 400;
private static final int PREF_H = 300;
private static final Rectangle BODY = new Rectangle(0, 10, 60, 20);
private static final Line2D ROOF_1 = new Line2D.Double(10, 10, 20, 0);
private static final Line2D ROOF_2 = new Line2D.Double(20, 0, 40, 0);
private static final Line2D ROOF_3 = new Line2D.Double(40, 0, 50, 10);
private static final Ellipse2D WHEEL_1 = new Ellipse2D.Double(15, 25, 10,10);
private static final Ellipse2D WHEEL_2 = new Ellipse2D.Double(45, 25, 10,10);
public Car() {
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
// draw the rectangle
g2.draw(BODY);
g2.draw(ROOF_1);
g2.draw(ROOF_2);
g2.draw(ROOF_3);
g2.draw(WHEEL_1);
g2.draw(WHEEL_2);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
}
If you want to get real fancy, use a Path2D object, AffineTransform and a Swing Timer and give your GUI a little animation:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Car2 extends JPanel {
private static final int PREF_W = 400;
private static final int PREF_H = 300;
private static final Rectangle BODY = new Rectangle(0, 10, 60, 20);
private static final Line2D ROOF_1 = new Line2D.Double(10, 10, 20, 0);
private static final Line2D ROOF_2 = new Line2D.Double(20, 0, 40, 0);
private static final Line2D ROOF_3 = new Line2D.Double(40, 0, 50, 10);
private static final Ellipse2D WHEEL_1 = new Ellipse2D.Double(15, 25, 10,
10);
private static final Ellipse2D WHEEL_2 = new Ellipse2D.Double(45, 25, 10,
10);
private static final int TIMER_DELAY = 30;
private static final int CAR_DELTA_X = 1;
private static final int CAR_DELTA_Y = CAR_DELTA_X;
private Path2D path2D = new Path2D.Double();
public Car2() {
path2D.append(BODY, false);
path2D.append(ROOF_1, false);
path2D.append(ROOF_2, false);
path2D.append(ROOF_3, false);
path2D.append(WHEEL_1, false);
path2D.append(WHEEL_2, false);
new Timer(TIMER_DELAY, new CarTimerListener()).start();;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.draw(path2D);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
class CarTimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
path2D.transform(AffineTransform.getTranslateInstance(CAR_DELTA_X, CAR_DELTA_Y));
repaint();
}
}
}
You should only have one main method in your application. I'd change it like below to let the user select which shape to print when running the application.
import javax.swing.JFrame;
public class DrawFrame {
public static void main(String[] args) {
if(args[0].equals("face") {
paintFace()
}
else if(args[0].equals("car") {
paintCar();
}
else {
System.out.println("No Shape Selected to Print");
}
}
private static paintFace() {
JFrame frame = new JFrame();
frame.setSize(300,400);
frame.setTitle("A Rectangle Object in a JFrame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Face face = new Face();
frame.add(face);
frame.setVisible(true);
}
private static paintCar() {
JFrame frame = new JFrame();
frame.setSize(300,400);
frame.setTitle("a car");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Car car = new Car();
frame.add(car);
frame.setVisible(true);
}
}
Also your car does not extend Component. It won't work.
Correct it as
public class Car extends JComponent { ... }

Drawing to a Panel from separate classes

I have been trying to learn how to draw to a Jpanel for a game. I want to draw to it from different classes (like a class that manages maps and a class that manages player models).
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Main
{
public static void main (String[] args)
{
JFrame frame = new JFrame ("Java Game");
frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
frame.setSize (1000, 600);
JPanel panel = new JPanel();
panel.setBackground (Color.WHITE);
Dimension size = new Dimension(1000,500);
panel.add (new Player()); // Class with paintComponent method.
panel.setPreferredSize(size);
panel.setBackground(Color.BLUE);
frame.getContentPane().add(panel);
frame.setVisible(true);
}
}
next class
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JComponent;
#SuppressWarnings ("serial")
public class Player extends JComponent
{
int x = 50;
int y = 450;
public void paintComponent (Graphics g)
{
super.paintComponent(g);
g.setColor (Color.BLACK);
g.fillRect (x, y, 50, 50);
}
}
You probably want to extend JPanel. It's already opaque, and it can handle the background color for you. Also give your panel a size like they do here, then you can do the drawing relative to the size.
Player p = new Player();
p.setBackground(Color.cyan);
frame.add(p);
frame.pack();
frame.setVisible(true);
…
public class Player extends JPanel {
private static final int X = 320;
private static final int Y = 240;
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillRect(getWidth() / 2 - 25, getHeight() / 2 - 25, 50, 50);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(X, Y);
}
}

about drawing a Polygon in java

hi there i'm trying to improve myself about java2D and first of all i'm dealing with drawing polygons. However, i can not see the polygon on frame. I read some tutorials and examples but as i said i face with problems. here is the sample code of drawing a polygon;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Polygon;
import javax.swing.JFrame;
public class jRisk extends JFrame {
private JFrame mainMap;
private Polygon poly;
public jRisk(){
initComponents();
}
private void initComponents(){
mainMap = new JFrame();
mainMap.setSize(800, 600);
mainMap.setResizable(false);
mainMap.setVisible(true);
mainMap.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
int xPoly[] = {150,250,325,375,450,275,100};
int yPoly[] = {150,100,125,225,250,375,300};
poly = new Polygon(xPoly, yPoly, xPoly.length);
}
protected void paintComponent(Graphics g){
super.paintComponents(g);
g.setColor(Color.BLUE);
g.drawPolygon(poly);
}
/**
* #param args
*/
public static void main(String[] args) {
new jRisk();
}
}
JFrame does not have a paintComponent(Graphics g) method. Add the #Override annotation and you will get a compile time error.
1) Use JPanel and override paintComponent (you would than add JPanel to the JFrame viad JFrame#add(..))
2) Override getPreferredSize() to return correct Dimensions which fit your drawing on Graphics object or else they wont be seen as JPanel size without components is 0,0
3) dont call setSize on JFrame... rather use a correct LayoutManager and/or override getPrefferedSize() and call pack() on JFrame after adding all components but before setting it visible
4) Have a read on Concurrency in Swing specifically about Event Dispatch Thread
5) watch class naming scheme should begin with a capital letter and every first letter of a new word thereafter should be capitalized
6) Also you extend JFrame and have a variable JFrame? Take away the extend JFrame and keep the JFrame variable as we dont want 2 JFrames and its not good practice to extend JFrame unless adding functionality
Here is your code with above fixes (excuse picture quality but had to resize or it was going to 800x600):
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Polygon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class JRisk {
private JFrame mainMap;
private Polygon poly;
public JRisk() {
initComponents();
}
private void initComponents() {
mainMap = new JFrame();
mainMap.setResizable(false);
mainMap.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
int xPoly[] = {150, 250, 325, 375, 450, 275, 100};
int yPoly[] = {150, 100, 125, 225, 250, 375, 300};
poly = new Polygon(xPoly, yPoly, xPoly.length);
JPanel p = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
g.drawPolygon(poly);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 600);
}
};
mainMap.add(p);
mainMap.pack();
mainMap.setVisible(true);
}
/**
* #param args
*/
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new JRisk();
}
});
}
}
As per your comment:
i am preparing a map which includes lots of polygons and yesterday i
used a JPanel over a JFrame and i tried to check if mouse was inside
of the polygon with MouseListener. later i saw that mouseListener gave
false responds (like mouse is not inside of the polygon but it acts
like it was inside the polygon). so i deleted the JPanel and then it
worked
Here is updated code with MouseAdapter and overridden mouseClicked to check if click was within polygon.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Polygon;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class JRisk {
private JFrame mainMap;
private Polygon poly;
public JRisk() {
initComponents();
}
private void initComponents() {
mainMap = new JFrame();
mainMap.setResizable(false);
mainMap.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
int xPoly[] = {150, 250, 325, 375, 450, 275, 100};
int yPoly[] = {150, 100, 125, 225, 250, 375, 300};
poly = new Polygon(xPoly, yPoly, xPoly.length);
JPanel p = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
g.drawPolygon(poly);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 600);
}
};
MouseAdapter ma = new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent me) {
super.mouseClicked(me);
if (poly.contains(me.getPoint())) {
System.out.println("Clicked polygon");
}
}
};
p.addMouseListener(ma);//add listener to panel
mainMap.add(p);
mainMap.pack();
mainMap.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new JRisk();
}
});
}
}
JFrame does not extend JComponent so does not override paintComponent. You can check this by adding the #Override annotation.
To get this functionality extract paintComponent to a new class which extends JComponent. Don't forget to call super.paintComponent(g) rather than super.paintComponents(g).
Replace
protected void paintComponent(Graphics g){
super.paintComponents(g);
g.setColor(Color.BLUE);
g.drawPolygon(poly);
}
With
protected void paint(Graphics g){
super.paint(g);
g.setColor(Color.BLUE);
g.drawPolygon(poly);
}

How to draw Graphics2D on JPanel which is inside another JPanel?

I would like to create 4 JPanels, draw a white rectangular on each
and then put them inside one, big JPanel. Big JPanel is inside the main frame.
However, the following code does not work. Please, tell me, how to fix this problem?
import javax.swing.*;
import java.awt.Graphics;
import java.io.*;
import java.net.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
public class Main extends JFrame
{
public void GUI () {
setBounds(0, 0, 480, 960);
addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent we){
System.exit(0);
}
});
setMinimumSize(new Dimension(480, 960));
setResizable(false);
JPanel mainPanel = new JPanel();
GridLayout GL = new GridLayout(4,0);
mainPanel.setLayout(GL);
JPanel panel1 = new MyCanvas();
JPanel panel2 = new MyCanvas();
JPanel panel3 = new MyCanvas();
JPanel panel4 = new MyCanvas();
mainPanel.add(panel1);
mainPanel.add(panel2);
mainPanel.add(panel3);
mainPanel.add(panel4);
add(mainPanel);
setVisible(true);
}
public static void main(String args[]) throws IOException
{
new Main().GUI();
}
class MyCanvas extends JPanel {
public void drawCanvas(Graphics g) {
super.paintComponent( g ); // call superclass's paintComponent
Graphics2D g2 = ( Graphics2D ) g; // cast g to Graphics2D
g2.setColor(Color.WHITE);
double x = 100;
double y = 100;
double width = x + 200;
double height = y + 50;
g2.fillRect(50, 50, 380, 200);
}
}
}
What is this supposed to do?:
public void drawCanvas(Graphics g) {
....
}
This method overrides no JPanel drawing method and so will not routinely get called when the JVM decides to paint your MyCanvas JPanel.
I think that instead you want to override the class's paintComponent(...) method which can easily be done by simply renaming your method to paintComponent(...). If/when you do this, don't forget to use the #Override annotation to be sure that you're overriding the method with the correct signature. You'll also want to change the method's access specifier to protected, not public.
Next you'll want to do something with those double variables that you're creating.

Categories