Search code examples
javaswingjlabel

JLabel components stay on screen


I'm working on a really basic bar chart which has to display 6 values. The problem I'm running into is that when i put the bars on the screen once they stay on the screen, and i cannot get them off. I've tried using the remove, repaint and revalidate functions but these all do not work.

What do I have to do to remove the bars so they don't clog up?

My code:

import javax.swing.*;
import java.awt.*;
import java.util.Collections;

public class BarChart extends JPanel
{
    private JLabel[] bars;

    public BarChart(int[] data)
    {
        update(data);
    }

    public void update(int[] data)
    {
        this.setSize(190, 155);
        this.setLayout(null);

        int max = 0;
        for (int i = 0; i < 6; i++) {if (data[i] > max) {max = data[i];}}

        bars = new JLabel[6];

        for (int i = 0; i < 6; i++)
        {
            bars[i] = new JLabel();
            bars[i].setOpaque(true);
            bars[i].setBackground(Color.RED);

            int height = (max != 0) ? (data[i]*155)/max : 0;
            System.out.printf("%d, %d, %d,... ", height, data[i], max);

            this.add(bars[i]);
            bars[i].setSize(25, height);
            bars[i].setLocation((31*i)+5, 155-height);
        }
        System.out.println("");
    }
}

Solution

  • For your current code, you would need to call removeAll(), then revalidate() and repaint() on the JPanel would "solve" your problem, but you have other unrelated problems:

    • You're setting a component's size when you should be producing a preferred size -- this is what layout managers generally work with
    • You're using null layouts, a very dangerous thing
    • You're using a "magic" number as a for loop ending condition -- a VERY dangerous thing to do. How do you know that the data array has 6 and only items within it. What harm is there in simply using the data array's length as you've likely done hundreds of times before?

    Instead, consider using more flexible code that will adapt to any size of data you give it and that avoids null layouts. For example consider the following code that draws the bars within the JPanel's paintComponent method:

    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.GridLayout;
    import java.awt.event.ActionEvent;
    import javax.swing.*;
    
    @SuppressWarnings("serial")
    public class TestBarChart extends JPanel {
        private static final int[] INIT_DATA = { 1, 2, 4, 5, 6, 9 };
        protected static final int MIN_DATA_lENGTH = 5;
        protected static final int MAX_DATA_LENGTH = 9;
        private static final int MAX_VALUE = 9;
        private static final int PREF_W = 300;
        private static final int PREF_H = 240;
        private BarChart2 barChart2 = new BarChart2(INIT_DATA, MAX_VALUE, PREF_W, PREF_H);
    
        public TestBarChart() {
            barChart2.setBorder(BorderFactory.createLineBorder(Color.BLUE));
    
            JPanel chartsPanel = new JPanel(new GridLayout(1, 0));
            chartsPanel.setLayout(new GridLayout(1, 0));
            chartsPanel.add(barChart2);
    
            JButton resetDataBtn = new JButton(new AbstractAction("Reset Data") {
    
                @Override
                public void actionPerformed(ActionEvent e) {
                    int dataLength = (int) ((MAX_DATA_LENGTH - MIN_DATA_lENGTH) * Math.random()) + MIN_DATA_lENGTH;
                    int[] data = new int[dataLength];
                    for (int i = 0; i < data.length; i++) {
                        data[i] = (int) (MAX_VALUE * Math.random()) + 1;
                    }
                    barChart2.setData(data, MAX_VALUE);
                }
            });
            JPanel btnPanel = new JPanel();
            btnPanel.add(resetDataBtn);        
    
            setLayout(new BorderLayout());
            add(chartsPanel);
            add(btnPanel, BorderLayout.PAGE_END);
        }
    
        private static void createAndShowGui() {
            TestBarChart mainPanel = new TestBarChart();
    
            JFrame frame = new JFrame("Test");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.getContentPane().add(mainPanel);
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(() -> createAndShowGui());
        }
    }
    

    @SuppressWarnings("serial")
    class BarChart2 extends JPanel {
        private static final double BAR_WIDTH = 0.90;
        private int prefW;
        private int prefH;
        private static final Color BAR_COLOR = Color.RED;
        private int[] data;
        private int maxValue;
    
        public BarChart2(int[] data, int maxValue, int prefW, int prefH) {
            setData(data, maxValue);
            this.prefW = prefW;
            this.prefH = prefH;
        }
    
        public final void setData(int[] data, int maxValue) {
            this.data = data;
            this.maxValue = maxValue;
            repaint();
        }
    
        public int[] getData() {
            return data;
        }
    
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.setColor(BAR_COLOR);
    
            // simple algebraic calculations on where to place the bars
            double denom = data.length + 1 - BAR_WIDTH;
            int barWidth = (int) ((getWidth() * BAR_WIDTH) / denom);  
    
            for (int i = 0; i < data.length; i++) {
                int x = (int) (getWidth() * (i + 1 - BAR_WIDTH) / denom);
                int height = (int) (getHeight() * data[i] / (double) maxValue);
                int y = (int) (getHeight() - height);
                g.fillRect(x, y, barWidth, height);
            }
        }
    
        @Override
        public Dimension getPreferredSize() {
            if (isPreferredSizeSet()) {
                return super.getPreferredSize();
            }
            return new Dimension(prefW, prefH);
        }
    
    }
    

    Note that the bar charts re-size to fill the GUI if you re-size the GUI. Note that the chart accomodates any number of data bars, all depending on the length of the data array passed into it.