How Do You Use Graphics 'g'? - java

I'm in a beginner's class that is teaching java and in our reading they gave an example about using graphics to draw items. So, I posted the code into my IDE and tried it out.
The code they posted was supposed to create an alien face and
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import javax.swing.JComponent;
/*
A component that draws an alien face
*/
public class FaceComponent extends JComponent
{
public void paintComponent(Graphics g)
{
// Recover Graphics2D
Graphics2D g2 = (Graphics2D) g;
// Draw the head
Ellipse2D.Double head = new Ellipse2D.Double(5, 10, 100, 150);
g2.draw(head);
// Draw the eyes
g2.setColor(Color.GREEN);
Rectangle eye = new Rectangle(25, 70, 15, 15);
g2.fill(eye);
eye.translate(50, 0);
g2.fill(eye);
// Draw the mouth
Line2D.Double mouth = new Line2D.Double(30, 110, 80, 110);
g2.setColor(Color.RED);
g2.draw(mouth);
// Draw the greeting
g2.setColor(Color.BLUE);
g2.drawString("Hello, World!", 5, 175);
}
}
However I am unsure how it is you create the Graphics to which you call the function. I had assumed that you would use
Graphics g = new Graphics();
Like how you call a new class, but this turned out to be an error. I tried looking into the documentation but it seems to gloss over what you use for 'g'.
Please help me understand.
Or is this a situation where this is a class I need to call from another class since I'm extending it?

paintComponent(Graphics g) is a method inherited from JComponent (Note that paintComponent should have #Override anotation), it is part of the draw system of the GUI. It's invoked from Java Swing Framework to ask for a Component to draw itself on the screen. The object past to the method is already instantiated and valid for usage.
You have to add your component to the screen with like JFrame/JPanel and make it visible to see it working.
Also that since you don't implement a measurable screen you need to add some space to it inside layout to see the result (setMinimumSize() after instiating, before frame.pack()).

Don't create a Graphics g with a new operator. It is passed in through the public void paintComponent(Graphics g) method.
Note that Java actually passes a Graphics2D object though this interface (which makes the cast possible) but for legacy reasons kept the Graphics interface by making the Graphics2D object inherit from Graphics.
The reason you don't create a new Graphics object is because the passed Graphics object is the one that is used by the rest of your widgets, and the AWT/Swing interface in general.
As far as "using" it, it is a standard Java object. Use it like a Java object, by calling methods on it.
g.setColor(...);
ect.

Related

creating a stroked shape

I am having trouble with creating a stroked shape in BasicStroke Outline = new BasicStroke(10f, 50, 50);. The error I am currently getting is error:
can't find symbol canvas.setStroke(Outline) pointing to the dot.
I am new to constructors so any help would be great and the only thing I did similar to this was creating an instance of Scanner.
import javax.swing.JFrame;
import java.awt.Graphics;
import java.awt.Color; //sets color
import java.awt.BasicStroke;
import java.awt.Graphics2D;
public class ColoredOlypmicRings extends JFrame
{
//varriables go here
public void paint(Graphics canvas)
{
super.paint (canvas);
canvas.setColor(Color.green);
canvas.drawOval(100,100,100,100); //color green
canvas.setColor(Color.red);
canvas.drawOval(200,200,100,100); //color red
final BasicStroke Outline = new BasicStroke(10f, 50, 50);
canvas.setStroke(Outline);
canvas.drawOval(300,300,200,200);
}
public ColoredOlypmicRings()
{
setSize(600,400);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public static void main(String[] args)
{
ColoredOlypmicRings guiWindow = new ColoredOlypmicRings();
guiWindow.setVisible(true);
}
}
The Graphics class can't handle Strokes and doesn't have methods for setting it as its API will tell you.
The Graphics2D class on the other hand can handle this class and should be used to handle it. So cast your Graphics object to a Graphics2D object.
e.g.,
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setStroke(....); // do it here after casting
}
Also check out the BasicStroke API as you're not using the constructor correctly, passing in incorrect parameters.
Other issues:
Don't draw directly in a JFrame or other top-level window.
Instead draw in the paintComnponent method of a JPanel that is displayed in the JFrame.
The three int BasicStroke constructor is being mis-used as the 2nd and 3rd parameters should be constants that represent the cap and join state of the Stroke object.

What is wrong with my applet code?

I am following a tutorial on applets on Youtube. My code looks exactly the same as the tutorial's does, but the background does not turn pink and Eclipse tells me there are errors in implements MouseListener and g2.draw(line); What did I do wrong? Click here for the video and here is my code:
package applets1;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import javax.swing.JApplet;
public class clean extends JApplet implements MouseListener{
public void start(){
}
public void init(){
setBackground(Color.pink);
addMouseListener(this);
}
public void paint(Graphics g){
Graphics g2D = (Graphics2D) g;
g.drawString("WAZZUP", 100, 90);
g.drawRect(100, 100, 400, 400);
Point2D.Double topLeft = new Point2D.Double(0.0, 25.0);
Point2D.Double topRight = new Point2D.Double(100.0, 25.0);
Line2D.Double line = new Line2D.Double(topLeft, topRight);
g2D.draw(line);
}
}
EDIT: The error at g2D.draw(line); says The method draw(Line2D.Double) is undefined for the type Graphics. I changed g2D.drawLine to g2D.draw
I also fixed the implements typo. The background is still not pink, despite the absence of an error and everything else works. What can I do to fix the pinkness and g2D.draw?
You have a typographical error. implements not implemets:
public class clean extends JApplet implements MouseListener{
Also you have declared g2D with the wrong type (Graphics vs Graphics2D). In other words, instead of
Graphics g2D = (Graphics2D) g; you need to use
Graphics2D g2D = (Graphics2D) g;
Once you make the above change, you will be able to invoke the g2D.draw() methods using the various 2D classes as parameters.
Also you have overridden the paint() method but you have not included a call to super.paint() - this should be the first line in your paint() method. Once you do this the background color should be rendered correctly (because it is handled by the base class, JApplet)

Assistance with java repaint() method?

I'm trying to make a rectangle to move across the screen, but instead it's just repainting and not getting rid of the preceding rectangle making it look like a giant rectangle across the screen. Any help would be appreciated, here's my code:
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.Timer;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.*;
#SuppressWarnings("serial")
public class Test extends JFrame implements ActionListener{
int x=50;
Timer tm = new Timer(30,this);
public static void main(String[] args){
new Test();
}
public Test(){
this.setSize(700, 500);
this.setTitle("Drawing Shapes");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
this.setVisible(true);
}
public void paint(Graphics g){
Graphics2D graph2 = (Graphics2D)g;
Shape Rect = new Rectangle2D.Float(x, 50, 50, 30);
graph2.setColor(Color.RED);
graph2.fill(Rect);
tm.start();
}
public void actionPerformed(ActionEvent e){
x=10+x;
repaint();
}
}
Draw in a JPanel that is held by and displayed within the JFrame. Don't draw directly in the JFrame as this risks drawing over things that shouldn't be messed with such as root panes, borders, child components,... Also you lose the benefits of automatic double buffering by drawing directly in the JFrame's paint method leading to choppy animation.
You should override the JPanel's paintComponent method not its paint method.
Always call the super's painting method in your own painting method (here it again should be paintComponent). This is your problem. So again, your paintComponent(Graphics g) override painting method should call super.paintComponent(g); on its first line. This will erase the old images.
You're breaking the paint chain...
public void paint(Graphics g){
// Broken here...
Graphics2D graph2 = (Graphics2D)g;
Shape Rect = new Rectangle2D.Float(x, 50, 50, 30);
graph2.setColor(Color.RED);
graph2.fill(Rect);
tm.start();
}
You MUST call super.paint. See Painting in AWT and Swing and Performing Custom Painting for more details about painting in Swing...
Top level containers are not double buffered and it is not recommended to paint directly to them, instead, create a custom component which extends from something like JPanel and override it's paintComponent method (calling super.paintComponent before performing any custom painting)
As a general rule of thumb, you should avoid extending from top level containers like JFrame, as you are not adding any new functionality to the class and they lock you into a single use-case, reducing the re-usability of your classes
DON'T call tm.start inside the paint method, you should do nothing in the paint methods except paint, never try and modify the state or otherwise perform an action which might indirectly modify the state of a component, this is a good way to have you program consume your CPU
To add to what other have already stated,
I've noticed that you use this.setLocationRelativeTo(null) for a simple application. Not saying it is bad, but you might want to check this thread to make sure it is what you want.
How to best position Swing GUIs?

Java applet with getSize().width, getSize().height

I just came across example - tutorial in book which I don't quite understand.
So here is the code
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Rectangle2D;
import java.util.GregorianCalendar;
import javax.swing.JApplet;
public class Watch extends JApplet {
private final Color butterscotch = new Color(255, 204, 102);
Rectangle2D.Float background;
// Whats is purpose of following line, here on this place? Applet works well even without it?
Graphics2D screen2D;
#Override
public void init() {
setBackground(Color.black);
}
#Override
public void paint(Graphics screen) {
super.paint(screen);
Graphics2D screen2D = (Graphics2D) screen;
Font type = new Font("Monospaced", Font.BOLD, 20);
screen2D.setFont(type);
screen2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
if (background == null) {
// set up the background rectangle
// Whats is purpose of following line? Applet works well even without parameters?
background = new Rectangle2D.Float(0F, 0F, getSize().width, getSize().height);
// But if previous line is omit then background color is not set - why?
// background = new Rectangle2D.Float(0F, 0F, 0F, 0F);
// background = new Rectangle2D.Float();
}
screen2D.fill(background);
GregorianCalendar day = new GregorianCalendar();
String time = day.getTime().toString();
screen2D.setColor(butterscotch);
screen2D.drawString(time, 5, 25);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// do nothing
}
repaint();
}
}
My questions are:
What is purpose of this declaration (line 16) here on this place in code:
Graphics2D screen2D;
code works even without it? To be more precise Graphics2D object is of course declared later with same object name (screen2D) later on in paint() (line 26).
What is purpose of
getSize().width, getSize().height in this line (33):
background = new Rectangle2D.Float(0F, 0F, getSize().width, getSize().height);
I mean, how can it even get size of newly created object when size is not defined (prior to) that object creation? OK, I recon that since Eclipse gives initial value of 200 x 200 this might makes sense, so that getSize().width, getSize().height gets those values? Am I right about that one?
Moreover any of following (substitution) lines of code works well, instead of that one:
background = new Rectangle2D.Float(0F, 0F, 0F, 0F);
or
background = new Rectangle2D.Float();
BUT, here comes another question
If background = new Rectangle2D.Float(0F, 0F, getSize().width, getSize().height);
is substituted with
background = new Rectangle2D.Float(0F, 0F, 0F, 0F);
or
background = new Rectangle2D.Float();
Then background color is NOT black as defined in init() line 20
setBackground(Color.black);
but it is some shade of gray. Why? What do I miss here?
One more note: I am using Eclipse IDE Kepler if it matters in this case anyhow (I know that default applet size is 200 x 200, which could be modified in Run configuration -> parameters )
In this example there are two objects called screen2D. The first which is declared on line 16 is a member variable that is package private (can be accessed by all classes in the given package). As you have pointed out this variable doesn't seem to be used at all.
The second screen2D is a local variable declared on line 26 and used only in the paint function.
Likely this is a typo and only one of these variables is needed for the example.
the function getSize() is inherited from the superclass JApplet so getSize.width() is getting the applets width not the width of a newly created object.
So when you change the background to have dimensions of 0 then set its color you are setting the color of an object that doesn't have any size. Since this object doesn't have any size it doesn't matter what color you set it to since you wont actually be seeing it.

How to I make a simple window that I can render to?

I've just finished a 2D vector/transformation library that I want to use on a simple example. I have a main loop that runs efficiently and update/render methods. I've always tried to understand what people are talking about when they use Java2D or jPanels or jFrames, but none of it makes sense to me.
I've made some 2D examples before, but it is using a jFrame with a Threaded canvas that I made when following a youtube tutorial. Its problem is that it is basically an integer array that allows for individual pixel setting and you can only use integers as positions, not floats like my library uses.
so my question is: how would I go about making a simple opening/closing window, that I can draw a sprite (should sprites just be some sort of slickUtil loaded thing, or will I have to load in individual pixels as I did before?) to, and that accepts float values for Cartesian coordinates with the origin at the centre.
Derive a class from JComponent and override the paintComponent method. It gets passed a Graphics object that can be cast into a Graphics2D object. The latter one has support for changing the coordinate system.
For drawing sprites: Loading individual pixels in a loop is very slow. There is a drawImage method in Graphics2D that supports everything you need for blitting sprites.
Here's an example to setup the Graphics2D object with a centered origin in a self-contained example:
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.RenderingHints;
import java.awt.geom.Line2D;
import javax.swing.JComponent;
import javax.swing.JFrame;
public class Draw2D extends JFrame {
public Draw2D() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new BorderLayout());
add(new DrawPane(), BorderLayout.CENTER);
pack();
}
public static void main(String[] args) {
Draw2D drawing = new Draw2D();
drawing.setVisible(true);
}
}
class DrawPane extends JComponent {
public DrawPane() {
setPreferredSize(new Dimension(640, 640));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
// use anti-aliasing for smooth lines
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// move origin to center
g2.translate(getWidth() / 2, getHeight() / 2);
// scale as you need. Using negative y so that y points upward
// note that non-square window sizes will cause a different aspect ratio,
// you probably want to use Math.min(width, height) or something
g2.scale(getWidth() / 2, -getHeight() / 2);
// set color and thickness
g2.setColor(Color.red);
g2.setStroke(new BasicStroke(0.001f));
// draw coordinate lines
g2.draw(new Line2D.Float(-1f, 0f, 1.0f, 0f));
g2.draw(new Line2D.Float(0, -1.0f, 0.0f, 1.0f));
// draw a vector
g2.draw(new Line2D.Float(0f, 0f, 0.25f, 0.25f));
}
}

Categories