Search code examples
javaswingeventsjpanelswingworker

JPanel with a ComponentListener


I have a Main JPanel, and its layout is set to the CardLayout.

Main JPanel has 4 Cards: Card1JPanel, Card2JPanel, Card3JPanel, Card4JPanel.

I also have a SwingWorker class called "MySwingy" that performs something forever while its loop flag is set to true.

When Card1JPanel is VISIBLE/SHOWN I want to execute MySwingy worker.

When Card1JPanel is NOT VISIBLE/HIDDEN I want to stop the MySwingy worker;

The following is the code I currently have and I am wondering if there is a better/cleaner way to tackle the problem above. In the following code example you will see that I use a ComponentListener for Card1JPanel to detect if it is shown or hidden, but what if I have many, many Cards each with its own ComponentListener event will these event listeners slow down my application?

Thank you very much

public class Card1JPanel extends JPanel{

    private MySwingy mySwingy;
    private JTable tableDatabase;

    public Card1JPanel(){
        initComponents();//tableDatabase is drawn here among other things
        this.addComponentListener(new myListener());
    }

    class myListener implements ComponentListener {

        @Override
        public void componentHidden(ComponentEvent e) {
            try{
                mySwingy.stopExecuting();//A flag inside of the worker gets set to false that terminates the while(true) loop.
            }catch(NullPointerException ex){
            }
        }

        @Override
        public void componentMoved(ComponentEvent e) {
        }

        @Override
        public void componentResized(ComponentEvent e) {
        }

        @Override
        public void componentShown(ComponentEvent e) {
            mySwingy = new MySwingy(tableDatabase);
            mySwingy.execute();
        }
    }
}

EDIT:Shows what mySwingy does:

MySwingy is used to parse data from an SQL database and to update the tableDatabase when the data in the database has changed. The tableDatabase is on the Card1JPanel and it is updated on the EDT from MySwingy using the SwingUtilities.InvokeLater. In my application I have many Cards (JPanels) with JTables and SwingWorkers that update their JTables on the EDT. Now, I am sure that the GUI would freeze if all of these JTables were updated constantly on the EDT from their SwingWorkers. Therefore, how do I stop the SwingWorker from updating the JTable when its JPanel is not Visible? That is my question.

public class MySwingy extends SwingWorker<Void,Void>{

    private JTable tableDatabase;
    private boolean isStopExecuting;
    private boolean isDatabaseDataChanged;

    public MySwingy(JTable tableDatabase){
        this.tableDatabase = tableDatabase
        isStopExecuting = false;
        isDatabaseDataChanged = false;
    }

    public void stopExecuting(){
        isStopExecuting = true;
    }

    @Override
    public Void doInBackground(){

        while(isStopExecuting == false){

            //Here is the code that parses some data from an SQL database and if the data in the SQL database
            //has changed the isDatabaseDataChanged boolean flag is set to true,
            //else isDatabaseDataChanged boolean flag is set to false;

            if(isDatabaseDataChanged == true){

                isDatabaseDataChanged = false;

                SwingUtilities.InvokeLater(new Runnable(){

                    @Override
                    public void run(){

                    //Update tableDatabase rows with new data

                    }
                });
            }
        }

    return null;

    }
}

Solution

  • Don't toggle the worker; let it run. Instead, conditionally update a view only when it isVisible(), either directly via process() or indirectly via a property change.

    Addenda: Several helpful comments merit closer examination.

    • As @Andrew notes, much depends on the worker's task. One might evolve a simulation's model on a background thread, but any rendering must take place on the event dispatch thread.

    • I'm no less confused than @mKorbel; you've asked for a critical appraisal of a solution without saying what problem it is meant to solve.

    • @kleopatra notes that SwingWorker is designed to be executed only once. Any attempt to control a doInBackground() loop from another thread will be awkward to synchronize. Also examine your task's anticipated latency, mentioned here. An instance of javax.swing.Timer, which exposes start() and stop() methods, may be an alternative.

    • Updated question: If your worker needs to poll a data source having variable latency, use a java.util.Timer in the background thread to perform the trigger query at a sustainable pace.