Reputation: 15
I'm just learning how GUI works and I wanted to write a code where following happens:
The problem is, I don't know how to notify the change when I use repaint()
, I tried creating the first picture with another method - fail, maybe I lack some knowledge. Currently we get just the second output that doesn't change after the click.
This is the code at the moment I got stuck:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class SimpleGUI implements ActionListener {
JButton button;
JFrame frame;
public void work() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
button = new JButton("Color change");
button.addActionListener(this);
mojpanel panel = new mojpanel();
frame.getContentPane().add(BorderLayout.SOUTH, button);
frame.getContentPane().add(BorderLayout.CENTER, panel);
frame.setSize(300,300);
frame.setVisible(true);
}
public void actionPerformed(ActionEvent event) {
frame.repaint();
}
}
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import javax.swing.*;
import java.awt.Graphics2D;
public class mojpanel extends JPanel {
public void paintComponent(Graphics g) {
g.setColor(Color.black);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
GradientPaint gradient = new GradientPaint(70,70, Color.orange, 150,150, Color.pink);
((Graphics2D) g).setPaint(gradient);
g.fillOval(100, 100, 100, 100);
}
}
import javax.swing.JFrame;
public class Test {
public static void main(String[] args) {
SimpleGUI aplGUI = new SimpleGUI();
JFrame frame = new JFrame();
mojpanel panel = new mojpanel();
frame.add(panel);
aplGUI.work();
}
}
import java.awt.*;
public class Painting extends SimpleGUI {
public void paint(Graphics g) {
g.setColor(Color.red);
g.drawRect(100, 100, 100, 100);
g.fillRect(100, 100, 100, 100);
}
}
Upvotes: 0
Views: 1068
Reputation: 298153
When you create new classes, you short invest more thought into them. You have two classes which are supposed to be interchangeable, one painting a red rectangle, the other painting a circle with a color gradient, with the ability to switch from the former to the latter.
So when you have one class extending JPanel
and the other extending SimpleGUI
, there is no way to exchange one for the other. Further, the names mojpanel
and Painting
do not reflect their purposes.
Besides that, you have it backward. Don’t implement an action that calls repaint()
, followed by an attempt to recognize that repaint()
has been called, to modify the GUI afterwards. Instead, change the GUI’s state and after the GUI has changed in a way that the visual appearance needs to be updated, call repaint
. Note that most properties of the Swing components do already trigger an according repaint automatically when you change them.
You may create two classes extending JComponent
having a custom paintComponent
method and replace one with the other when the action has been triggered. But there’s a less intrusive way. Let the classes implement Icon
and set the icon property of a component, e.g. a JLabel
. This is one of the properties that will trigger the painting automatically:
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class SimpleGUI {
static class GradientOval implements Icon {
@Override
public void paintIcon(Component c, Graphics g, int x, int y) {
GradientPaint gradient
= new GradientPaint(70,70, Color.orange, 150,150, Color.pink);
((Graphics2D)g).setPaint(gradient);
g.fillOval(100, 100, 100, 100);
}
@Override
public int getIconWidth() {
return 200;
}
@Override
public int getIconHeight() {
return 200;
}
}
static class RedRectangle implements Icon {
@Override
public void paintIcon(Component c, Graphics g, int x, int y) {
g.setColor(Color.RED);
g.fillRect(100, 100, 100, 100);
}
@Override
public int getIconWidth() {
return 200;
}
@Override
public int getIconHeight() {
return 200;
}
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel content = new JLabel(new RedRectangle());
JButton button = new JButton("Change To Circle");
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
content.setIcon(new GradientOval());
}
});
frame.getContentPane().add(BorderLayout.SOUTH, button);
frame.getContentPane().add(BorderLayout.CENTER, content);
frame.setSize(300, 300);
frame.setVisible(true);
}
}
I don’t know which level of Java knowledge you have. The code
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
content.setIcon(new GradientOval());
}
});
creates an instance of an anonymous inner class implementing ActionListener
. You can simplify this code using a lambda expression:
button.addActionListener(e -> content.setIcon(new GradientOval()));
To demonstrate the interaction between component properties and repaints, here an approach using a custom component:
import java.awt.*;
import javax.swing.*;
public class SimpleGUI {
static class DualAppearance extends JComponent {
private boolean first = true;
public boolean isFirst() {
return first;
}
public void setFirst(boolean shouldBeFirst) {
if(shouldBeFirst != first) {
first = shouldBeFirst;
repaint();
}
}
public void next() {
if(first) {
first = false;
repaint();
}
}
@Override
protected void paintComponent(Graphics g) {
if(first) {
g.setColor(Color.RED);
g.fillRect(100, 100, 100, 100);
}
else {
GradientPaint gradient
= new GradientPaint(70,70, Color.orange, 150,150, Color.pink);
((Graphics2D)g).setPaint(gradient);
g.fillOval(100, 100, 100, 100);
}
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
@Override
public Dimension getMinimumSize() {
return new Dimension(200, 200);
}
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DualAppearance content = new DualAppearance();
JButton button = new JButton("Change To Second");
button.addActionListener(e -> content.next());
frame.getContentPane().add(BorderLayout.SOUTH, button);
frame.getContentPane().add(BorderLayout.CENTER, content);
frame.setSize(300, 300);
frame.setVisible(true);
}
}
This DualAppearance
component follows the usual pattern. When being requested to paint itself, it will always paint itself according to the current state. This may happen multiple times without a state change, due to other reasons, e.g. being requested by the system. When its own state changes and requires a repaint, which only this component can know, it will invoke repaint
.
You can easily modify this example code to toggle between both appearances by replacing
JButton button = new JButton("Change To Second");
button.addActionListener(e -> content.next());
with
JButton button = new JButton("Toggle");
button.addActionListener(e -> content.setFirst(!content.isFirst()));
Upvotes: 2