Results 1 to 7 of 7
  1. #1
    frenk_castle is offline Member
    Join Date
    Mar 2010
    Location
    Belgrade, Serbia
    Posts
    27
    Rep Power
    0

    Default JComboBox ActionListener and Swing Thread safety

    I am developing an application that among other things has two JComboBoxes. It is convenient that both combo boxes has the same action listener. Basically at the most upper level I want to create a JFrame class which will contain all the GUI elements, data storage class which will contain internal data needed for application to run, data access class which will work with files and control class which will control the work of all classes. I want control class to be the action listener for the two combo boxes.

    Question one: Is there a smarter way to determine which combo box trigered the event than

    Java Code:
    public void actionPerformed(ActionEvent event)
    {
        Object source = event.getSource();
    
        if(source == comboBox1)
        {
            comboBox1ControlMethod();
        }
        else if(source == comboBox2)
        {
            comboBox2ControlMethod();
        }
        else
        {
            JOptionPane.showMessageDialog(null, errorString);
        }
    }

    Mister Cay S. Horstmann says in his book Core Java 8 Edition that this is possible way to solve the problem but he doesn't recommend it.

    Second question: Imagine that my action listener is written in the above fashion. And imagine that comboBox1ControlMethod and comboBox2ControlMethod launch a separate thread which will gather appropriate data do there work and then terminate.

    Let say user make a selection in the comboBox2. comboBox2ControlMethod launches the thread which in turn determine which item from the combo box user selected, then read appropriate files, update the internal data class, and that repaint the frame. By repainting the frame I mean only changing the text in several JTextField classes and changing which item is selected in several JComboBoxes. All this components whose data I change like this don't have ActionListener associated with them. I care about these components contest only when the user decided to save his work.

    I read in the API that Swing is not thread save.

    Can I launch a separate thread to do this work or do I have to do this work in the same thread that controls the GUI.

    If I was unclear tell me and I will try to explain better.

    Thanks in advance.

  2. #2
    Fubarable's Avatar
    Fubarable is offline Moderator
    Join Date
    Jun 2008
    Posts
    19,316
    Blog Entries
    1
    Rep Power
    26

    Default

    Regarding your second question: Yes, it's best to use a background thread such as a SwingWorker or a plain vanilla background thread when calling time or CPU-intense processes as long as you take care to call most Swing code from the EDT.

  3. #3
    frenk_castle is offline Member
    Join Date
    Mar 2010
    Location
    Belgrade, Serbia
    Posts
    27
    Rep Power
    0

    Default

    Quote Originally Posted by Fubarable View Post
    Regarding your second question: Yes, it's best to use a background thread such as a SwingWorker or a plain vanilla background thread when calling time or CPU-intense processes as long as you take care to call most Swing code from the EDT.
    But what it means "as long as you take care to call most Swing code from the EDT". What is most but enough and what is too much. Let say that I have a JPanel which contains JComboBox comboBox and JTextField textField. And let say that inside comboBox ActionListerer I start a thread.

    Java Code:
    public void actionPerformed(ActionEvent e)
    {
        t.start();
    }
    Then inside thread t I call
    Java Code:
    //some code
    textField.setText(someString);
    //some code
    Have I violated thread safety?

    Thanks in advance.

  4. #4
    Michael Dunn is offline Member
    Join Date
    Jul 2008
    Posts
    62
    Rep Power
    0

    Default

    > Have I violated thread safety?

    any time you access a swing component it is far safer to do it via a
    SwingUtilities.invokeLater(..)
    which will add it to the end of the Swing thread (EDT)

    according to the api, some actions are thread safe, but I wouldn't trust that.

    in your first post, when there are only 2 sources, I hate this (in a single listener):
    Object source = event.getSource();
    I would prefer to add a specific listener to a component.

    a calculator with 10 numeric buttons and numerous operands would be different

  5. #5
    frenk_castle is offline Member
    Join Date
    Mar 2010
    Location
    Belgrade, Serbia
    Posts
    27
    Rep Power
    0

    Default

    SwingUtilities.invokeLater seems to be the answer to second problem.

    Regarding the first I found the better way to do it. It is convenient for me to have only one class that controls the application. Because both action listeners needs to call the same methods. I will make two inner classes which will be actionListeners and both of them will be able to call the methods of the enclosing class.

    If I understood ActionListener correctly I need a class that implement ActionListener interface. That class call it listenClass need to have actionPerformed method. I then add listenClass using addActionListener method to some other class which in this example we will call talkClass.

    Java Code:
    talkClass.addActionListener(listenClass);
    If I understood event handling only one method of the class can be called when event occurs actionPerformed method.

    That means that I either need to have separate class for each event I want to handle or to somehow distinguish the source of the event in actionPerformed method. That is the reason I tried the first solution. It is convinient to have a single event handling class because most methods for handling both events are identical. It is more complicated to have two classes with identical methods or two classes derived from same class that has methods needed for both handler classes.

    Now. My brother who is Java programmer but one that had very little need for GUI and event handling, he write mostly server side banking software, tells me that I can have more than one method in a single class that can be called when event occurs.

    To try to explain. Let say that I have one listenClass. listenClass has methods called action1 and action2. And let say I have two talk classes talkClass1 and talkClass2. He is convinced that I can set listenClass to be ActionListener for both talkClass1 and talkClass2 but than I can set that when talkClass1 emits the event action1 method is called and when talkClass2 emits the event action2 method is called.

    He has very little experience in GUI programming and all that he has are with graphic designer from NetBeans. He does server side software most of the time so he might be mistakening. In case he is not. Can what he is suggesting to me be done? If it can be done how can it be done?

    Thanks in advance.

  6. #6
    Fubarable's Avatar
    Fubarable is offline Moderator
    Join Date
    Jun 2008
    Posts
    19,316
    Blog Entries
    1
    Rep Power
    26

    Default

    This is ugly code, sorry, but it does show one control class delivering more than one action listener...
    Java Code:
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JScrollPane;
    import javax.swing.JTextArea;
    
    public class MultListeners {
      private static void createAndShowUI() {
        View view = new View();
        Control control = new Control();
        control.setView(view);
        view.addActionListener1(control.getListener1());
        view.addActionListener2(control.getlistener2());
        
        JFrame frame = new JFrame("Multiple Listeners");
        frame.getContentPane().add(view.getMainPanel());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
      }
    
      public static void main(String[] args) {
        java.awt.EventQueue.invokeLater(new Runnable() {
          public void run() {
            createAndShowUI();
          }
        });
      }
    }
    
    class View {
      private JPanel mainPanel = new JPanel();
      private JButton button1 = new JButton("Button 1");
      private JButton button2 = new JButton("Button 2");
      private JTextArea displayArea = new JTextArea(15, 25);
      
      View() {
        mainPanel.add(button1);
        mainPanel.add(button2);
        mainPanel.add(new JScrollPane(displayArea, 
            JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, 
            JScrollPane.HORIZONTAL_SCROLLBAR_NEVER));
        displayArea.setEditable(false);
        displayArea.setFocusable(false);
      }
      
      public void addActionListener1(ActionListener al) {
        button1.addActionListener(al);
      }
      
      public void addActionListener2(ActionListener al) {
        button2.addActionListener(al);
      }
      
      public void displayAppendText(String text) {
        displayArea.append(text);
      }
      
      public JPanel getMainPanel() {
        return mainPanel;
      }
    }
    
    class Control {
      private ActionListener listener1;
      private ActionListener listener2;
      private View view;
      
      public void setView(View view) {
        this.view = view;
      }
    
      public ActionListener getListener1() {
        if (listener1 == null) {
          listener1 = new ActionListener() {
            public void actionPerformed(ActionEvent e) {
              sendText(1);
            }
          };
        }
        return listener1;
      }
    
      public ActionListener getlistener2() {
        if (listener2 == null) {
          listener2 = new ActionListener() {
            public void actionPerformed(ActionEvent e) {
              sendText(2);
            }
          };
        }
        return listener2;
      }
    
      private void sendText(int i) {
        view.displayAppendText("This is being sent by listener number " + i + "\n");
      }
    }

  7. #7
    frenk_castle is offline Member
    Join Date
    Mar 2010
    Location
    Belgrade, Serbia
    Posts
    27
    Rep Power
    0

    Default

    First of all thank you. Writing that amount of code to help me learn. I appreciate it. I don't want to be ungrateful but this is one control class that contains two classes both of which implement ActionListener interface. It is very elegant work around the problem but it is important to me that I understood event handling correctly. I need class that implement ActionListener interface. That class need to implement actionPerformed method which we be called when associated method occur.

    Thank you all for your help.

Similar Threads

  1. ActionListener NOT working
    By zed420 in forum New To Java
    Replies: 3
    Last Post: 12-20-2009, 04:40 PM
  2. Replies: 2
    Last Post: 12-10-2009, 02:58 PM
  3. ActionListener new user
    By AmyHill in forum Java Applets
    Replies: 1
    Last Post: 11-16-2009, 08:15 PM
  4. How to access the ActionListener
    By jboy in forum New To Java
    Replies: 3
    Last Post: 10-15-2009, 07:04 PM
  5. Inside a Timer thread loop,how to refresh a JTable in swing
    By neha_negi in forum Threads and Synchronization
    Replies: 3
    Last Post: 09-04-2009, 02:45 AM

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •