I want to repaint a circle whenever a button is pressed.
Currently, I have it whenever I press a button, it prints to the console what button I pressed. For example, if I press the "Paint Red" button, I want it to fill the circle with red, and the same with the other colors as well. I'm trying to wrap my head around the whole paint/paintComponent difference.
This is what I have so far...
public class testCircle extends JPanel {
public void paint(Graphics g)
{
setSize(500,500);
int R = (int) (Math.random( )*256);
int G = (int)(Math.random( )*256);
int B= (int)(Math.random( )*256);
Color randomColor = new Color(R, G, B);
g.setColor(randomColor);
g.drawOval(75, 100, 200,200);
g.fillOval(75, 100, 200, 200);
}
public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.setSize(400, 400);
testCircle circlePanel = new testCircle();
frame.add(circlePanel);
JButton redButton = new JButton("Paint Red");
redButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event)
{
System.out.println("Red Button Pressed!");
}
});
JButton blueButton = new JButton("Paint Blue");
blueButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event)
{
System.out.println("Blue Button Pressed!");
}
});
JButton greenButton = new JButton("Paint Green");
greenButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event)
{
System.out.println("Green Button Pressed!");
}
});
redButton.setPreferredSize(new Dimension(100,100));
blueButton.setPreferredSize(new Dimension(100,100));
greenButton.setPreferredSize(new Dimension(100,100));
frame.setLayout(new FlowLayout());
frame.add(redButton);
frame.add(blueButton);
frame.add(greenButton);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Consider these changes to your code:
As discussed here, Swing programs should override paintComponent() instead of overriding paint().
Give your panel an attribute for currentColor.
private Color currentColor;
Let each button's ActionListener set currentColor and invoke repaint().
currentColor = color;
repaint();
Use Action for to encapsulate your program's functionality.
A complete example is examined here.
You don't trigger a "repaint" anywhere in your code. That should work:
redButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
System.out.println("Red Button Pressed!");
frame.invalidate();
frame.validate();
}
});
Related
I'm new to java and I'm trying to figure out how action listeners and buttons work. I've found that I can get a working JButton if I put it directly into my JFrame object. But if I put it in a JPanel within that JFrame, it won't respond. Why is that?
Main.java
public class Main {
private static Frame f = new Frame();
public static void main(String[] args) {}
}
Frame.java
public class Frame extends JFrame {
private final int WIDTH = 640, HEIGHT = 480;
private Panel p = new Panel();
Frame() {
super("Java Program");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(WIDTH, HEIGHT);
this.setLayout(null);
this.setLocationRelativeTo(null);
this.setVisible(true);
}
public void paint(Graphics g) {
super.paint(g);
p.paintComponent(g);
}
}
Panel.java
public class Panel extends JPanel {
JButton b = new JButton("Button");
Panel() {
b.setBounds(0, 0, 200, 100);
add(b);
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
b.setText("Pressed");
}
});
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
b.paint(g);
}
}
I am not a Swing expert so I can't really explain why it does not work. It seems like an unresponsive button is painted on top of you button. I tweaked it a little and here are a few modifications to get it to work:
Add the panel to the Frame: add(p);
Remove the this.setLayout(null); line, it seems to mess up the frame
To set the size of the Frame, use setPreferredSize: this.setPreferredSize(new Dimension(WIDTH, HEIGHT));
You also need to call pack() at then end of your Frame constructor.
And you need to remove b.paint(g) from your Panel.paintComponent(), this seems to be what paints the "unresponsive" button, (see image at the end of the answer).
Optionally, you can remove the paint() from the Frame, it does nothing more than the JFrame's one
Here is a modified working version:
class Frame extends JFrame {
private final int WIDTH = 640, HEIGHT = 480;
private Panel p = new Panel();
Frame() {
super("Java Program");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setPreferredSize(new Dimension(WIDTH, HEIGHT));
this.setLocationRelativeTo(null);
this.setVisible(true);
// add the panel to the frame
add(p);
pack();
}
}
class Panel extends JPanel {
JButton b = new JButton("Button");
Panel() {
b.setBounds(0, 0, 200, 100);
add(b);
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
b.setText("Pressed");
}
});
}
// You can also get rid of this method,
// I just leave it here to show that I removed the b.paint(g) line
public void paintComponent(Graphics g) {
super.paintComponent(g);
}
}
Here is what the same code shows if you leave b.paint(g) in Panel.paintComponent(), as you can see there are 2 buttons, the one in the corner does not work.
I want my GUI to draw circles/rectangles on the exact position I coded in the method paintComponent when I click on the respective buttons.
But I just don't know how to go on. What should I tell actionPerformed to do? Trying for a few hours to figure out a way, but I'm only getting errors.
public class Kreise extends JFrame {
Kreise() {
setLayout(new GridLayout(2, 1));
JLabel label = new JLabel("Draw Circ / Rect here: ");
label.setLayout(new FlowLayout(FlowLayout.CENTER));
JPanel jp1 = new JPanel();
jp1.setBackground(Color.LIGHT_GRAY);;
jp1.add(label);
JPanel jp2 = new JPanel(new FlowLayout());
JButton circ = new JButton("Circle");
JButton rect = new JButton("Rectangle");
circ.addActionListener(new KRListener(true));
rect.addActionListener(new KRListener(false));
jp2.add(circ);
jp2.add(rect);
MyPanel obj = new MyPanel();
jp1.add(obj);
add(jp1);
add(jp2);
setSize(400, 250);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
public class MyPanel extends JPanel {
public boolean circleZ = true;
public void paintComponent(Graphics g) {
if (circleZ = true) {
super.paintComponent(g);
g.drawOval(150, 50, 50, 50);
} else if (circleZ = false) {
super.paintComponent(g);
g.drawRect(150, 50, 50, 50);
}
}
}
public class KRListener implements ActionListener {
boolean b;
KRListener(boolean b) {
this.b = b;
}
public void actionPerformed(ActionEvent e) {
?
}
}
public static void main(String[] args) {
new Kreise();
}
}
Presuming I understand the question clearly (you wish to toggle between the a rectangle or circle), in the ActionListener implementation you need to:
Toggle the appropriate boolean value
Call repaint on the JPanel instance that performs the painting
One way to accomplish these steps is to have a single toggle JButton, and pass an instance of the JPanel used for drawing to your ActionListener implementation, which can be used to accomplish both steps above:
public class KRListener implements ActionListener {
private MyPanel panel;
KRListener(MyPanel panel) {
this.panel = panel;
}
#Override
public void actionPerformed(ActionEvent e) {
panel.circleZ = !panel.circleZ;
panel.repaint();
}
}
And when you paint:
if ( circleZ ){
g.drawOval(150, 50, 50, 50);
}else{
g.drawRect(150, 50, 50, 50);
}
I dont know what you are using the global boolean variable b for But I noticed that you have to call the repaint() method when you press the Button.
public class KRListener implements ActionListener {
boolean b;
KRListener(boolean b) {
this.b = b;
}
#Override
public void actionPerformed(ActionEvent e){
//add some code here to change properties of the drawing before calling the repaint method?
repaint();
}
}
I want to ask what function or another have to write so that every time I pressed the Start button (function:addbutton), the other button to exit (function:addButton2) to change its title to how many times I press the start?
class DroppingFrame extends JFrame {
public DroppingFrame() {
int clicked=0;
String b="a";
setSize(1400, 700);
setTitle("Dropping");
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
Container contentPane = getContentPane();
canvas = new JPanel();
contentPane.add(canvas, "Center");
JPanel p = new JPanel();
addButton(p, "Drop ball", clicked, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
//addButton.setText(String.valueOf(++clicked));
Ball b = new Ball(canvas);
// if(b== new Ball(canvas)){
// clicked++;
// }
b.start();
}
});
addButton2(p, b, clicked, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
canvas.setVisible(false);
System.exit(0);
}
});
contentPane.add(p, "South");
}
public void addButton(Container c, String title, int i, ActionListener a) {
//i++;
//title = Integer.toString(i);
JButton b = new JButton(title);
c.add(b);
b.addActionListener(a);
}
public void addButton2(Container c, String title, int i, ActionListener a ) {
i++;
title = Integer.toString(i);
JButton b = new JButton(title);
c.add(b);
b.addActionListener(a);
}
private JPanel canvas;
}
My preference would be:
Make clicked a class field - that way you can access it and mutate it inside the event handler
Create a class field for a button
Refactor the addButton method so there is only one of them - this makes your code tidier:
Change the method return type to return the button created, then you decide if you store it or not from the caller. This just smells nicer.
The code looks like:
class DroppingFrame extends JFrame {
final JPanel canvas = new JPanel();
JButton button2;
int clicked = 0;
public DroppingFrame() {
setSize(1400, 700);
setTitle("Dropping");
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
Container contentPane = getContentPane();
contentPane.add(canvas, "Center");
JPanel p = new JPanel();
addButton(p, "Drop ball", clicked, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
Ball b = new Ball(canvas);
b.start();
button2.setText(String.valueOf(++clicked));
}
});
button2 = addButton(p, String.valueOf(clicked), clicked, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
canvas.setVisible(false);
System.exit(0);
}
});
contentPane.add(p, "South");
}
public JButton addButton(Container c, String title, int i, ActionListener a) {
JButton b = new JButton(title);
c.add(b);
b.addActionListener(a);
return b;
}
}
It requires the reference of the second button so that start button can update its text.
Keep it simple:
Use setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); instead of adding WindowListener to close the window.
Use JFrame#dispose to close the JFrame programmatically.
Favor Composition over Inheritance It means if you are not overriding any logic/implementation of the existing class then don't extend it.
There is no meaning of creating separate method for adding each component. Either make the method generic or simply remove it.
Sample code:
public class DroppingFrame {
private int clicked = 0;
public DroppingFrame() {
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container contentPane = frame.getContentPane();
JPanel p = new JPanel();
final JButton btn2 = new JButton(String.valueOf(clicked));
btn2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
frame.dispose();
}
});
JButton btn1 = new JButton("Drop ball");
btn1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
btn2.setText(String.valueOf(++clicked));
}
});
p.add(btn1);
p.add(btn2);
contentPane.add(p, BorderLayout.SOUTH);
frame.setVisible(true);
}
}
It's not correct.. addButton always creates a new button and its reference is lost.. you should make it class-scoped.
So make a class variable:
JButton button = new JButton();
Change your function:
public void addButton(Container c, String title, int i, ActionListener a) {
button.setText(title);
c.add(button);
button.addActionListener(a);
}
And also change your actionPerformed override:
public void actionPerformed(ActionEvent evt) {
button.setText(String.valueOf(++clicked));
....
});
I've created a modal dialog inside a JFrame using the glass pane. My display method is quite simple: it creates a JPanel as glass pane with some alpha background and adds the JLabel and an ok and close button. Then the glass pane is set and displayed via frame.getGlassPane().setVisible(true);.
Everything works fine: if I call the method the pane is displayed and I can click ok or cancel and the glass pane hides. But the method returns directly after showing the glass pane. But I want it to behave like the JOptionPane methods: they block until the dialog is closed.
But everytime I'm trying to insert any kind of busy waiting at the end of my show method the GUI is frozen if I click the open button. I've also tried to get the mechanism from JDialog#show() but that's a bit to complicated for me.
So how to block the show method while the glass pane is visible?
Here is a simple example:
public class GlassPaneSSCE extends JPanel {
private JFrame parentFrame;
public GlassPaneSSCE(JFrame parent) {
parentFrame = parent;
addKeyListener(new KeyAdapter() {});
addMouseListener(new MouseAdapter() {});
setBackground(new Color(0, 0, 0, 100));
initGui();
}
#Override
protected void paintComponent(Graphics g) {
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());
super.paintComponent(g);
}
private void initGui() {
setLayout(new FlowLayout(FlowLayout.CENTER));
setOpaque(false);
final JPanel content = new JPanel(new BorderLayout(4, 4));
content.setOpaque(true);
content.setBorder(new EmptyBorder(8, 8, 8, 8));
JLabel top = new JLabel("Title of this little modal dialog");
content.add(top, BorderLayout.NORTH);
JPanel inner = new JPanel(new BorderLayout());
content.add(inner, BorderLayout.CENTER);
inner.add(new JScrollPane(new JList(new String[] {
"Item 1 ",
"Item 2", "Item 3"
})));
Box ctrlButtons = Box.createHorizontalBox();
ctrlButtons.setBorder(new EmptyBorder(0, 4, 4, 4));
ctrlButtons.add(Box.createHorizontalGlue());
ctrlButtons.add(new JButton(new AbstractAction("OK") {
#Override
public void actionPerformed(ActionEvent e) {
parentFrame.getGlassPane().setVisible(false);
parentFrame.setGlassPane(new JPanel());
}
}));
content.add(ctrlButtons, BorderLayout.SOUTH);
add(content);
}
public void display() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
parentFrame.setGlassPane(GlassPaneSSCE.this);
parentFrame.getGlassPane().setVisible(true);
// Set the focus on the glass pane
requestFocus();
setFocusCycleRoot(true);
}
});
// The next line should be executed only if
// the ok button is clicked and not before
System.out.println("End of display()");
}
public static void main(String[] args) {
final JFrame f = new JFrame();
f.setLayout(new FlowLayout(FlowLayout.CENTER));
JTextArea tp = new JTextArea(10, 10);
for (int i = 0; i < 20; i++) {
JButton b = new JButton("Open");
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
GlassPaneSSCE gp = new GlassPaneSSCE(f);
gp.display();
}
});
f.add(b);
tp.append("Item " + (i+1) + "\n");
}
f.add(new JScrollPane(tp));
f.setSize(600, 600);
f.setVisible(true);
}
}
I made a program to draw shapes and change their colors. I got the reset button to reset to default shapes and colors but can not get it to clear the shapes from the canvas. How do I do that? Here is my code:
// Color Clear choice box
Choice colorChoice;
// the canvas
DrawCanvas canvas;
/**
* Constructor
*/
public Draw() {
super("Java Draw");
setLayout(new BorderLayout());
// create panel for controls
Panel topPanel = new Panel(new GridLayout(3, 0));
add(topPanel, BorderLayout.NORTH);
// create button control
Panel buttonPanel = new Panel(new FlowLayout(FlowLayout.LEFT));
topPanel.add(buttonPanel);
circle = new Button("Circle");
buttonPanel.add(circle);
roundRec = new Button("Rounded Rectangle");
buttonPanel.add(roundRec);
threeDRec = new Button("3D Rectangle");
buttonPanel.add(threeDRec);
// add button listener
circle.addActionListener(this);
roundRec.addActionListener(this);
threeDRec.addActionListener(this);
Panel buttonPanel1 = new Panel(new FlowLayout(FlowLayout.LEFT));
topPanel.add(buttonPanel1);
lines = new Button("Lines");
buttonPanel1.add(lines);
squares = new Button("Square");
buttonPanel1.add(squares);
ovals = new Button("Ovals");
buttonPanel1.add(ovals);
lines.addActionListener(this);
squares.addActionListener(this);
ovals.addActionListener(this);
// create panel for color choices
Panel colorPanel = new Panel(new FlowLayout(FlowLayout.LEFT));
topPanel.add(colorPanel);
Label label = new Label("Filled Color:");
colorPanel.add(label);
colorChoice = new Choice();
for(int i=0; i<COLOR_NAMES.length; i++) {
colorChoice.add(COLOR_NAMES[i]);
}
colorPanel.add(colorChoice);
colorChoice.addItemListener(this);
// create reset button
Button resetButton = new Button("Reset");
colorPanel.add(resetButton);
resetButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
colorChoice.select(0); // reset color choice box
canvas.setFilledColor(COLORS[0]); // reset color used
canvas.setShape(DrawCanvas.CIRCLE); // reset shape
}
});
// create the canvas
canvas = new DrawCanvas();
add(canvas, BorderLayout.CENTER);
}// end of constructor
/**
* Implementing ActionListener
*/
public void actionPerformed(ActionEvent event) {
if(event.getSource() == circle) { // circle button
canvas.setShape(DrawCanvas.CIRCLE);
}
else if(event.getSource() == roundRec) { // rounded rectangle button
canvas.setShape(DrawCanvas.ROUNDED_RECTANGLE);
}
else if(event.getSource() == threeDRec) { // 3D rectangle button
canvas.setShape(DrawCanvas.RECTANGLE_3D);
}
}
/**
* Implementing ItemListener
*/
public void itemStateChanged(ItemEvent event) {
Color color = COLORS[colorChoice.getSelectedIndex()];
canvas.setFilledColor(color);
}
/**
* the main method
*/
public static void main(String[] argv) {
// Create a frame
Draw frame = new Draw();
frame.setSize(WIDTH, HEIGHT);
frame.setLocation(150, 100);
// add window closing listener
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent event) {
System.exit(0);
}
});
// Show the frame
frame.setVisible(true);
}
}
Assuming a DrawCanvas extends JPanel, you need to call repaint at the end of your reset listener.