2

I have a class that extends JFrame with ten radio buttons on it, and I've used a variety of JPanels and GridLayouts to help me place them correctly. I'm attempting to make it so that when you select a combination of radio buttons, the program will draw a line between each of the radio buttons in the order you've selected them. However, I can't get anything to appear. I'm not sure if I'm not overridding the right method, if I should be using Graphics2D, if the panels are hiding whatever I'm drawing...preferably, I'd like a solution that doesn't have me overriding a JPanel or something like that.

    public void paintComponent(Graphics g)
{
    super.update(g);
    if(buttonsSelected>1)
    {
        g.setColor(new Color(0xE3, 0xC9, 0x39));
        for(int k=0;k>4&&lastButton[k+1]!=-1;k++)
        {
            g.drawLine(buttonTest[lastButton[k]].getX(), buttonTest[lastButton[k]].getY(), buttonTest[lastButton[k+1]].getX(), buttonTest[lastButton[k]].getY());
            System.out.println("Ole!");
        }
    }
}

Additionally, here is part of the code I'm using to draw the panes

    int j=0;
    for(int k=0;k<10;k++)
    {
        buttonTest[k]=new JRadioButton();
        buttonTest[k].setActionCommand(Integer.toString(k));
        buttonTest[k].setToolTipText(powersDin[k]);
        buttonTest[k].addActionListener(new GoddessListener());
        buttonTest[k].setEnabled(false);
    }
    buttonTest[0].setEnabled(true);
    buttonTest[6].setEnabled(true);
    buttonTest[9].setEnabled(true);

    paneGrids[0]=new JPanel();
    paneGrids[0].setLayout(new GridLayout(1,7));
    paneGrids[0].add(new JLabel()); //adding a blank JLabel lets me pad out the empty cells I don't want to fill
    paneGrids[0].add(new JLabel());
    paneGrids[0].add(new JLabel());
    paneGrids[0].add(buttonTest[j++]);
    paneGrids[0].add(new JLabel());
    paneGrids[0].add(new JLabel());
    paneGrids[0].add(new JLabel());
chif-ii
  • 995
  • 1
  • 6
  • 12
  • you have to override the paint method of every single checkbox! otherwise they will update and delete what you have drawn. – Willi Mentzel Oct 22 '14 at 15:42
  • I'm trying to draw a line between the buttons, not on them exactly. And also, when I run the code, I should be getting "Ole!" on the console, which isn't happening. – chif-ii Oct 22 '14 at 15:46
  • that "Ole!" is shown doesn't surprise me! But a checkbox has a rectangular area around it! – Willi Mentzel Oct 22 '14 at 15:48
  • No, it's not getting shown! And also, I'm using radio buttons (though your point about them being rectangular would probably apply to them as well) – chif-ii Oct 22 '14 at 15:49
  • sry, I skipped that! please provide more code but in a mininmal way (just one checkbox etc.). – Willi Mentzel Oct 22 '14 at 16:08

1 Answers1

4

You should use the @Override annotation on your paintComponent method. You will see that you are not overriding any method on your JFrame. It is because paintComponent is defined on JComponent which JFrame does not inherit from. Therefore your paintComponent method is never called.

Instead of trying to paint on a top-level container (which should generally not be done), use a JPanel for the custom painting.

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);

    // your custom painting
}

Using a JPanel is no more complicated, just call yourFrame.setContentPane(yourCustomPanel);.

If you want to draw on top of the children you can override paint judiciously. paintComponent will necessarily draw behind the children because it is for drawing the component. paint will draw on everything. The other option for drawing on top of children is to using something like a glass pane or JLayeredPane which are more difficult to work with.

Here is an MCVE that demonstrates how to do something like I think you are wanting to do simply.

connective grid

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JCheckBox;
import javax.swing.SwingUtilities;

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

import java.awt.GridLayout;
import java.awt.Point;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Color;
import java.awt.RenderingHints;

import java.util.Map;
import java.util.LinkedHashMap;
import java.util.Iterator;

class ConnectiveGrid implements Runnable {
    static final int GRID_H = 5;
    static final int GRID_W = 10;

    Map<JCheckBox, Point> path;
    GridPanel grid;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new ConnectiveGrid());
    }

    @Override
    public void run() {
        JFrame frame = new JFrame("Connective Grid");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // (LinkedHashMap will maintain insertion order)
        path = new LinkedHashMap<JCheckBox, Point>();

        grid = new GridPanel();
        ActionListener listener = new Listener();

        for(int i = 0; i < GRID_H * GRID_W; i++) {
            JCheckBox check = new JCheckBox();
            check.addActionListener(listener);
            grid.add(check);
        }

        frame.setContentPane(grid);
        frame.pack();
        frame.setResizable(false);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    class Listener implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent ae) {
            Object sc = ae.getSource();
            if(!(sc instanceof JCheckBox)) {
                return;
            }

            JCheckBox check = (JCheckBox)sc;
            if(check.isSelected()) {

                Dimension sz = check.getSize();
                Point pt = check.getLocation();

                pt.x += sz.width / 2;
                pt.y += sz.height / 2;

                // if the check box is selected
                // put it and its associated point in the path
                path.put(check, pt);

            } else {

                // else remove the check box and point
                path.remove(check);
            }

            // prompt a clean repaint
            grid.repaint();
        }
    }

    class GridPanel extends JPanel {
        GridPanel() {
            super(new GridLayout(GRID_H, GRID_W));
        }

        @Override
        public void paint(Graphics g) {
            super.paint(g);

            // verify there are enough points to draw at least one line
            if(path.size() < 2) {
                return;
            }

            Graphics2D copy = (Graphics2D)g.create();
            copy.setPaint(Color.BLUE);
            copy.setRenderingHint(
                RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON
            );

            // iterate the path and paint line-by-line
            Iterator<Point> it = path.values().iterator();
            Point prev = it.next();
            do {
                Point next = it.next();

                copy.drawLine(
                    prev.x, prev.y,
                    next.x, next.y
                );

                prev = next;
            } while(it.hasNext());

            copy.dispose();
        }
    }
}
Community
  • 1
  • 1
Radiodef
  • 37,180
  • 14
  • 90
  • 125
  • ...wait, do I need to convert every single JPanel I want to draw on into a custom JPanel in order for this to work? – chif-ii Oct 22 '14 at 16:28
  • I don't see why you would. I had the impression you were only trying to paint on the container. – Radiodef Oct 22 '14 at 16:33
  • Now it's not displaying some of the buttons. If I use setEnable(false), will it not draw them, or am I doing something wrong? – chif-ii Oct 22 '14 at 16:47
  • I don't think disabling a button should have an effect on your custom painting. I including an MCVE that I hope demonstrates how to do something like this correctly. – Radiodef Oct 22 '14 at 17:54