简体   繁体   中英

Java Swing repaint and revalidate not always working

I have a problem with swing and updating a JPanel with few buttons. Sometimes I need to delete or add or even modify some buttons. I have created a routine to do that, with few dialogs and messagebox. The fact is that sometimes (randomly) the JPanel don't refresh correctly (it don't delete the button as requested or even it not add it). If I call a second time the refresh routine (I call the dialog to add a button and then click exit without adding anythin) all go well. I can't find out why, I think it's about timing and the time that needs each repaint or revalidate method to be effective, this consideration is because I tried to debug the program (with eclipse and few breakpoints) and during debug it always refresh correctly.

Here is some code (simplified).

// This method is used when first creating the GUI (the removeAll obviously remove nothing
// I call the revalidate because I re-add the buttons, and I call repaint to have the
// JPanel itself repainted.

public static void repaintServices() {
    //services is a JPanel
    services.removeAll();
    JLabel lbl = new JLabel("SERVIZI");

    services.add(lbl);
    services.add(Box.createRigidArea(new Dimension(0, 20)));

    // ########## SERVICES BUTTONS
    Database.connect();
    ArrayList<Service> s = Database.listServices();
    JPanel btt = new JPanel();
    for (int i = 0; i < s.size(); i++) {
        JButton b = new JButton(s.get(i).getName().toUpperCase());
        if (i % 3 == 0) {
            btt = new JPanel();
            btt.setLayout(new BoxLayout(btt, BoxLayout.LINE_AXIS));
            services.add(btt);
            services.add(Box.createRigidArea(new Dimension(0, 10)));
            btt.add(b);
        } else {
            btt.add(Box.createRigidArea(new Dimension(10, 0)));
            btt.add(b);
        }
    }
    Database.disconnect();
    // ########## END SERVICES BUTTONS

    services.add(Box.createVerticalGlue());

    services.revalidate();
    services.repaint();
}

This is where I modify the database (and so the button list)

LOG.finest("User ask to add a new Service");

ArrayList<String> result = GUI.showServiceDialog();
if (result.size() > 0) {
    if (!"".equals(result.get(0))) {
        String name = result.get(0);

        Database.connect();
        if (Database.addService(name, price, cadence)) {
            GUI.showMessage("Servizio aggiunto correttamente");
        GUI.repaintServices();
        }
        Database.disconnect();
    } else {
        GUI.showErrorMessage("All field necessary!\nNew Service NOT added");
        return;
    }
}else {
    return;
}

This is the metod used to get the values inserted by the user (uses a custom modal dialog box)

public static ArrayList<String> showServiceDialog() {
    ArrayList<String> ret = new ArrayList<String>();
    JTextField name = new JTextField(15);
    JTextField price = new JTextField(5);
    JTextField cadence = new JTextField(4);

    // NOT USEFUL STUFF, just adding some components. JUMP DOWN TO.....
    JPanel p = new JPanel();
    p.setLayout(new BoxLayout(p, BoxLayout.PAGE_AXIS));
    JPanel pp = new JPanel();
    pp.setLayout(new BoxLayout(pp, BoxLayout.LINE_AXIS));

    pp.add(new JLabel("Nome:"));
    pp.add(Box.createHorizontalStrut(10));
    pp.add(name);

    p.add(pp);
    p.add(Box.createVerticalStrut(10)); // a spacer

    pp = new JPanel();
    pp.setLayout(new BoxLayout(pp, BoxLayout.LINE_AXIS));
    pp.add(new JLabel("Prezzo:"));
    pp.add(Box.createHorizontalStrut(10));
    pp.add(price);
    pp.add(new JLabel("€"));
    pp.add(Box.createHorizontalStrut(20));
    pp.add(new JLabel("Frequenza:"));
    pp.add(Box.createHorizontalStrut(10));
    pp.add(cadence);

    p.add(pp);
    p.add(Box.createVerticalStrut(15));

    // .... HERE, following the interesting part.

    int result = JOptionPane.showConfirmDialog(window, p, "Inserisci i dati del nuovo servizio", JOptionPane.OK_CANCEL_OPTION);
    if (result == JOptionPane.OK_OPTION) {
        ret.add(name.getText());
        ret.add(price.getText());
        ret.add(cadence.getText());
    }
    return ret;

}

I have some other methods like delete, edit etc etc but the problem (not refreshing correct) is randomly with each of this method, I posted the simplest part i have.

any Idea? (I have the same problem also with other part of the GUI like comboBoxes or listBoxes, the GUI is started with SwingUtilities.invokeLater)

HERE some SSCCE code, the problem occurs doing: ADD, ADD, REMOVE, ADD

the adds after a remove do not refresh correctly (sometimes, sometimes randomly, as I said). (without touching any other thing). I hope the code Is clear enough and short enough, I removed the database that seems not to be involved.

package it.kApps;

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

public class RevalidateRepaintTry {

private static JPanel   services;
private static ArrayList<String>    s;

public RevalidateRepaintTry() {
    s = new ArrayList<String>();
    s.add("btt1");
    s.add("btt2");
    JFrame main = new JFrame();
    main.getContentPane().setLayout(new BoxLayout(main.getContentPane(), BoxLayout.PAGE_AXIS));
    services = new JPanel();
    repaintServices();
    JButton b = new JButton("ADD");
    b.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent evt) {
            String add = JOptionPane.showInputDialog("insert new");
            s.add(add);
            repaintServices();
        }
    });
    JButton bb = new JButton("DEL");
    bb.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent evt) {
            s.remove(s.size() - 1);
            repaintServices();
        }
    });
    main.add(services);
    main.add(b);
    main.add(bb);
    main.setVisible(true);
    main.pack();
    main.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

}

public static void main(String arg[]) {

    javax.swing.SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            new RevalidateRepaintTry();
        }
    });

}

public static void repaintServices() {
    // services is a JPanel
    services.removeAll();
    JLabel lbl = new JLabel("SERVIZI");

    services.add(lbl);
    services.add(Box.createRigidArea(new Dimension(0, 20)));

    // ########## SERVICES BUTTONS
    JPanel btt = new JPanel();
    for (int i = 0; i < s.size(); i++) {
        JButton b = new JButton(s.get(i));
        if (i % 3 == 0) {
            btt = new JPanel();
            btt.setLayout(new BoxLayout(btt, BoxLayout.LINE_AXIS));
            services.add(btt);
            services.add(Box.createRigidArea(new Dimension(0, 10)));
            btt.add(b);
        } else {
            btt.add(Box.createRigidArea(new Dimension(10, 0)));
            btt.add(b);
        }
    }
    // ########## END SERVICES BUTTONS

    services.add(Box.createVerticalGlue());

    services.revalidate();
    services.repaint();
}
}

I have a feeling it's a problem with the layout manager...

Personally, I'd start out by using a different layout manager.

JPanel uses a FlowLayout by default, which has a awesome side effect of not "wrapping" components the expand beyond the horizontal width of the parent container.

While I understand that Box can be used with other layout managers, it was designed to work with BoxLayout , so I wouldn't be surprised if it didn't work the way you wanted.

public class RevalidateRepaintTry {

    private static JPanel services;
    private static ArrayList<String> s;

    public RevalidateRepaintTry() {
        s = new ArrayList<String>();
        s.add("btt1");
        s.add("btt2");
        JFrame main = new JFrame();
        main.getContentPane().setLayout(new BoxLayout(main.getContentPane(), BoxLayout.PAGE_AXIS));
        services = new JPanel();
        repaintServices();
        JButton b = new JButton("ADD");
        b.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent evt) {
                String add = JOptionPane.showInputDialog("insert new");
                s.add(add);
                repaintServices();
            }

        });
        JButton bb = new JButton("DEL");
        bb.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent evt) {
                s.remove(s.size() - 1);
                repaintServices();
            }

        });
        main.add(services);
        main.add(b);
        main.add(bb);
        main.setVisible(true);
        main.pack();
        main.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    }

    public static void main(String arg[]) {

        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new RevalidateRepaintTry();
            }

        });

    }

    public static void repaintServices() {
        // services is a JPanel
        services.removeAll();
        JLabel lbl = new JLabel("SERVIZI");

        JPanel view = new JPanel(new GridBagLayout());
        JScrollPane scroll = new JScrollPane(view);

        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 0;

        view.add(lbl, gbc);

        // ########## SERVICES BUTTONS
        for (int i = 0; i < s.size(); i++) {
            JButton b = new JButton(s.get(i));
            if (i % 3 == 0) {
                gbc.gridy++;
                gbc.gridx = 0;
            } else {
                gbc.gridx++;
            }
            view.add(b, gbc);
        }
        // ########## END SERVICES BUTTONS

        view.add(Box.createVerticalGlue());

        services.add(scroll);

        services.revalidate();
        services.repaint();
    }
}

Note, I threw in a JScrollPane , it's probably not required

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM