Threaded OverlayLayout prints only blank squares instead of circles - java

I got two threads printing in a JFrame:
Thread-1: prints green squares
Thread-2: prints blue circles
To make both figures appear in the JFrame I set the layout for each thread as:
setLayout(new OverlayLayout(this));
setOpaque(false);
Problem: the blue circles appear like blank squares in the JFrame. Only the green squares appear correctly.
Here is the main class:
public class Main {
public static void main(String[] args) {
FigurePlacer circle = new FigurePlacer("circle");
FigurePlacer square = new FigurePlacer("square");
JFrame window = new JFrame();
window.add(circle);
window.add(square);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setTitle("Task");
window.setSize(700, 700);
window.setLocationRelativeTo(null);
window.setVisible(true);
}
}
And here is the Threaded Class:
public class FigurePlacer extends JPanel implements Runnable{
String figure;
final int width = 700;
final int height = 700;
int x_pos = 0;
int y_pos = 0;
int x_width = 50;
int y_height = 50;
public FigurePlacer(String str){
figure = str;
randomCoord();
Thread th = new Thread (this);
th.start();
}
private void randomCoord(){ //this ramdomize x,y coord to place a new object
Random random = new Random();
x_pos = random.nextInt(width);
y_pos = random.nextInt(height);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println(figure);
switch (figure){
case "square":
g.setColor(Color.GREEN);
g.fillRect(x_pos, y_pos, x_width, y_height);
break;
case "circle":
g.setColor(Color.BLUE);
g.fillOval(x_pos, y_pos, x_width, y_height);
break;
}
}
#Override
public void run(){ //paints the objects
while (true){
randomCoord();
paintImmediately(x_pos, y_pos, x_width, y_height);
try{
Thread.sleep (50);
}
catch (InterruptedException ex){}
}
}
}

setLayout(new OverlayLayout(this));
setOpaque(false);
Setting non-opaque on the panel containing the two child panels doesn't do anything.
window.add(circle);
window.add(square);
You need to make the "square" panel non-opaque, so you can see the "circle" panel. (or make both panels non-opaque). The point is when two components are painted on top of one another the top panel needs to be non-opaque).
And your painting code is still incorrect. Have you tried resizing the frame. If you do you will see all the squares disappear.

Related

How to correctly perform multithreading so that icons on individual threads move across the screen together?

I'm trying to create an animation in which a given number of icons start on the left side of the frame and move to the right side of the screen. The icons are lined up vertically and each is supposed to run on its own thread.
How do I get all icons to do this? I tried adjusting posY when I create each racer, but so far I can only get the last racer that's created to show.
import javax.swing.*;
import java.awt.*;
public class Races {
private JFrame frame;
private JPanel gui;
private Icon img;
private int imgWidth;
private int imgHeight;
private int numOfRacers; // num of threads / racers
public static void main(String[] args) {
new Races(5);
}
public Races(int num) {
numOfRacers = num;
createGUI();
frame.add(gui);
frame.pack();
frame.setVisible(true);
}
private void createGUI() {
frame = new JFrame("Off to the Races - by Brienna Herold");
gui = new JPanel();
gui.setPreferredSize(new Dimension(imgWidth * 20, imgHeight * numOfRacers));
img = new ImageIcon("races.png");
imgWidth = img.getIconWidth();
imgHeight = img.getIconHeight();
int posY = 0;
for (int i = 0; i < numOfRacers; i++) {
System.out.println("Starting new thread..." + posY);
racer = new Racer(posY);
Thread racerThread = new Thread(racer);
racerThread.start();
posY += imgHeight;
}
}
protected class Racer extends JPanel implements Runnable {
private int lastPosX;
private int posX;
private int posY;
public Racer(int _posY) {
posX = 0;
posY = _posY;
}
#Override
public void paintComponent(Graphics g) {
// Call the method on the JPanel
super.paintComponent(g);
img.paintIcon(gui, g, posX, posY);
//posY += imgHeight;
posX += lastPosX + 3;
}
#Override
public void run() {
while (true) {
repaint();
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
}
}
gui.add(racer, BorderLayout.CENTER);
You only ever add one Racer component to the frame.
but so far I can only get the last racer that's created to show.
The "racer" variable points to the last Racer component created.
If you want multiple components then you will need to create a panel using a null layout. Then you need to add each Racer component to this panel and then add the panel to the frame. Using this approach you would move the component by using the setLocation(...) method of the component.
If you want to paint an image in a different location, then you would use a single panel which would contain an ArrayList or Racer objects. Then when the paintComponent() method of the panel is invoked you iterate through the List and paint each Racer at its new location.

Repainting JPanel

So i'm trying to clear my drawing Panel and I have looked at multiple examples but none of them seem to be working for me? I have a clear button that clears textfields/errors which I got to work perfectly but the Drawing panel still does not clear arraylists or "repaint".
I'm playing around with changing around the size of the oval so please ignore my drawPoints method.
Here is my code:
public class Panel extends JPanel{
ArrayList<Point> pointArray = new ArrayList<>();
ArrayList<Color> colorArray = new ArrayList<>();
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
repaint();
//Create the 2D graphics object
Graphics2D myDrawing = (Graphics2D) g;
for (int i = 0; i < pointArray.size(); i++) {
myDrawing.setColor(colorArray.get(i));
myDrawing.fillOval(pointArray.get(i).x,pointArray.get(i).y, 10, 10);
}
}
public void drawPoints(int mouseX, int mouseY, int height, int width){
Point p = new Point(mouseX,mouseY);
pointArray.add(p);
colorArray.add(this.getForeground());
repaint();
}
public void changeColor(){
int red = (int) (Math.random() * 256);
int green = (int) (Math.random() * 256);
int blue = (int) (Math.random() * 256);
this.setForeground(new Color(red,green,blue));
}
public void mousePressed(MouseEvent event) {
pointArray.clear();
colorArray.clear();
repaint();
}
}
public static void main(String[] args) {
//set the frame
JFrame frame = new JFrame();
frame.setSize(600, 300);
frame.setTitle("Multiple Panels");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//create the panel for GUI
JPanel panelGUI = new JPanel();
panelGUI.setBackground(Color.yellow);
//GUIs
//create textfields
JTextField radiusField1 = new JTextField("10", 10);
JTextField radiusField2 = new JTextField("10", 10);
//create buttons
final JButton clearDrawingButton = new JButton("Clear Screen");
final JButton changeColorButton = new JButton("Change Color");
//labels
final JLabel displayLabel = new JLabel("");
//add all GUIs to the GUI panel
panelGUI.add(radiusField1);
panelGUI.add(radiusField2);
panelGUI.add(changeColorButton);
panelGUI.add(clearDrawingButton);
panelGUI.add(displayLabel);
//create the panel to draw
final Panel drawingPanel = new Panel();
drawingPanel.setBackground(Color.white);
//create the initial color
Color drawingColor = new Color(255,0,0);
//set the initial drawing color of the panel
drawingPanel.setForeground(drawingColor);
//add the grid with two columns and two rows to add the three panels
GridLayout grid = new GridLayout(0,2,10,20);
//add the grid to the frame
frame.setLayout(grid);
//add the panels to the frame
frame.add(panelGUI);
frame.add(drawingPanel);
class MouseClickListener implements MouseListener
{
public void mouseClicked(MouseEvent event)
{
int x = event.getX();
int y = event.getY();
System.out.println(x + " " + y);
try {
String text1 = radiusField1.getText();
String text2 = radiusField2.getText();
int height = Integer.parseInt(text1);
int width = Integer.parseInt(text2);
drawingPanel.drawPoints(x, y, height, width);
} catch (NumberFormatException ex) {
displayLabel.setText("Textfields empty! Please enter number.");}
}
// Do­nothing methods
public void mouseReleased(MouseEvent event) {}
public void mousePressed(MouseEvent event) {}
public void mouseEntered(MouseEvent event) {}
public void mouseExited(MouseEvent event) {}
}
class ButtonListener implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
if (event.getSource()== changeColorButton){
drawingPanel.changeColor();
}
if(event.getSource()==clearDrawingButton){
radiusField1.setText("10");
radiusField2.setText("10");
displayLabel.setText("");
}
}
}
MouseListener listener1 = new MouseClickListener();
drawingPanel.addMouseListener(listener1);
ActionListener listener = new ButtonListener();
changeColorButton.addActionListener(listener);
clearDrawingButton.addActionListener(listener);
frame.setVisible(true);
}
}
I have a clear button that clears textfields/errors which I got to work perfectly but the Drawing panel still does not clear arraylists or "repaint".
Well look at the code that clears the text fields:
if(event.getSource()==clearDrawingButton){
radiusField1.setText("10");
radiusField2.setText("10");
displayLabel.setText("");
}
Where is the code that clears the ArrayLists?
Add the code to clear the ArrayLists to the ActionListener.
You can also check out Custom Painting Approaches for working code that draws "rectangles". It supports different colors and a "Clear" button.
Also, instead of using a JTextField for the oval size, you might want to consider using a JSpinner. This will allow the user to easily change the numeric value and you don't have to add any special editing to make sure the value entered is a number.
You have this mousePressed method in your main class:
public void mousePressed(MouseEvent event) {
pointArray.clear();
colorArray.clear();
repaint();
}
But it isn't doing anything because your main class does not implement MouseListener and no one is calling this method.
The rest of your code is not very pretty looking. I assume you are doing this as part of a course or otherwise just trying to learn Java Swing in a non-work environment. If this is true, I would recommend that you start over - at least with your MouseListeners, and instead create Actions for your button responses by subclassing AbstractAction and using it with JButton.setAction(myAction); This may seem painful now, but you'll be glad you did it in the future

Object.drawItself in PaintComponent doesnt work

PaintComponent doest paint figures. Just nothing is happening, clean Jframe appear.
I think something is wrong with list or with the way i called method
List is in class with Paint Component
public class Paint extends JPanel implements ActionListener {
List<Figures> figuresList = new ArrayList<Figures>();
Timer t = new Timer(5, this);
public void paintComponent(Graphics g) {
super.paintComponent(g);
for (Figures figure : figuresList) {
figure.drawItself(g, figure.getLocationX(), figure.getLocationY());
}
t.start();
}
#Override
public void actionPerformed(ActionEvent e) {
{
for (Figures figure : figuresList) {
if (figure.getLocationX() < 0 || figure.getLocationX() > 540) {
figure.setVelocityX(-figure.getVelocityX());
}
if (figure.getLocationY() < 0 || figure.getLocationX() > 220) {
figure.setVelocityY(-figure.getVelocityY());
}
figure.setLocationX(figure.getLocationX()
+ figure.getVelocityX());
figure.setLocationY(figure.getLocationY()
+ figure.getVelocityY());
}
}
repaint();
}
And drawitself:
public class Circle implements Figures {
public int locationX = 12;
public int locationY = 12;
public int velocityX =1;
public int velocityY =1;
public void drawItself(Graphics g, int locationX, int locationY){
this.locationX = locationX;
this.locationY = locationY;
g.drawOval(locationX, locationY, 40, 40);
g.fillOval(locationX, locationY, 40, 40);
}
Main:
public static void main(String[] args) {
Circle c = new Circle();
Quadrat q = new Quadrat();
Paint p = new Paint();
p.figuresList.add(c);
p.figuresList.add(q);
GUI.Configuration();
}
GUI
public class GUI {
public static void Configuration(){
JFrame frame = new JFrame("Figures Animation");
frame.setSize(600,300);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new Paint();
frame.getContentPane().add(BorderLayout.CENTER, panel);
}
You create and add a Paint instance here:
public class GUI {
public static void Configuration(){
JFrame frame = new JFrame("Figures Animation");
frame.setSize(600,300);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new Paint(); // *** new Paint is here, but nothing is added
frame.getContentPane().add(BorderLayout.CENTER, panel);
}
But nothing of use has been added to it. All the important stuff is added to a completely different Paint JPanel, one that is never displayed:
public static void main(String[] args) {
Circle c = new Circle();
Quadrat q = new Quadrat();
Paint p = new Paint(); // **** ANOTHER new Paint is here, and it gets goodies
p.figuresList.add(c);
p.figuresList.add(q);
// but is never added to a JFrame and is never displayed.
GUI.Configuration();
}
Don't do this. Create one Paint JPanel, one only, add the important components to it, and then only add that one to the JFrame. Most important, don't just type in code, think and plan your program before committing it to code, and you won't see errors like this.
Also, and again, do not start a Timer from within paintComponent and don't create Circle there. You can draw your Circle instance in paintComponent, but create it and start your Timer within the Paint constructor.

Swing paintComponent not working properly

I don't understand why i have to set Component.setPreferredSize() to draw and why my ovals are not placed in one place. Also i got some other questions, which are described below.
public class PaintPanel extends JPanel implements ActionListener
{
private void initStructure()
{
for (int i : new Range(MAX_AGENTS)) {
Agent agent = new Agent();
agents.add(agent);
add(agent);
}
}
//i want to override parent class 'add', to easy call 'actionPerformed' on children elements.
public Component add(Element comp)
{
elements.add(comp);
return super.add(comp);
}
#Override
public void actionPerformed(ActionEvent e)
{
for(Element element: elements){
element.actionPerformed(e);
}
repaint();
}
//ELEMENT is abstract class: 'public class Element extends JComponent implements ActionListener'
public class Agent extends Element
{
public Agent()
{
super();
// setPreferredSize(new Dimension(120,120)); !!!! a-a, i don't know the future size of the oval or triangle, i don't want and i can't set this :(
setVisible(true);
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
System.out.println("draw"); //called
Point p1 = new Point(1, 1);
Point p2 = new Point(101, 101);
Point p3 = new Point(10, 10);
int[] xs = { p1.x, p2.x, p3.x };
int[] ys = { p1.y, p2.y, p3.y };
Polygon triangle = new Polygon(xs, ys, xs.length);
g.setColor(new Color(255,255,255)); // !! never painted
g.fillPolygon(triangle); //!! never painted
g.drawOval(10,10,10,10); // !!!! painted only when i set preferredSize
}
#Override
public void actionPerformed(ActionEvent e)
{
System.out.println("i am alive"); //working
}
}
In case, if i set preferred size i got the picture below.
So.
Question
1) Is there any patterns to not set preferred size and draw component inside Component calling paint() on contentPanel?
2) Why g.fillPolygon not working?
3) Why my ovals are not placed in one point?
Why my ovals are not placed in one point?
I would guess you are adding your Agent components to a JPanel. By default a JPanel uses a FlowLayout. So each component is placed 120 pixels apart and flow to a new line when the row is filled.
i don't know the future size of the oval or triangle, i don't want and i can't set this
Don't use the drawOval(...) method. Instead use a Shape object that represents an oval. Then you can get the size of the Shape and use this value in the getPreferredSize() method mentioned by #hovercraft.
Check out Playing With Shapes for more info on this concept. Of course if you use this concept you would need to define the Shapes as instance variable so the Shape can be referenced by both the paintComponent() and getpreferredSize() methods.
The issue is if the JPanel doesn't have a preferred size, and if it is being added to a container that uses a layout manager that doesn't fill the container (such as FlowLayout), then how will the GUI know what size the drawing JPanel should be? I've heard that better than calling setPreferredSize(...) on your JPanel is to override its Dimension getPreferredSize() method (ask kleopatra, a Swing expert on this site).
Regarding:
Why g.fillShape not working?
Check the API -- does Graphics have a fillShape method? Nope. But Graphics2D has a fill(Shape s) method, and that's what you want.
Why my ovals are not placed in one point?
Please clarify this and provide details. What do you mean by "placed in one point"? What behavior exactly are you expecting and why?
Edit: your triangle is not being drawn because all the points are co-linear!
For example:
import java.awt.*;
import javax.swing.*;
public class MyDrawingPanel extends JPanel {
private static final int PREF_W = 100;
private static final int PREF_H = PREF_W;
// private Point p1 = new Point(1, 1);
private Point p1 = new Point(30, 1);
private Point p2 = new Point(100, 101);
// private Point p3 = new Point(10, 10);
private Point p3 = new Point(50, 10);
private int[] xs = { p1.x, p2.x, p3.x };
private int[] ys = { p1.y, p2.y, p3.y };
private Polygon triangle = new Polygon(xs, ys, xs.length);
public MyDrawingPanel() {
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(new Color(255, 255, 255)); // !! never painted
g.fillPolygon(triangle); // !! never painted
g.drawOval(10, 10, 10, 10); // !!!! painted only when i set preferredSize
}
private static void createAndShowGui() {
int rows = 4;
int cols = 4;
JPanel gridPanel = new JPanel(new GridLayout(rows, cols));
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
gridPanel.add(new MyDrawingPanel());
}
}
JFrame frame = new JFrame("PaintPanel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(gridPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}

Help with simple frame and graphics

For homework, I'm trying to create a "CustomButton" that has a frame and in that frame, I draw two triangles, and a square over it. It's supposed to give the user the effect of a button press once it is depressed. So for starters, I am trying to set up the beginning graphics, drawing two triangles, and a square. The problem I have is although I set my frame to 200, 200, and the triangles I have drawn I think to the correct ends of my frame size, when I run the program, I have to extend my window to make the whole artwork, my "CustomButton," viewable. Is that normal? Thanks.
Code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class CustomButton
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
CustomButtonFrame frame = new CustomButtonFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
class CustomButtonFrame extends JFrame
{
// constructor for CustomButtonFrame
public CustomButtonFrame()
{
setTitle("Custom Button");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
CustomButtonSetup buttonSetup = new CustomButtonSetup();
this.add(buttonSetup);
}
private static final int DEFAULT_WIDTH = 200;
private static final int DEFAULT_HEIGHT = 200;
}
class CustomButtonSetup extends JComponent
{
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
// first triangle coords
int x[] = new int[TRIANGLE_SIDES];
int y[] = new int[TRIANGLE_SIDES];
x[0] = 0; y[0] = 0;
x[1] = 200; y[1] = 0;
x[2] = 0; y[2] = 200;
Polygon firstTriangle = new Polygon(x, y, TRIANGLE_SIDES);
// second triangle coords
x[0] = 0; y[0] = 200;
x[1] = 200; y[1] = 200;
x[2] = 200; y[2] = 0;
Polygon secondTriangle = new Polygon(x, y, TRIANGLE_SIDES);
g2.drawPolygon(firstTriangle);
g2.setColor(Color.WHITE);
g2.fillPolygon(firstTriangle);
g2.drawPolygon(secondTriangle);
g2.setColor(Color.GRAY);
g2.fillPolygon(secondTriangle);
// draw rectangle 10 pixels off border
g2.drawRect(10, 10, 180, 180);
}
public static final int TRIANGLE_SIDES = 3;
}
Try adding
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
to your CustomButtonSetup class.
And then do
setTitle("Custom Button");
//setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
CustomButtonSetup buttonSetup = new CustomButtonSetup();
this.add(buttonSetup);
pack();
(From the api-docs on pack():)
Causes this Window to be sized to fit the preferred size and layouts of its subcomponents.
You should get something like:
The DEFAULT_WIDTH and DEFAULT_HEIGHT that you set is for the entire frame, including borders, window titles, icons, etc. It's not the size of the drawing canvas itself. Thus, it is expected that if you draw something in a 200x200 canvas, it would not necessarily fit in a 200x200 window containing that canvas.

Categories

Resources