Search code examples
javaswingactionlistenerjcomponentcustom-painting

How to draw a bar in this example?


In this exercise, when you enter the name of a city and its population, in the window it must be graphed a bar that represents the population of that city as shown in the figures, the exercise is almost complete but it doesn't work, could someone tell me what I'm doing wrong?, Why is no bar drawn?, the condition that makes the exercise a bit more difficult to me is that the size of the bars and lines must change if the window size changes. The main class can't be modified because is already given.

enter image description here

enter image description here

package Hw02_Statistics;

import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.table.DefaultTableModel;

import hw01_clocknumbers.DigitalNumber;

public class Statistics extends JFrame {

    private JLabel              city, population, other;
    private JTextField          tcity, tpopulation;
    private JButton             add;
    private JTable              table;
    private DefaultTableModel   tablem;
    private int index=1;
    private bar bars;


    public Statistics() {
        setTitle("Statistics");
        setSize(500, 350);
        setupWidgets();
        seupEvents();
        setVisible(true);   

    }

    private void setupWidgets() {

        bars = new bar();       
        city =          new JLabel("City",JLabel.LEFT);
        population =    new JLabel("Population",JLabel.LEFT);
        tcity =         new JTextField();
        other =         new JLabel();
        tpopulation =   new JTextField();
        add =           new JButton("Add");
        tablem =        new DefaultTableModel(new Object[] {"Index", "City", "Population"}, 0);
        table =         new JTable(tablem);
        JPanel norte =  new JPanel(new GridLayout(5,1));
        JPanel sur =    new JPanel(new GridLayout(1,2));

        add(norte, BorderLayout.NORTH);
        add(sur, BorderLayout.CENTER);

        norte.add(city);
        norte.add(tcity);
        norte.add(population);
        norte.add(tpopulation);
        norte.add(add);
        sur.add(new JScrollPane(table));
        sur.add(bars);          
    }

    private void seupEvents() {

        setDefaultCloseOperation(EXIT_ON_CLOSE);

        add.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {

                if (!tcity.getText().equals("") && !tpopulation.getText().equals("")) {

                    Object rowinfo[]    = {index, tcity.getText(), tpopulation.getText()}; 
                    tablem.addRow(rowinfo); 
                    int n= Integer.parseInt(tpopulation.getText());
                    bars.setScale(1, index);    
                    tcity.setText("");
                    tpopulation.setText("");
                    index++;
                }   
            }
        });

    }

    public static void main(String[] args) {
        new Statistics();
    }
}

This is the second class I used

package Hw02_Statistics;

import java.awt.Graphics;
import java.awt.GridLayout;

import javax.swing.JComponent;

import hw01_clocknumbers.DigitalNumber;

public class bar extends JComponent {

    private int  digit;
    private Integer scale=0;
    private int index;
    private rect    rects[];

    public bar () {

    }

    public void paint(Graphics g) {
        int w   =getWidth();
        int h   =getHeight();

        rects   = new rect[4];

        rects[index] = new rect(scale, index, w, h);

//      System.out.println(w); 243
//      System.out.println(h); 183

        g.drawLine(w/12, h/9, w/12, 8*h/9);
        g.drawLine(w/12, 8*h/9, 12*w/12, 8*h/9);

    }

    public void setScale(Integer scale, int index) {
        this.index=index-1;
        this.scale=scale;
        repaint();

    }
}

And this is the last Class I used, and it is the one that doesn't work

package Hw02_Statistics;

import java.awt.Graphics;

import javax.swing.JComponent;

public class rect extends JComponent{

    private int scale;
    private int index;
    private int w, h;

    public rect(int scale, int index, int w, int h) {
        this.scale = scale;
        this.index= index;
        this.w =w;
        this.h=h;       
    }

    public void paint(Graphics e) {

        e.fillRect(6*w/12+w*index/12,h/9,scale*w/12,scale*7*h/9);
        System.out.println("Aaaa");

        repaint();

    }
}

Solution

  • class bar (which should be called Bar according to java naming conventions) was renamed to Bars and changed to hold and draw all bars:

    class Bars extends JComponent {
        //collection of bar heights
        private final List<Integer>  barsValues;
        private static final int W = 20; //bar width
        public Bars () {
            barsValues   = new ArrayList<>();
        }
    
        //override paintComponent rather than paint
        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g); //always call super
            int w  = getWidth();
            int h  = getHeight();
            //use double to avoid rounding errors
            double minX = w/12 , maxX = 12*minX, maxY = h/9, minY = 8 * maxY;
            double graphHeight = minY - maxY;
            //draw axis
            g.drawLine((int)minX, (int)maxY, (int)minX, (int)minY);
            g.drawLine((int)minX, (int)minY, (int)maxX, (int)minY);
    
            //draw bars
            if(barsValues.size() == 0) return;
            double x = minX + W ;
            int maxHeight =  getMaxHeight(); //calculate scale based on max height
            double scale = maxHeight > 0 ?  graphHeight / getMaxHeight() : 1;
            for(int barValue : barsValues){
                double barHeight = scale*barValue;
                g.fillRect((int)x ,(int)(minY - barHeight), W, (int)barHeight);
                x += 2*W;
            }
        }
    
        //add bar values. valid values should be > 0
        void addBar(int height) {
            barsValues.add(height);
            repaint();
        }
    
        //get max height
        int getMaxHeight(){
            int max = 0;
            for (int value : barsValues){
                if(value > max) {
                    max = value;
                }
            }
            return max;
        }
    }
    

    class rect is not needed.

    To test you need to do some minor changes in Statistics:
    change private bar bars; to private Bars bars;
    Initialize it by bars = new Bars();
    and change one line in the action listener from bars.setScale(1, index) to bars.addBar(n);

    The complete code can be copy-pasted from here