VIAGC
VIAGC

Reputation: 839

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.

Upvotes: 0

Views: 266

Answers (2)

Gilbert Le Blanc
Gilbert Le Blanc

Reputation: 51485

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;
        }
        
    }

}

Upvotes: 1

daniu
daniu

Reputation: 15008

The values you give to fillRect are wrong. The first two are the top left corner's coordinates, relative to the component you're painting in; in your case the MyDrawPanel. With the code you posted, this drawing area is outside of the container the panel is placed in. You want to do

g.fillRect(0,0,200,200);

A note: You usually want to call frame.pack() after you've finished adding all components, so it can layout itself. In your case, this results in a tiny window because the system doesn't know how large it should be. You probably want to add a method

public Dimension getPreferredSize() {
    System.out.println("getting pref size");
    return new Dimension(200, 200);
}

to ensure it's always large enough to draw the full rectangle.

Also, you should call frame.getContentPane().setLayout(new BorderLayout()) before. You can print it out without setting it to see it is not the default. EDIT: As VGR points out, the documentation says that it is in fact a BorderLayout. I cannot confirm that is the case - it is in fact a RootLayout. That seems to behave like a BorderLayout though.

Upvotes: 2

Related Questions