Trouble with ActionListener and object positioning - java

The following code draws a rectangle whenever I click on the button, I want the rectangle to be drawn only once regardless of how many times the button is clicked. Also how can I position the Rectangle in the center of the frame and the button above it?
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.geom.*;
class rectangle{
public static void main(String args[]){
EventQueue.invokeLater(new Runnable(){
public void run(){
final JFrame frame=new JFrame("RECTANGLE");
final JPanel panel=new JPanel();
JButton button=new JButton("DRAW");
panel.add(button);
frame.add(panel);
frame.setSize(400,400);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event){
panel.add(new drawrectangle());
}
});
}
});
}
}
class drawrectangle extends JComponent{
public void paintComponent(Graphics g){
Graphics2D g2=(Graphics2D) g;
g2.setPaint(Color.BLACK);
Rectangle2D rect=new Rectangle2D.Double(50,50,200,200);
g2.draw(rect);
g2.fill(rect);
}
}

You can declare field
boolean firstClick = true;
Than write something like that:
public void actionPerformed(ActionEvent event){
if(firstClick){
panel.add(new drawrectangle()); }
firstClick = false;
}

To answer your first question, you can keep track of how many times the button has been pressed using a variable and incrementing it in actionPerformed() method. Then you will know how many times the button has been pressed and take action accordingly.
To answer your second question, the easier way to do it would be to use BorderLayout. Create two JPanels add button to one panel and add it to NORTH and add the second panel to CENTRE. Then when you press the button you can add the rectangle to the panel in the CENTRE.
The difficult approach but more precise way of doing it is to manually place the button and rectangle by removing the layout manager (panel.setLayout(null)), and then specifying the size and location of all components. You also will then have to keep track of change in window state etc.
Still another way is to use GridBagLayout, which is a great balance between the first and second approach.

The following code draws a rectangle whenever I click on the button, I want the rectangle to be drawn only once regardless of how many times the button is clicked. Also how can I position the Rectangle in the center of the frame and the button above it?
No the code you posted dont draw the rectangle at all when you click the button.
Just count how often your button got clicked and if it is the first time you add your button to the JPanel.
You can use BorderLayout to position the Rectangle in the center and the button above it.

Related

Java repaint() method issue. Repainting JPanel will disappear painted components in JFrame

I created a separate class for JFrame and JPanel, then draw (fillOval in a JFrame class) and draw (fillOval in a JPanel class), and a button that will just animate the JPanel components. But the problem is, whenever i repaint the JPanel class; ---- The JFrame components disappeared. I don't understand why is this happening. I want the JFrame component be permanent for every animation done in JPanel class.
Sample Code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class TryRepaintIssue extends JFrame
{
public TryRepaintIssue(){
thePanel panel = new thePanel();
add(panel);
setSize(1000,1000);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
public void paint(Graphics g){
super.paint(g);
g.fillOval(100,500,100,100);
}
public static void main(String[] args){
new TryRepaintIssue();
}
public static class thePanel extends JPanel{
private int y = 100, vector = 1;
public thePanel(){
JButton button = new JButton("Play");
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ae){
y += vector;
repaint();
}
});
add(button);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.red);
g2.fillOval(100,y,100,100);
}
}
}
The JFrame components disappeared.
The components do not disappear. The button and panel are still displayed.
I assume you mean the custom painting of the black circle disappears.
I don't understand why is this happening
The paint() method of the frame is responsible for painting all the child components of the frame. So it repaints the JPanel you add to the frame, which in turn paints the JButton you add to the panel.
It then paints the black circle on top of the panel.
When you click the button you repaint only the "panel" which causes the JButton and the red circle to be painted.
You lose the painting of the black circle because you no longer invoke the code to paint that circle.
If you want the black circle to remain you have a couple of options:
The best solution is to NOT override paint() on the frame. Instead do all the custom painting in your panel. So paint both the black and red circles.
repaint the entire frame in your ActionListener code:
//repaint();
SwingUtilities.windowForComponent(button).repaint();
use the Glass Pane as suggested in the answer by Tom.
You shouldn't override JFrame.paint, particularly without calling the super. Usually drawing in these situations is done on a glass pane.

Add drawing to a Panel

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);
}
}

JFrame's Border Layout Positioning Not as Expected

I'm trying to create a Java Program that does this,
A Frame with
NORTH ;Description:Two Button Listener
CENTRE:Description: Click Button1 to change solidcolor circle and Button2 to change gradient circle
SOUTH: I want 2 buttons ,Button 1 and Button 2
WEST: A Circle with a gradient colour
EAST:A circle with a solid colour
It's fairly Simple , when I click a button the respective circle's colour should change
The code is as follows ..
import javax.swing.*;//for the frame etc
import java.awt.*;//for paintComponent(),Grapics Object etc
import java.awt.event.*;//for listeners
class CircleGradientColor extends JPanel
{
public void paintComponent(Graphics g)
{
Graphics2D g2d=(Graphics2D)g;//cast
int r1=(int)(Math.random()*255);
int g1=(int)(Math.random()*255);
int b1=(int)(Math.random()*255);
int r2=(int)(Math.random()*255);
int g2=(int)(Math.random()*255);
int b2=(int)(Math.random()*255);
Color startcolor=new Color(r1,g1,b1);
Color endcolor=new Color(r2,g2,b2);
GradientPaint gradient=new GradientPaint(10,10,startcolor,70,70,endcolor);
g2d.setPaint(gradient);//here it aint set color for a Graphics2D object
g2d.fillOval(10,10,60,60);//fills with the Current PaintBrushColor
}
}
class CircleSolidColor extends JPanel//this class will contain code for the circle
{
public void paintComponent(Graphics g)
{
int r=(int)(Math.random()*255);//generate random float between 0 & 255
int b=(int)(Math.random()*255);//generate random float between 0 & 255
int gr=(int)(Math.random()*255);//generate random float between 0 & 255
Color randcolor=new Color(r,gr,b);//name clashed with Graphics g
g.setColor(randcolor);
g.fillOval(10,10,60,60);
}
}
public class TwoButtonGui
{
JFrame frame;
CircleSolidColor circlesolidcolor;
CircleGradientColor circlegradientcolor;
JButton b1;
JButton b2;
JLabel toplabel;
JLabel centerlabel;
public static void main(String[] args)
{
TwoButtonGui twobuttongui=new TwoButtonGui();
twobuttongui.go();
}
public void go()
{
frame=new JFrame();
toplabel=new JLabel("Example of Multiple Action Listeners");
centerlabel=new JLabel("Click The respective Button to change circle Color");
b1=new JButton("Click to change solid");
b2=new JButton("Click to change Gradient");
b1.addActionListener(new CircleSolidColorListener());
b2.addActionListener(new CircleGradientColorListener());
circlesolidcolor=new CircleSolidColor();
circlegradientcolor=new CircleGradientColor();
frame.setSize(1000,1000);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(BorderLayout.NORTH,toplabel);
frame.getContentPane().add(BorderLayout.SOUTH,b1);
frame.getContentPane().add(BorderLayout.SOUTH,b2);
//if i add 2 SOUTH position i though first will go left and second right
//IS it so
frame.getContentPane().add(BorderLayout.CENTER,centerlabel);
frame.getContentPane().add(BorderLayout.WEST,circlesolidcolor);
frame.getContentPane().add(BorderLayout.EAST,circlegradientcolor);
frame.setVisible(true);
}
//inner class for solidlistener
class CircleSolidColorListener implements ActionListener
{
public void actionPerformed(ActionEvent e1)
{
circlesolidcolor.repaint();
}
}
class CircleGradientColorListener implements ActionListener
{
public void actionPerformed(ActionEvent e2)
{
circlegradientcolor.repaint();
}
}
}//main class ends
But the output I get is rather absurd
Where am I going Wrong , I know it can be corrected using multiple Panels and LayoutMangaers etc but is there a way to get the results I described without all that?
I wrote the program as a variant of an example in Headfirst Java which seems to work just fine with (2 buttons , a label and a Circle ), so why doesn't this work , can I add 2 components in a single position like say BorderLayout.SOUTH as described in my comments,
Thanks!
North, is correct.
South, adding b2 will overwrite b1, what you want to do is create a new container like JPanel and add b1 to b2 to that container. Then add this container to south. This new container can use GridLayout(1,2)
Center, is correct.
East and west, the components are 'blank' so the default size giving by layout manager is none ( painting in the container background != isNotBlank() ). You can however use circles*.setPrefferredSize(dimension) to force a size on them.

How repaint method differs for a Jpanel and Jframe?

i have the following code for animating a ball from top left corner towards the bottom right corner.
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
class MainFrame{
int i=0,j=0;
JFrame frame = new JFrame();
public void go(){
Animation anim = new Animation();
anim.setBackground(Color.red);//Why color is not changing to red for the panel.
frame.getContentPane().add(anim);
frame.setVisible(true);
frame.setSize(475,475);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
for(i=0,j=0;i<frame.getHeight()&&j<frame.getWidth();++i,++j){
anim.repaint();//Main problem is here,described below.
try{
Thread.sleep(50);
}
catch(Exception ex){}
}
}
public static void main(String[] args) {
MainFrame mf = new MainFrame();
mf.go();
}
class Animation extends JPanel{
public void paintComponent(Graphics g){
Graphics2D g2d = (Graphics2D)g;
g.fillOval(i,j,25,25);
}
}
}
Questions
When i do anim.repaint() inside the method go i don't get the ball animating from top left corner to bottom right corner but it gets smeared down the path.But if i replace it with frame.repaint() i get the desired result that is a moving ball.So what is the difference between these two calls to repaint?
Why the color of panel is not changing after anim.setBackground(Color.red); in go method?
If you run this programe you will find that the ball is not exactly going at the bottom edge,so how can i acheive that?
);//Why color is not changing to red for the panel
You should always invoke super.paintComponent(g) when you override the paintComponent(...) method. The default code is responsible for painting the background.
but it gets smeared down the path
Same answer as above. You need the background to be painted so that all the old paintings are removed.
the ball is not exactly going at the bottom edge
If you mean the ball is not on an exact diagonal on the panel, that is because you set the size of the frame manually and you did not account for the size of the titlebar and borders. If you want the panel to be (475, 475) then override the getPreferredSize() method of the panel to return that dimension. Then in your code you replace the frame.setSize() with frame.pack().

Rectangle not showing up

Nothing happens on pressing the Button, what I am doing wrong?? I want the Rectangle to be drawn on pressing the button. What's wrong with the code??
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.geom.*;
class rectangle{
public static void main(String args[]){
EventQueue.invokeLater(new Runnable(){
public void run(){
final JFrame frame=new JFrame("RECTANGLE");
final JPanel panel=new JPanel();
JButton button=new JButton("DRAW");
panel.add(button);
frame.add(panel);
frame.setSize(400,400);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event){
panel.add(new drawrectangle());
}
});
}
});
}
}
class drawrectangle extends JComponent{
public void paintComponent(Graphics g){
Graphics2D g2=(Graphics2D) g;
g2.setPaint(Color.PINK);
Rectangle2D rect=new Rectangle2D.Double(50,50,200,200);
g2.draw(rect);
g2.fill(rect);
}
}
You need to call repaint() on your JFrame.
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event){
drawrectangle rec= new drawrectangle();
panel.add(rec);
rec.paintComponent();
frame.repaint();
}
});
Simply write :
frame.validate();
frame.repaint();
Your component has no size. You need to either override getPreferredSize() and return the size of your rectangle, or else you need to set its size in its constructor by calling setSize(). The reason your rectangle doesn't appear is because it is drawing outside of its area, and the Java 2D graphics are clipping the portion you are drawing outside the component (that is, all of it).
It is not the case that you need to either call repaint() or validate() because when you add the rectangle to the panel, it will automatically lay itself out again and mark itself as needing a repaint.
However, be aware that you are adding a new rectangle every time the event fires. Is that what you want? Also, rectangle and drawrectangle should begin with capital letters to conform to the normal Java naming conventions, although there is also a java.awt.Rectangle class, so Rectangle might not be the best name to use.
Just add window.visibility to true.

Categories

Resources