How do I draw an array of graphics objects? - java

So I have been pulling my hair out over this assignment for quite some time now, and I could really use some help. I have to create a rectangle graphics object in a super class, then extend that class into a subclass, that then draws 7 of said rectangles to draw a number like you would see on a calculator. I am creating an array of rectangles in the subclass and trying to add them to a panel, however I keep getting errors for all the ways I try to add to the panel. Any help would be appreciated immensely.
This is my super class
import javax.swing.*;
import java.awt.*;
public class Bar extends JPanel {
public Color myColor;
Graphics g;
int x;
int y;
int w;
int h;
public Bar(int newX, int newY, int newW, int newH) {
x = newX;
y = newY;
w = newW;
h = newH;
super.paintComponent(g);
g.setColor(getColor());
g.fillRect(x, y, w, h);
}
public Color getColor() {
return myColor;
}
}
and then my subclass
import java.awt.Color;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class LED extends Bar {
public static Bar[] myLED;
public static int number;
public boolean isOn;
public LED(Bar[] myLED) {
super(10, 10, 15, 45);
switch(number) {
case 0: myLED[0] = new Bar(10, 10, 15, 45);
isOn = true;
}
}
public void setOn(boolean isOn) {
if(isOn = true) {
myColor = Color.GREEN;
}
else
myColor = Color.BLACK;
}
public static Bar[] getLED() {
return myLED;
}
public static void main(String[] a) {
JFrame window = new JFrame("Calculator");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setBounds(30, 30, 500, 500);
JPanel panel = new JPanel();
panel.add(myLED); //I'm getting an error on this line, as well as the one below this.
window.getContentPane().add(myLED);//getContentPane().
window.setVisible(true);
}
}
Thanks.

Related

Adding an array of JComponent extended objects to a JFrame

I'm begining with JFrame, I'm triying to make a StarField, for the moment I'm adding the Star JComponent to the Starfield JFrame:
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JComponent;
public class Star extends JComponent{
public int x;
public int y;
private final Color color = Color.YELLOW;
public Star(int x, int y) {
this.x = x;
this.y = y;
}
public void paintComponent(Graphics g) {
g.setColor(color);
g.fillOval(x, y, 8, 8);
}
}
and the StarField code:
import javax.swing.*;
public class StarField extends JFrame{
public int size = 400;
public Star[] stars = new Star[50];
public static void main(String[] args) {
StarField field = new StarField();
field.setVisible(true);
}
public StarField() {
this.setSize(size, size);
for (int i= 0; i< stars.length; i++) {
int x = (int)(Math.random()*size);
int y = (int)(Math.random()*size);
stars[i] = new Star(x,y);
this.add(stars[i]);
}
}
}
The problem it's thar it only print one star, I think it is the last one, the coords are working like they are supposed to do it, so I think the mistake is in the JComponent or JFrame implementation, I'm self-learning, so maybe my code isn't the correct way for using swing.
Thank you, and sorry for my english, I'd tried to write it the best I know.
In your case you cannot use a layout manager, and need to reset it to null. See the my code below
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.WindowConstants;
public class StarField extends JFrame {
public int size = 400;
public Star[] stars = new Star[50];
public static void main(String[] args) {
StarField field = new StarField();
field.setVisible(true);
}
public StarField() {
this.setSize(size, size);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
// usually you should use a normal layout manager, but for your task we need null
getContentPane().setLayout(null);
for (int i = 0; i < stars.length; i++) {
int x = (int) (Math.random() * size);
int y = (int) (Math.random() * size);
stars[i] = new Star(x, y);
this.add(stars[i]);
}
}
public class Star extends JComponent {
private final Color color = Color.YELLOW;
public Star(int x, int y) {
// need to set the correct coordinates
setBounds(x, y, 8, 8);
}
#Override
public void paintComponent(Graphics g) {
g.setColor(color);
g.fillOval(0, 0, getWidth(), getHeight());
}
}
}

Drawing Shape with ActionListener from Another Class

In my program, I am gonna ask for number of rectangles in JPanel and add them into the frame with create button. I want to call rectangles from another class. But I can not see my rectangles. When I write same paint method in my main class, I can see rectangles but they appers when I run the program. I want them to appear with actionlistener. What am I doing wrong? Here is my classes;
Rectangle Class:
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
public class Rectangle extends JFrame {
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public int getW() {
return w;
}
public void setW(int w) {
this.w = w;
}
public int getH() {
return h;
}
public void setH(int h) {
this.h = h;
}
public Color getC() {
return c;
}
public void setC(Color c) {
this.c = c;
}
private int x,y,w,h;
private Color c;
private asgn3 a3;
public Rectangle() {
}
#Override
public void paint(Graphics g) {
super.paint(g);
g.fillRect(getX(), getY(), getW(), getH());
g.setColor(Color.red);
}
}
My main JFrame class:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class Example extends JFrame implements ActionListener {
private JTextField recttext;
private JButton create;
private JLabel rectlabel,createlabel;
private int seat a;
private Rectangle[] rect;
public Example() {
setLayout(null);
recttext = new JTextField();
recttext.setSize(150, 40);
recttext.setLocation(100, 40);
add(recttext);
rectlabel = new JLabel("Rectangle number");
rectlabel.setSize(100, 20);
rectlabel.setLocation(102, 20);
add(rectlabel);
create = new JButton("Create");
create.setSize(70,30);
create.setLocation(670, 20);
add(create);
create.addActionListener(this);
setSize(800,800);
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setResizable(false);
}
public static void main(String[] args) {
new Example();
}
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource().equals(create)) {
a = Integer.parseInt(recttext.getText());
rect = new Rectangle[a];
for(int i=0;i<a;i++) {
rect[i]= new Rectangle();
}
for(int i=1;i<=a/10;i++) {
for(int j=i;j<11;j++) {
rect[i-1].setX((getWidth()/(a/10))*i+20);
rect[i-1].setY(100+j*50);
rect[i-1].setW(100);
rect[i-1].setH(50);
repaint();
}
}
}
}
}
When I write same paint method in my main class
What paint method? All your code does is create an Array of Rectangle objects. You have no code to do any painting.
The way painting works is that you override the paintComponent() of a JPanel. So you need to create a custom JPanel for your painting. Then you create an ArrayList of Rectangle objects. In the paintComponent() method you iterate through the ArrayList and paint each Rectangle.
Read the section from the Swing tutorial on Custom Painting for a simple example to get you started.
You can also check out the DrawOnComponent example found in Custom Painting Approaches. It demonstrates how you can dynamically add Rectangle objects to be painted. The key it so use the addRectangle(...) method each time you want to display another Rectangle.

Two ScheduledExecutorService overriding eachother?

I'm trying to make two blinking circles that blink at different rates. I'm using ScheduledExecutorService in the Circle class to regulate the blinking, and its duration is set by the ms (milliseconds) variable in each Circle.
When I make one car individually, they blink at the correct rates (I have the black one set to 1000ms, the red set to 10ms). However, when I create them both and add them to my JLayeredPane, they both blink at the shorter period.
I'm not too familiar with the use of ScheduledExecutorService, so if someone could help me out with what's going wrong it'd be greatly appreciated!
import java.awt.Color;
import java.awt.Graphics;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.awt.*;
import javax.swing.*;
public class blinker extends JFrame
{
JLayeredPane lp = new JLayeredPane();
public carlight()
{
lp.setLayout(new BorderLayout());
lp.setPreferredSize(new Dimension(450, 450));
car c1 = new car(new Color(0, 0, 0), "1", 10, 0, 0);
c1.setOpaque(false);
car c2 = new car(new Color(255, 0, 0), "2", 1000, 100, 100);
c2.setOpaque(false);
c1.setBounds(0, 0, 450, 450);
c2.setBounds(0, 0, 450, 450);
lp.add(c2);
lp.add(c1);
add(lp);
setTitle("Carlights");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(500, 500);
setVisible(true);
}
public static void main(String[] args)
{
carlight cl = new carlight();
}
}
class Circle extends JPanel
{
private Color color;
private String name;
private long ms;
private int x, y;
private boolean on = true;
ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
#Override
public void paintComponent(Graphics g)
{
super.paintComponents(g);
if(on)
{
g.setColor(color);
int r = 50;
g.fillOval(x, y, r, r);
on = false;
}
else
{
on = true;
}
}
public car(Color c, String s, long l, int x, int y)
{
color = c;
name = s;
ms = l;
this.x = x;
this.y = y;
this.service.scheduleAtFixedRate(new Runnable()
{
public void run()
{
repaint();
}
}, 0, ms, TimeUnit.MILLISECONDS);
}
}
Your problem is that you have program logic in the paintComponent method, where you change the state of the boolean variable. You don't have full control over when or even if this method will be called, and in fact both paintComponents will be called when repaint is called which is why your blinkers aren't working. The solution: get the logic out of the paintComponent method by changing the state of the boolean field, on, elsewhere. Also you'll want to use a Swing Timer for better Swing threading.
You also will want to fix your use of layouts including avoiding use of setBounds. This is especially dangerous and unpredictable in your setup, using it with a BorderLayout. Myself, I'd not make the Circle class extend a JPanel but rather make it a logical class, not a component class, and then I'd have the drawing component, a class that does extend JPanel, hold instances of Circle classes and then draws them in its paintComponent. For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
#SuppressWarnings("serial")
public class BlinkerEg extends JPanel {
private static final int PREF_W = 450;
private static final int PREF_H = PREF_W;
private List<Circle> circles = new ArrayList<>();
public BlinkerEg() {
circles.add(new Circle(Color.red, 1000, 0, 0, 450, this));
circles.add(new Circle(Color.black, 60, 0, 0, 450, this));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
for (Circle circle : circles) {
circle.paint(g2);
}
}
private static void createAndShowGui() {
BlinkerEg mainPanel = new BlinkerEg();
JFrame frame = new JFrame("BlinkerEg");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class Circle {
private Color color;
private int x, y;
private int diam;
private JComponent component;
private boolean on = true;
public Circle(Color color, int ms, int x, int y, int diam, JComponent component) {
this.color = color;
this.x = x;
this.y = y;
this.diam = diam;
this.component = component;
new Timer(ms, new TimerListener()).start();
}
public void paint(Graphics g) {
if (on) {
g.setColor(color);
g.fillOval(x, y, diam, diam);
}
}
public boolean isOn() {
return on;
}
public void setOn(boolean on) {
this.on = on;
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
setOn(!isOn());
component.repaint();
}
}
}

Drawing Multiple JComponents to a Frame

I am trying to draw multiple car objects onto the same window but it appears that they are overwriting each other.
Here is my overridden paintComponent method in the Car class
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setColor(wheelColor);
g2.fill(leftWheel);
g2.fill(rightWheel);
g2.setColor(bodyColor);
g2.fill(body);
g2.fill(cab);
}
And in my Viewer Class:
JFrame f = new JFrame();
initializeFrame(f);
Car x = new Car(100, 100);
Car y = new Car(300, 300);
f.add(x);
f.add(y);
Although the coordinates seem to be different, only the last car is being drawn.
Any suggestions? Thanks
What you want to do is use a data structure of Car objects and loop through them in the paintComonent method. Something like
List<Car> cars = new ArrayList<>();
....
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Car car : cars) {
car.drawCar(g);
}
}
The drawCar method would come from your Car class
public class Car {
int x, y;
public Car(int x, int y) {
this.x = x;
this.y = y;
}
public void drawCar(Graphics g) {
g.setColor(Color.BLACK);
// do everything here as you would in a paintComponent method
}
}
See more examples here and here and here and here and here and here.
UPDATE
Here is a simple example use some "Ferraris" I whipped up, also using some animation, but with the same basic points I have above.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class DrawCar extends JPanel{
private static final int D_W = 400;
private static final int D_H = 400;
List<Car> cars;
public DrawCar() {
cars = new ArrayList<>();
cars.add(new Car(100, 300));
cars.add(new Car(200, 100));
Timer timer = new Timer(50, new ActionListener(){
public void actionPerformed(ActionEvent e) {
for (Car car : cars) {
car.move();
repaint();
}
}
});
timer.start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Car car : cars) {
car.drawCar(g);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(D_W, D_H);
}
public class Car {
private static final int INCREMENT = 5;
int x, y;
public Car(int x, int y) {
this.x = x;
this.y = y;
}
public void drawCar(Graphics g) {
g.setColor(Color.BLUE);
g.fillRect(x, y, 100, 30);
g.setColor(Color.BLACK); // body
g.fillOval(x + 15, y + 20, 15, 15); // wheel
g.fillOval(x + 60, y + 20, 15, 15); // wheel
g.fillRect(x + 15, y - 20, 60, 20); // top
}
public void move() {
if (x == D_W) {
x = 0;
} else {
x += INCREMENT;
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.add(new DrawCar());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
but it appears that they are overwriting each other.
The default layout manager for a JFrame is a BorderLayout. So by default you are adding all your components to the CENTER of the BorderLayout. However, you can only ever add one component to the CENTER so only the last Car is displayed.
Change the Layout Manager to a FlowLayout to see the difference.
Or, it looks like you are trying to paint the Cars in random positions, in which case you should use a "null" layout. Then you will be responsible for setting the size/location of each of the Car components.

How to change locations of drawn objects with user input

I'm attempting to draw multiple fish (very simple ones), as many as the user specifies. But the circles aren't drawn in the proper places when the user specifies they want more than one fish to be drawn. All ovals are drawn, they just aren't in the right spots, so they no longer look like fish.
public class FishList extends JPanel {
static int fn = Integer.parseInt(JOptionPane.showInputDialog(null, "How many fish would you like to draw? "));
static int w = 200;
static int h = 100;
static int x;
static int y;
static int a = x + 20;
static int b = y + 30;
static int d = 50;
static int c = x + 195;
public FishList() {
setPreferredSize(
new Dimension(400,400));
}
public void paint(Graphics g) {
g.setColor(Color.GREEN);
g.fillOval(x, y, w, h);
g.fillOval(c, y, d, h);
g.setColor(Color.BLACK);
g.fillOval(a, b, 25, 25);
}
public static void main(String[] args) {
MyFrame frame1 = new MyFrame("Drawing Fish");
JPanel outer = new JPanel();
for(int i=0; i<fn; i++){
x = 0 + (i*(w+d+1));
y = 0;
FishList sPanel1 = new FishList();
outer.add(sPanel1);
}
frame1.add(outer);
frame1.pack();
frame1.setVisible(true);
}
}
JPanel, by default, uses a FlowLayout. FlowLayout also uses the component's preferred size to determine how to layout each component within the Container.
Failing to call super.paint is going to cause you serious issues. In fact, you should use paintComponent instead of paint and make sure you are calling super.paintComponent.
Take a look at Performing Custom Painting and Painting in AWT and Swing for more details
Components already have a sense of location and size, which you are ignoring. The use of static variables in this context isn't going to help, because basically, each instance of your fish will be painted in the exact same location, as they will share the same value of the each of the static variables...
A better solution would be to generate a class that is capable of begin painted, which then paints the "fish".
These would be included inside a component capable of painting them, which you could then just add to a Container which is using a BorderLayout.
For example...
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.Rectangle;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Fishies {
public static void main(String[] args) {
new Fishies();
}
public Fishies() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new FishBowel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class FishBowel extends JPanel {
private List<Fish> fishes = new ArrayList<>(25);
public FishBowel() {
for (int index = 0; index < 10; index++) {
int width = random(20);
int height = width;
int x = random(200 - 20);
int y = random(200 - 20);
fishes.add(new Fish(new Rectangle(x, y, width, height)));
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for (Fish fish : fishes) {
fish.paint(g2d);
}
g2d.dispose();
}
}
public static int random(int max) {
return (int)(Math.round(Math.random() * max));
}
public class Fish {
private Color color;
private Ellipse2D fish;
public Fish(Rectangle bounds) {
this(new Color(random(255), random(255), random(255)), bounds);
}
public Fish(Color color, Rectangle bounds) {
this.color = color;
fish = new Ellipse2D.Float(bounds.x, bounds.y, bounds.width, bounds.height);
}
public Ellipse2D getFish() {
return fish;
}
public Color getColor() {
return color;
}
public void paint(Graphics2D g2d) {
g2d.setColor(getColor());
g2d.fill(fish);
}
}
}

Categories