Search code examples
javaswingjframejpanelpaintcomponent

How can I add a rectangle in BorderLayout.SOUTH?


I am trying to add a thing like this in my music player application in swing.

I tried to add a rectangle to BorderLayout.SOUTH, but it never appeared on screen. Here is what I did:

public class MyDrawPanel extends JPanel {

    @Override
    public void paintComponent(Graphics g) {
        g.setColor(Color.GREEN);
        g.fillRect(200,200,200,200);

    }


    public static void main(String[] args) {
        JFrame frame = new JFrame();
        MyDrawPanel a = new MyDrawPanel();
        frame.getContentPane().add(BorderLayout.SOUTH,a);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(1000,1000);
        frame.setVisible(true);
    }

}

I just did not try 200,200,200,200, but I tried a lot of values, even with the help of a for loop, but it never appeared on screen. If I used CENTER instead of SOUTH it appeared. I read the documentation to check how fillRect works, but it simply said it added x+width and y+height. The point (0,0) is the top left corner. I checked that by adding a rectangle to CENTER layout. How cam I do it?

I did not share the output, because it was just a blank screen.


Solution

  • I thought this might make a quick little project. Here's the level meter I came up with.

    Level Meter GUI

    The important parts are the DrawingPanel and the LevelMeterModel. The DrawingPanel takes the information from the LevelMeterModel and paints the bars on a JPanel.

    The LevelMeterModel is an int array of levels, a minimum level, and a maximum level. The maximum level could be calculated, but I assumed music has a certain volume and frequency range.

    The JFrame holds the DrawingPanel. A Swing Timer varies the levels somewhat randomly. The random numbers are in a small range so the bar heights don't change abruptly.

    Here's the complete runnable code.

    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.util.Random;
    
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.SwingUtilities;
    import javax.swing.Timer;
    
    public class LevelMeterGUI implements Runnable {
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new LevelMeterGUI());
        }
        
        private final DrawingPanel drawingPanel;
        
        private final LevelMeterModel model;
        
        public LevelMeterGUI() {
            this.model = new LevelMeterModel();
            this.drawingPanel = new DrawingPanel(model);
        }
    
        @Override
        public void run() {
            JFrame frame = new JFrame("Level Meter GUI");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            
            frame.add(drawingPanel, BorderLayout.CENTER);
            
            frame.pack();
            frame.setLocationByPlatform(true);
            frame.setVisible(true);
            
            System.out.println("Frame size: " + frame.getSize());
            
            Timer timer = new Timer(250, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent event) {
                    model.setRandomLevels();
                    drawingPanel.repaint();
                }
            });
            timer.start();
        }
        
        public class DrawingPanel extends JPanel {
    
            private static final long serialVersionUID = 1L;
            
            private final int drawingWidth, drawingHeight, margin, rows;
            
            private final Dimension barDimension;
            
            private final LevelMeterModel model;
            
            public DrawingPanel(LevelMeterModel model) {
                this.model = model;
                this.margin = 10;
                this.rows = 20;
                this.barDimension = new Dimension(50, 10);
                
                int columns = model.getLevels().length;
                drawingWidth = columns * barDimension.width + (columns + 1) * margin;
                drawingHeight = rows * barDimension.height + (rows + 1) * margin;
                
                this.setBackground(Color.WHITE);
                this.setPreferredSize(new Dimension(drawingWidth, drawingHeight));
            }
            
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                
                int maximum = model.getMaximumLevel();
                double increment = (double) maximum / rows;
                
                int peak = rows * 75 / 100;
                int x = margin;
                for (int level : model.getLevels()) {
                    int steps = (int) Math.round((double) level / increment);
                    int y = drawingHeight - margin - barDimension.height;
                    for (int index = 0; index < steps; index++) {
                        if (index < peak) {
                            g.setColor(Color.GREEN);
                        } else {
                            g.setColor(Color.RED);
                        }
                        g.fillRect(x, y, barDimension.width, barDimension.height);
                        y = y - margin - barDimension.height;
                    }
                    x += margin + barDimension.width;
                }
            }
            
        }
        
        public class LevelMeterModel {
            
            private final int minimumLevel, maximumLevel;
            
            private int[] levels;
            
            private final Random random;
            
            public LevelMeterModel() {
                this.minimumLevel = 100;
                this.maximumLevel = 999;
                this.levels = new int[8];
                this.random = new Random();
                setRandomLevels();
            }
            
            public void setRandomLevels() {
                for (int index = 0; index < levels.length; index++) {
                    levels[index] = getRandomLevel(levels[index]);
                }
            }
            
            private int getRandomLevel(int level) {
                if (level == 0) {
                    return random.nextInt(maximumLevel - minimumLevel) + minimumLevel;
                } else {
                    int minimum = Math.max(level * 90 / 100, minimumLevel);
                    int maximum = Math.min(level * 110 / 100, maximumLevel);
                    return random.nextInt(maximum - minimum) + minimum;
                }
            }
    
            public int[] getLevels() {
                return levels;
            }
    
            public int getMinimumLevel() {
                return minimumLevel;
            }
    
            public int getMaximumLevel() {
                return maximumLevel;
            }
            
        }
    
    }