I created an Arraylist of objects (Circles). I added a mouseclick event and so once I click in the panel, a circle will be drown and stored in the arraylist. I am stuck and can't think of a way around my code... I would appreciate some hints that could help me solving my problem.
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
public class Circle extends JFrame
{
private JPanel panel;
private ArrayList <Circle> circle;
public static void main(String[]args)
{
setTitle("Drawing Circles");
// Set the size of the window.
setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
// Specify an action for the close button.
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// set the Frame's layout
setLayout(new BorderLayout());
// Add the panels to the frame's content pane.
add(panel, BorderLayout.CENTER);
// Display the window.
setVisible(true);
circle = new ArrayList<Circle>();
}
public Circle()
{
panel.addMouseListener(new ClickingLitener());
}
private class ClickingLitener implements MouseListener
{
public void mouseClicked(MouseEvent e)
{
circle.add(new Circle());
// somehow I need to call panel.paintComponent ... right ?
}
}
}
Your Circle class extends JFrame. You can't add or paint a JFrame on a JPanel so your basic approach is flawed.
If you want to do custom painting to draw a Circle on a panel then you need to store information about the Circle that you want to draw. Then in the paintComponent() method of the panel you iterate through the ArrayList and paint all the circles. When you add a new Circle to the ArrayList you just invoke repaint() on the panel.
Check out Custom Painting Approaches for working examples of this approach. The code adds "Rectangles" to the panel, but you should be able to change the code easily enough to add circles.
Related
I'm practising to draw a shape on a JPanel by clicking on a Jbutton, but I cannot. It's been five hours that I'm surfing the web, but I cannot find the way to do it.
This is what I want to do: if I click on "Rectangle" button a rectangle appears under the buttons and if I click on "Circle" button a circle appears.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
class Shape extends JFrame {
JButton rec, circle;
static String botSelected;
Shape (){
frameSet ();
}
void frameSet(){
JFrame frame = new JFrame();
frame.setVisible(true);
frame.setSize(600,300);
rec = new JButton ("Rectangle");
circle = new JButton("Circle");
JPanel panel = new JPanel();
frame.add(panel);
panel.add(rec);
panel.add(circle);
Click clk = new Click();
rec.addActionListener(clk);
circle.addActionListener(clk);
}
public void paint (Graphics g){
super.paint(g);
if (botSelected.equals("Rectangle"))
g.fillRect(50,50,50,50);
else if (botSelected.equals("Circle"))
g.fillOval(50,50,50,50);
}
public static void main (String [] arg){
Shape s = new Shape();
}
}
class Click implements ActionListener{
public void actionPerformed (ActionEvent e){
Shape.botSelected = e.getActionCommand();
}
}
The first thing I would do is have a read through Painting in Swing and Performing custom painting to better understand how the painting process works.
Next you need to understand that JFrame is a bad choice for painting to. Why? Because it's multilayered.
A JFrame contains a JRootPane, which contains a JLayeredPane the contentPane, glassPane and the JMenuBar and in your example, it also contains a JPanel.
With the (default) exception of the glassPane, all these components are opaque.
While it's possible to have something drawn in the paint method show it, if any of the other components paint themselves, it will be wiped clean - this is because Swing components can actually be painted independently of each other, with having to have the parent paint itself first.
A better solution is to start by extending from JPanel and override its paintComponent method.
For simplicity, I'd also encourage you to implement the ActionListener against this class as well, it will allow the actionPerformed method to access the properties of the component and, in your case, call repaint to trigger a paint cycle when you want to update the UI.
Here is a derived example from your code.
As #MadProgrammer said, don't extend JFrame.
In the following example, here are the major changes :
give a non-null value to botSelected, or the first calls to paintComponent will give you a NullPointerException
the class now extends JPanel, and overrides paintComponent for custom painting
the ActionListener is an anonymous class, because you don't need a separate class, and it has direct access to the fields from Shape
botSelected is no longer static (see above point)
.
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
class Shape extends JPanel {
JButton rec, circle;
String botSelected = "";// don't let it be null, it would make paintComponent crash on startup
Shape() {
frameSet();
}
void frameSet() {
JFrame frame = new JFrame();
frame.setVisible(true);
frame.setSize(600, 300);
rec = new JButton("Rectangle");
circle = new JButton("Circle");
frame.add(this);
this.add(rec);
this.add(circle);
// anonymous class, has access to fields from the outer class Shape
ActionListener clk = new ActionListener() {
public void actionPerformed(final ActionEvent e) {
botSelected = e.getActionCommand();
repaint();
}
};
rec.addActionListener(clk);
circle.addActionListener(clk);
}
//custom painting of the JPanel
#Override
public void paintComponent(final Graphics g) {
super.paintComponent(g);
if (botSelected.equals("Rectangle")) {
g.fillRect(50, 50, 50, 50);
} else if (botSelected.equals("Circle")) {
g.fillOval(50, 50, 50, 50);
}
}
public static void main(final String[] arg) {
Shape s = new Shape();
}
}
I cant Draw two items( there will be more) to a Jframe, im trying to make a landscape, but the item painted last overwrites anything before it.
Main:
import javax.swing.*;
import java.awt.*;
public class TheComponets extends JComponent {
public static void main(String[] args){
JFrame frame = new JFrame();
frame.setSize(600, 600);
frame.setTitle("A house on the water!");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
House home = new House();
Sun sun = new Sun();
frame.setLayout(new GridLayout(2,3));
frame.add(home);
frame.add(sun);
}
}
House class:
import javax.swing.*;
import java.awt.*;
import java.applet.*;
// Program to draw a house
public class House extends JComponent
{
public void paintComponent(Graphics g)
{
// Draw the roof
g.setColor(Color.red);
int xs[] = {100,160,220};
int ys[] = {100,50,100};
Polygon poly=new Polygon(xs,ys,3);
g.fillPolygon(poly);
// Draw the body of house
g.setColor(Color.blue);
g.fillRect(100,100,120,120);
// draw the door
g.setColor(Color.orange);
g.fillRect(145,160,30,60);
}
}
Sun class:
import javax.swing.*;
import java.awt.*;
public class Sun extends JComponent {
public void paintComponent(Graphics g)
{
// draw sun
g.setColor(Color.yellow);
g.fillOval(500, 0, 50, 50);
}
}
I want the house and the sun to show up in the Jframe, but as of now it will only show the last frame.add() object. I have only been programming for two months and dont know much about Swing and awt. Please try to keep that in mind when answering.
The reason of this, is that a JFrame uses a BorderLayout by default. When you frame.add(component) without any constraints, the component will be added to BorderLayout.CENTER position. So, no matter how many components you will add without constraints, borderlayout will override the older since all of them are being added to CENTER.
The solution would be either to choose where you want your components to be added:
frame.add(home,BorderLayout.CENTER);
frame.add(sun,BorderLayout.LINE_START);
either to change the layout of your container (the JFrame in your case):
frame.setLayout(new FlowLayout());
frame.add(home);
frame.add(sun);
Worth to read: A Visual Guide to Layout Managers
Finally, do not #Override paint() method. #Override paintComponent() method instead.
When you use individual panels for custom painting then your painting will be done in 2D since components are positioned in 2D space based on the layout manager used.
im trying to make a landscape,
Then keep all you custom painting in a single class.
First you paint the background. Then you paint the sun, then the house etc.
You will now have full control over the order in which items are painted.
To Add multiple components on JFrame, need to set layout from Layout manager. it can be FlowLayout or Gridlayout or BorderLayout....
In your case,it add Last component only because JFrame's default layout is BoderLayout and can add component
frame.add(home, BorderLayout.LINE_START);
you have PAGE_START, PAGE_END, LINE_START, LINE_END, CENTER position to add component. so add second component to other position or change layout of frame using
frame.setLayout(new FlowLayout());
I have tried many solutions but I do not know what is wrong with my code.
Basicaly I want to display the shapes in the window, FlowLayout does not display anything and BorderLayout displays the last one which is not what I want. Ignore the shape.draw() method, it just prints coordinates of the shape. Shape extends JComponent.
package Shapes;
import javax.swing.*;
import java.awt.*;
/**
* Created by Matej on 10/12/2016.
*/
public class TestShapes extends JFrame
{
//static Shape[] shapes = new Shape[3];
public static void main(String[] args)
{
Shape[] shapes = new Shape[3];
shapes[0] = new Circle(300,100,20);
shapes[1] = new Rectangle(100,100,40,60);
shapes[2] = new RedRectangle(200,200,20,30);
TestShapes window = new TestShapes();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setSize(500,500);
JPanel panel = new JPanel();
panel.setLayout(new FlowLayout());
for(Shape shape:shapes)
{
shape.printName();
shape.draw();
panel.add(shape);
}
window.add(panel);
window.setVisible(true);
}
public void paint (Graphics g)
{
super.paint(g);
}
}
You've not shown us your code for your Shape class and subclass, but regardless you're doing it wrong, and the fix is to not have them extend JComponent. Rather make them logical not component classes, and pass them into a JPanel that holds them in an ArrayList<...> and that draws them in its paintComponent method override. Then add this JPanel to your JFrame's contentPane, BorderLayout.CENTER, so that it may display its contained shapes.
Note that JComponents default to a preferred size of 0,0 unless they have been given other reason not to -- such as if they hold components that have preferred size or if they have an overridden getPreferredSize() method.
But again this is moot, because you're not wanting to display each figure in its own component as this will unnecessarily limit what you can do with the images and where you can display them.
I used a border layout and I put a canvas in the center; where the main game will be, but I can't draw anything to it.
Could anyone point me in the right direction?
import java.awt.*;
import javax.swing.*;
public class TestingGraphics {
public static void main (String[] args) {
GameScene window = new GameScene();
}
}
import java.awt.*;
import javax.swing.*;
public class GameScene extends JFrame {
Canvas gameCanvas;
Graphics Pencil;
JPanel game;
public GameScene() {
game = new JPanel();
add(game);
setTitle("Yet to name this thing.");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
gameCanvas = new Canvas();
gameCanvas.setPreferredSize(new Dimension(1280, 720));
game.add(gameCanvas);
drawString();
pack();
setLocationRelativeTo(null);
setVisible(true);
}
public void drawString(Graphics Pencil) {
Pencil.drawString("boo", 100, 100);
}
}
Your problem is that you're making wild guesses on how to draw in Swing and that never works, and your errors include trying to draw directly within a JFrame, trying to call a method without passing in necessary parameters, drawing outside of any painting method.... First and foremost, go to the Swing drawing tutorials which you can find here: Swing Drawing Tutorials -- and read them.
Next, do as they tell you:
Create a class that extends JPanel
Draw in the paintComponent method override of that class, not directly in a JFrame
Be sure to call the super's paintComponent method in your overridden method.
Add your JPanel to a top-level window such as a JFrame
Display the GUI
Done.
This should be simple enough, yet I can't make it work.
I have two classes, one which should draw a circle, and the other which sets up a frame and panel with a button. On clicking the button, a circle should appear on the frame. I am confused as to why it doesn't appear. It's probably something very simple, sorry.
package ballsOnPane;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Display implements ActionListener{
private JFrame frame;
private JPanel panel;
private JButton button;
public static void main(String[] args) {
Display display = new Display();
}
public Display() {
frame = new JFrame();
frame.setSize(800, 500);
frame.setTitle("Show balls");
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
panel = new JPanel();
frame.add(panel, BorderLayout.CENTER);
button = new JButton("New Ball");
frame.add(button, BorderLayout.SOUTH);
button.addActionListener(this);
}
#Override
public void actionPerformed(ActionEvent e) {
Ball ball = new Ball(100, 100, 50);
panel.add(ball);
}
}
and ball class:
package ballsOnPane;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import javax.swing.JPanel;
public class Ball extends JPanel{
private int x;
private int y;
private int r;
public Ball(int x, int y, int r){
this.x = x;
this.y = y;
this.r = r;
}
public void paintComponent(Graphics g) {
Graphics2D g2 =(Graphics2D) g;
Ellipse2D circ = new Ellipse2D.Float(x, y, r, r);
g2.draw(circ);
}
}
When you add components to a visible GUI the basic code is:
panel.add(...);
panel.revalidate(); // to invoke the layout manager
panel.repaint(); // to repaint the components
Otherwise the size of the components added is (0, 0) so there is nothing to paint. The same goes for the button. It should be added BEFORE the frame is visible, or you need to do the revalidate()/repaint() as well.
In this case you also have a second problem:
frame.add(panel, BorderLayout.CENTER);
First you add an empty panel to the CENTER of the BorderLayout, then when you click the button you add the Ball to the CENTER.
Swing will paint the last component added first. So the ball gets painted and then the empty panel gets painted over top.
Get rid of the panel, it serves no purpose.
Edit:
For some reason I thought you were adding the Ball to the frame instead of the panel when you clicked the button.
My explanation above was correct if you did in fact add the Ball directly to the frame.
However, my explanation is incorrect since your code in the ActionListener does add the Ball to the panel. The proper explanation is below.
When you add the Ball to the panel, you don't see the Ball because by default a JPanel uses a FlowLayout and the FlowLayout respects the preferred size of any component added to it. You did not implement the getPreferredSize() method so the size is (0, 0) so there is nothing to paint.
So if you do implement the getPreferredSize() method in your Ball class the Ball will dislay on the panel. Also, you will be able to display a new Ball every time you click on the button.
As camickr answered, you need to revalidate() (or call both invalidate() & validate(), both will work) and then repaint() the panel.
The invalidate method marks the panel as being ''incorrect''; basically marking it for inspection.
Validate performs the layout of the component.
Revalidate does both, however, validate is synchronous whilst revalidate is not.
After you've validated the panel, you'll need to call repaint to redraw that panel.
As a side-note, JavaFX is replacing Swing/AWT, and in my opinon, is easier to use. You may want to look into it. :)
A short code example to do something similar to what you're currently doing, in JavaFX:
public class test extends Application{
Scene scene;
#Override
public void start(Stage primaryStage) throws Exception{
StackPane root = new StackPane();
root.setBackground(
new Background(new BackgroundFill(Color.ALICEBLUE,null,null)));
Button begin = new Button("Add Circle");
begin.setOnAction((ActionEvent e)->{
Circle c = new Circle(200,200,100,Color.RED);
root.getChildren().add(c);
});
root.getChildren().add(begin);
scene = new Scene(root, 700, 700);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}