Leprachon
Leprachon

Reputation: 127

Updating JLabel everytime Frame's panel is updated

How can I update JLabel Count #0 to Count #1 as continued, at topPanel in Frame?

Initially it is Count #0 and as the operation performs further it changes to Count #1.

OUTPUT: Here it is showing Count #0 but it should be Count #2 as the grey box is on second level, Similarly Here it should show Count #5, as its on fifth level from top.

import java.awt.*;
import java.awt.image.BufferedImage;
import javax.swing.*;

class Count {
    private int num;
    
    public Count() {
        this.num = 1;
    }
    
    // Generate Next Number
    public void generate(int currentNumber) {
        this.num = currentNumber;
        this.num += 1;
    }
    
    public int getNumber() {
        return this.num;
    }
}

class Panel extends JPanel {
    private final BufferedImage image;
    private Count count;
    
    public Panel() {
        this.image = new BufferedImage(300, 300, BufferedImage.TYPE_INT_RGB);
        this.count = new Count();
        Timer timer = new Timer(0, ae -> createImage(image));
        timer.setDelay(1000);
        timer.start();
    }
    
    public void createImage(BufferedImage image) {
        Graphics g = image.getGraphics();
        
        int number = this.count.getNumber();
        
        // Set field on frame which will be added to bottomPanel
        for (int i = 0; i < (number * 20); i++) {
            for (int j = 0; j < (number * 20); j++) {
                g.setColor(Color.GRAY);
                g.fillRect(i, j, 20, 20);
                g.setColor(Color.GREEN);
                g.drawRect(i, j, 20, 20);
            }
        }
        
        // Generating next number
        this.count.generate(number);
        repaint();
    }
    
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(image, 0, 0, null);
    }
}

class GUI extends JFrame {
    private JPanel topPanel;
    private JPanel bottomPanel;
    
    public GUI() {
        topPanel = new JPanel();
        bottomPanel = new JPanel();
        
        // Setting topPanel and Adding Label to topPanel
        topPanel.setLayout(new BoxLayout(topPanel, BoxLayout.PAGE_AXIS));
        updateLabel(0);
        
        // Instructions for bottomPanel
        Panel panel = new Panel();
        panel.setPreferredSize(new Dimension(300, 300));
        
        // Adding the instructions to bottomPanel
        bottomPanel.add(panel);

        // Adding topPanel and bottomPanel to Frame
        add(topPanel, BorderLayout.PAGE_START);
        add(bottomPanel, BorderLayout.CENTER);

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        pack();
    }
    
    public void updateLabel(int number) {
        // Label - 1
        JLabel countLabel = new JLabel("CountLabel");
        countLabel.setText("   Count #" + number);
        countLabel.setFont(new Font("Comic Sans MS", Font.PLAIN, 14));
        countLabel.setBounds(10, 5, 300, 20);
        
        topPanel.add(countLabel);
    }
}

public class NumberPresentation {
    public static void main(String[] args) {
        new GUI().setVisible(true);
    }
}

So, how should I start working so that the JLabel in topPanel keeps updating every time Count class generate() another number, and generate() is called in Panel class's createImage() function?

Thank You.

PS: The bottomPanel animation is good, I just want to know how I can work around with JLabel to update it everytime.

Upvotes: 1

Views: 56

Answers (1)

Oussama
Oussama

Reputation: 412

I suggest a Callback oriented approach, this is very similar to how JButton's addActionListener works. Warning it's a bit long and might get messy if you abuse it, but it keeps your initial classes separation intact.

The idea is to add an onUpdate method to your Panel class that gets called every time the panel updates. And then to define what the action is when you create a panel.

For that, you will first need to declare an abstract class or interface with the methods you need, I choose generic names but feel free to customize

interface Callback {
    public void action(int v);
}

We can now pass declare and pass an action method that takes an integer to any class, you just need to add a Callback field :

class Panel extends JPanel {
    private final BufferedImage image;
    private Count count;
    private Callback onUpdate;

    public void setOnUpdateAction(Callback action) {
        this.onUpdate = action;
    }

You're free to choose where to call onUpdate, I made it after the next number is generated

// Generating next number
this.count.generate(number);
onUpdate.action(this.count.getNumber());
repaint();

Your Panel class is now ready.

Now after creating your panel, just make a call to setOnUpdateAction

panel.setOnUpdateAction(new Callback() {
    public void action(int v) {
        countLabel.setText("   Count #" + v);
    }
});

Since there is only one method and one instruction, you can use a shorter Lambda syntax similar to the one you used in the Timer

panel.setOnUpdateAction(v -> countLabel.setText("   Count #" + v));

( I had to make the countLabel a field to access it inside GUI.)

And there you have it !

enter image description here

Here's the full modified code :

import java.awt.*;
import java.awt.image.BufferedImage;
import javax.swing.*;

class Count {
    private int num;

    public Count() {
        this.num = 1;
    }

    // Generate Next Number
    public void generate(int currentNumber) {
        this.num = currentNumber;
        this.num += 1;
    }

    public int getNumber() {
        return this.num;
    }
}

interface Callback {
    public void action(int v);
}

class Panel extends JPanel {
    private final BufferedImage image;
    private Count count;
    private Callback onUpdate;


    public void setOnUpdateAction(Callback action) {
        this.onUpdate = action;
    }

    public Panel() {
        this.image = new BufferedImage(300, 300, BufferedImage.TYPE_INT_RGB);
        this.count = new Count();
        Timer timer = new Timer(0, ae -> createImage(image));
        timer.setDelay(1000);
        timer.start();
    }

    public void createImage(BufferedImage image) {
        Graphics g = image.getGraphics();

        int number = this.count.getNumber();

        // Set field on frame which will be added to bottomPanel
        for (int i = 0; i < (number * 20); i++) {
            for (int j = 0; j < (number * 20); j++) {
                g.setColor(Color.GRAY);
                g.fillRect(i, j, 20, 20);
                g.setColor(Color.GREEN);
                g.drawRect(i, j, 20, 20);
            }
        }

        // Generating next number
        this.count.generate(number);
        onUpdate.action(this.count.getNumber());
        repaint();
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(image, 0, 0, null);
    }
}

class GUI extends JFrame {
    private JPanel topPanel;
    private JPanel bottomPanel;
    private JLabel countLabel;  // Made it a field to use after being created

    public GUI() {
        topPanel = new JPanel();
        bottomPanel = new JPanel();

        // Setting topPanel and Adding Label to topPanel
        topPanel.setLayout(new BoxLayout(topPanel, BoxLayout.PAGE_AXIS));
        updateLabel(0);

        // Instructions for bottomPanel
        Panel panel = new Panel();
        panel.setPreferredSize(new Dimension(300, 300));

        panel.setOnUpdateAction(new Callback() {
            public void action(int v) {
                countLabel.setText("   Count #" + v);
            }
        });

        // You can also use this lambda syntax ( since Callback has only 1 method to implement )
        // panel.setOnUpdateAction(v -> countLabel.setText("   Count #" + v));


        // Adding the instructions to bottomPanel
        bottomPanel.add(panel);

        // Adding topPanel and bottomPanel to Frame
        add(topPanel, BorderLayout.PAGE_START);
        add(bottomPanel, BorderLayout.CENTER);

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        pack();
    }

    public void updateLabel(int number) {
        // Label - 1
        countLabel = new JLabel("CountLabel");
        countLabel.setText("   Count #" + number);
        countLabel.setFont(new Font("Comic Sans MS", Font.PLAIN, 14));
        countLabel.setBounds(10, 5, 300, 20);

        topPanel.add(countLabel);
    }
}

public class NumberPresentation {
    public static void main(String[] args) {
        new GUI().setVisible(true);
    }
}

Upvotes: 1

Related Questions