Reputation: 333
here's the relevant code from the form where JComponents should be drawn - this code creates and adds to the form a JComponent and also adds it to an ArrayList, as I need to be able to destroy the components later.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JOptionPane;
public class frmMain extends javax.swing.JFrame implements ActionListener {
ArrayList<Pocitadlo> poc;
Random rnd;
/**
* Creates new form frmMain
*/
public frmMain() {
initComponents();
poc = new ArrayList<Pocitadlo>();
rnd = new Random();
// Pocitadlo tmp = new Pocitadlo(100, 40, 40, getFont());
// tmp.addActionListener(this);
// this.add(tmp);
// tmp.start();
}
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
int val, x, y;
val = 20;
x = (this.getWidth() / 2) - 10 + rnd.nextInt(20);
y = (this.getHeight() / 2) - 10 + rnd.nextInt(20);
try{
val = Integer.parseInt(txtCounterValue.getText());
Pocitadlo tmp = new Pocitadlo(val, x, y, getFont());
tmp.addActionListener(this);
poc.add(tmp);
tmp.setVisible(true);
tmp.start();
this.add(tmp);
this.setTitle("Počet počítadiel: " + this.getComponentCount());
repaint();
tmp.repaint();
} catch(Exception e){
JOptionPane.showMessageDialog(this, "Nesprávne zadaná alebo prázdna hodnota počítadla. Hodnota musí byť celé číslo.", "Chyba", JOptionPane.ERROR_MESSAGE);
}
}
@Override
public void actionPerformed(ActionEvent e){
if(e.getActionCommand().equals("counterFinished")){
Pocitadlo tmp = (Pocitadlo)e.getSource();
poc.remove(tmp);
this.setTitle("Počet počítadiel: " + poc.size());
this.remove(tmp);
}
}
and here is the code of the JComponents i'm adding and naiively expect to be drawn:
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JComponent;
import javax.swing.Timer;
public class Pocitadlo extends JComponent implements MouseListener, ActionListener {
private int maxValue, value;
private Boolean isRunning, isDisplayed;
private Timer valueTimer, blinkTimer;
private ActionListener actionListener;
private Font font;
public Pocitadlo(int timeout, int x, int y, Font f){
isRunning = false;
isDisplayed = true;
maxValue = value = timeout;
valueTimer = new Timer(1000, this);
valueTimer.setActionCommand("valueTimer");
blinkTimer = new Timer(200, this);
blinkTimer.setActionCommand("blinkTimer");
this.setBounds(x, y, 100, 50);
}
public void start(){
isRunning = true;
valueTimer.start();
}
public void stop(){
isRunning = false;
valueTimer.stop();
}
@Override
public void actionPerformed(ActionEvent e){
if(e.getActionCommand().equals("valueTimer")){
value--;
if(actionListener != null){
repaint();
actionListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "counterTick"));
}
if(value == 0 && actionListener != null){
isRunning = false;
valueTimer.stop();
actionListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "counterFinished"));
}
if(value < maxValue / 2 && !blinkTimer.isRunning()) blinkTimer.start();
}
if(e.getActionCommand().equals("blinkTimer")){
isDisplayed = !isDisplayed;
repaint();
}
}
public void addActionListener(ActionListener listener){
actionListener = listener;
}
@Override
public void mouseClicked(MouseEvent e) {
value += 5000;
if(value > maxValue) value = maxValue;
repaint();
}
@Override
public void paintComponent(Graphics g){
g.setColor(Color.red);
g.fillRect(getX(), getY(), getWidth(), getHeight());
if(isDisplayed){
g.setColor(Color.green);
g.fillRect(getX(), getY(), getWidth(), getHeight());
g.setColor(Color.black);
//g.setFont(font);
g.drawString(value + "/" + maxValue, 0, 0);
}
//super.paintComponent(g);
}
}
this results in nothing - the JComponents are created and correctly added at least to the ArrayList, and its paintComponent method run, as well as the timer events, components are also correctly removed at least from the ArrayList when the countdown reaches 0, but doesn't draw anything and I have no idea why.
please note that the commented code in frmMain constructor kind of works, and if uncommented, it draws the JComponent... well... just the rectangles, doesn't draw any text, but I suppose that's another problem, however, if you're willing to help with that one too, I'd be glad.
also, if I'm using action events and listeners incorrectly, please ignore that, I'm a beginner at Java and would like to know how to do it correctly, but later, right now I just need to get this code to work so please focus on the issue, thank you.
yes, there's some dead things, like the font passed to the component, those are remains of my attempts to get the drawString to work.
Upvotes: 0
Views: 314
Reputation: 347214
You project is suffering from a number of really basic misconceptions about how painting in Swing works.
Because (at the time I tested it) you hadn't provided the full code, I had to make some assumptions.
Your expection is that the content pane of the JFrame
is using a null
layout. I'd find it difficult to see how you would mange this and have other components on the frame. In any case, I would isolate a separate container to act as the "main" view for your Pocitadlo
. This allows you to continue using layout managers to manage the other components as well.
I'm a little concerned that you trap the entire Pocitadlo
generation code in a try-catch
simply to catch a numeric conversion. Since you've already applied a default value, it would be reasonable to trap the conversion in its own try-catch
and ignore it if it fails (displaying a message as required). Now I'm coming in from the outside, so you may see a need for this, it just caught my eye...
Your valueTimer
actionPerformed
code is slightly erronous, as it's possible for the blinkTimer
to continue running even after the valueTimer
has completed. I corrected this by merging two of you if
statements into a if-else
statement instead...
if (value == 0 && actionListener != null) {
isRunning = false;
valueTimer.stop();
actionListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "counterFinished"));
blinkTimer.stop();
} else if (value < maxValue / 2 && !blinkTimer.isRunning()) {
blinkTimer.start();
}
I, personally, would use a JPanel
over a JComponent
. JPanel
is opaque to start with.
The main problem is in you paintComponent
method.
super.paintComponent
, and on opaque components it should be called first.g.fillRect(getX(), getY(), getWidth(), getHeight());
. This is actually wrong. The graphics context has already been translated so that the x/y position of the component/graphics is now equal to 0x0. Have a read through Painting in AWT and Swing for a better explanation, but basically, this means, the top/left corner of you paint area is now 0x0.Font
s ascent value..
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestRepaint01 {
public static void main(String[] args) {
new TestRepaint01();
}
public TestRepaint01() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
MainFrame frame = new MainFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(200, 200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class MainFrame extends javax.swing.JFrame implements ActionListener {
ArrayList<Pocitadlo> poc;
Random rnd;
private JPanel body;
/**
* Creates new form frmMain
*/
public MainFrame() {
setLayout(new BorderLayout());
poc = new ArrayList<Pocitadlo>();
rnd = new Random();
body = new JPanel(null);
add(body);
JButton btn = new JButton("Add");
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int val, x, y;
val = 20;
x = (getWidth() / 2) - 10 + rnd.nextInt(20);
y = (getHeight() / 2) - 10 + rnd.nextInt(20);
// try {
// val = Integer.parseInt(txtCounterValue.getText());
Pocitadlo tmp = new Pocitadlo(val, x, y, getFont());
tmp.addActionListener(MainFrame.this);
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.weightx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
poc.add(tmp);
tmp.start();
body.add(tmp);
body.repaint();
setTitle("Počet počítadiel: " + getComponentCount());
// } catch (Exception e) {
// JOptionPane.showMessageDialog(this, "Nesprávne zadaná alebo prázdna hodnota počítadla. Hodnota musí byť celé číslo.", "Chyba", JOptionPane.ERROR_MESSAGE);
// }
}
});
add(btn, BorderLayout.SOUTH);
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("counterFinished")) {
System.out.println("Counter finished");
Pocitadlo tmp = (Pocitadlo) e.getSource();
poc.remove(tmp);
this.setTitle("Počet počítadiel: " + poc.size());
body.remove(tmp);
// body.revalidate();
body.repaint();
}
}
}
public class Pocitadlo extends JPanel implements MouseListener, ActionListener {
private int maxValue, value;
private Boolean isRunning, isDisplayed;
private Timer valueTimer, blinkTimer;
private ActionListener actionListener;
private Font font;
public Pocitadlo(int timeout, int x, int y, Font f) {
isRunning = false;
isDisplayed = true;
maxValue = value = timeout;
valueTimer = new Timer(1000, this);
valueTimer.setActionCommand("valueTimer");
blinkTimer = new Timer(200, this);
blinkTimer.setActionCommand("blinkTimer");
this.setBounds(x, y, 100, 50);
}
@Override
public void removeNotify() {
super.removeNotify();
stop();
blinkTimer.stop();
}
public void start() {
isRunning = true;
valueTimer.start();
}
public void stop() {
isRunning = false;
valueTimer.stop();
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("valueTimer")) {
value--;
System.out.println("value = " + value);
if (actionListener != null) {
repaint();
actionListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "counterTick"));
}
if (value == 0 && actionListener != null) {
isRunning = false;
valueTimer.stop();
actionListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "counterFinished"));
blinkTimer.stop();
} else if (value < maxValue / 2 && !blinkTimer.isRunning()) {
blinkTimer.start();
}
}
if (e.getActionCommand().equals("blinkTimer")) {
System.out.println("Blink");
isDisplayed = !isDisplayed;
repaint();
}
}
public void addActionListener(ActionListener listener) {
actionListener = listener;
}
@Override
public void mouseClicked(MouseEvent e) {
value += 5000;
if (value > maxValue) {
value = maxValue;
}
repaint();
}
@Override
public void mouseExited(MouseEvent e) {
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.red);
// location bad
g.fillRect(0, 0, getWidth(), getHeight());
if (isDisplayed) {
g.setColor(Color.green);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.black);
//g.setFont(font);
FontMetrics fm = g.getFontMetrics();
g.drawString(value + "/" + maxValue, 0, fm.getAscent());
}
g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
}
}
None of the problems you've encountered are super critical (sure, you program doesn't do what you want) and suggest that you've reached a pinnacle moment in your code abilities. I say this, because I made the exact same mistakes... ;)
Take a closer look at 2D Graphics and Custom Painting
I'd also take a look at Code Conventions for the Java Programming Language as you simply annoy people if you don't follow them ;)
Upvotes: 3