Results 1 to 8 of 8
  1. #1
    Mr Wogan is offline Member
    Join Date
    Dec 2012
    Posts
    14
    Rep Power
    0

    Default My ChangeListener isn't listening to changes made by my Action Listener

    Hello everyone. I'm puzzled by the behaviour of some of my event Listeners, I've written a small expample program to demonstrate my problem. It happens when an ActionListener that I have registered on a JButton fires and calls InsetTab on an instance of JTabbedPane. The JTabbedPane instance has a ChangeListener registered on it that I expected to notice the newly added tab and and perform a few simple tasks on the newly added tab. In this greatly simplified example these tasks are to merely create a new instance of the TabComponent that contains the JButton with the ActionListener registered to it that caused the creation of the new tab, change the title text of the newly created tab and finally to create a new instance of JPanel, colour it green and add it to the new tab window.

    The ChangeListener works fine when JTabbedPane.insertTab() is called from the containing JFrame's constructor method as you can see if you compile and run the example code given below. However when JTabbedPane.insertTab() is called from the ActionListener on the instance of JButton, the ChangeListener never fires. Can anyone help me understand why this is the case?

    Here's the example code that demonstrates what I'm talking about.

    EventTest.java
    Java Code:
    package eventtest;
    
    import java.awt.Color;
    import java.awt.Dimension;
    import javax.swing.GroupLayout;
    import javax.swing.GroupLayout.SequentialGroup;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JTabbedPane;
    import javax.swing.event.ChangeEvent;
    import javax.swing.event.ChangeListener;
    
    
    public class EventTest extends JFrame {
    
        private JTabbedPane tabbedPane = new JTabbedPane();
    
        public EventTest() {
            super();
            setDefaultCloseOperation(EXIT_ON_CLOSE);
            setTitle("Event Test");
            GroupLayout layout = new GroupLayout(getContentPane());
            getContentPane().setLayout(layout);
            SequentialGroup hGroup = layout.createSequentialGroup();
            SequentialGroup vGroup = layout.createSequentialGroup();
            layout.setHorizontalGroup(hGroup);
            layout.setVerticalGroup(vGroup);
            hGroup.addComponent(tabbedPane);
            vGroup.addComponent(tabbedPane);
            
            tabbedPane.addChangeListener(new PaneChangeListener());
            tabbedPane.insertTab("This title text will be replaced by the "
                    + "ChangeListener associated with this instance of "
                    + "JTabbedPane",
                    null,
                    null,
                    null,
                    tabbedPane.getTabCount());
            pack();
            setLocationRelativeTo(null);
        }
    
        private class PaneChangeListener implements ChangeListener {
            /* I don't understand why this listener is fired when InsertTab is 
             * called in EventTest's constructor, but is not fired when InsertTab 
             * is called from TabComponent.
             */
            @Override
            public void stateChanged(ChangeEvent evt) {
                JTabbedPane s = (JTabbedPane) evt.getSource();
                s.setTitleAt(s.getTabCount() - 1,
                        "Tab #" + Integer.toString(s.getTabCount()));
                s.setTabComponentAt(s.getTabCount() - 1, new TabComponent(s));
                JPanel panel = new JPanel();
            panel.setPreferredSize(new Dimension(500, 100));
            panel.setBackground(Color.GREEN);
            s.setComponentAt(s.getTabCount() - 1, panel);
            }
        }
    
        
        public static void main(String[] args) {
            java.awt.EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    new EventTest().setVisible(true);
                }
            });
        }
    }
    TabComponent.java:
    Java Code:
    package eventtest;
    
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import javax.swing.GroupLayout;
    import javax.swing.JButton;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.JTabbedPane;
    
    
    public class TabComponent extends JPanel {
    
        JTabbedPane parentPane;
        private static ActionListener defaultActionListener;
    
        public TabComponent(JTabbedPane pane) {
            super();
            parentPane = pane;
            JLabel label = new JLabel() {
                @Override
                public String getText() {
                    int indexInTabPane = parentPane.indexOfTabComponent(TabComponent.this);
                    if (indexInTabPane != -1) {
                        return parentPane.getTitleAt(indexInTabPane);
                    } else {
                        return "Not assigned to a TabbedPane";
                    }
                }
    
                @Override
                public void setText(String text) {
                    int indexInTabPane = parentPane.indexOfTabComponent(TabComponent.this);
                    if (indexInTabPane != -1) {
                        parentPane.setTitleAt(indexInTabPane, text);
                    }
                }
            };
    
    
            JButton button = new JButton("Add a tab");
            if (defaultActionListener == null) {
                defaultActionListener = new ButtonActionListener();
            }
            button.addActionListener(defaultActionListener);
            button.setFocusable(false);
    
            // This block of code is just LayoutManager stuff.
            GroupLayout layout = new GroupLayout(this);
            this.setLayout(layout);
            GroupLayout.SequentialGroup hGroup = layout.createSequentialGroup();
            GroupLayout.SequentialGroup vGroup = layout.createSequentialGroup();
            layout.setHorizontalGroup(hGroup);
            layout.setVerticalGroup(vGroup);
            int gapSize = 2;
            hGroup.addGap(gapSize)
                    .addComponent(label)
                    .addGap(gapSize)
                    .addComponent(button)
                    .addGap(gapSize);
    
            vGroup.addGap(gapSize)
                    .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
                    .addComponent(label)
                    .addComponent(button))
                    .addGap(gapSize);
        }
    
        protected class ButtonActionListener implements ActionListener {
            /* When the Button is pressed and this ActionListener fires and calls
             * InsertTab on parentPane,the ChangeListener registered on 
             * parentPane doesn't seem to pick up on the change. What am I missing?
             */
    
            @Override
            public void actionPerformed(ActionEvent evt) {
                JButton s = (JButton) evt.getSource();
                parentPane.insertTab("Hey! Where's my TabComponent?",
                        null,
                        null,
                        null,
                        parentPane.getTabCount());
            }
        }
    }

  2. #2
    DarrylBurke's Avatar
    DarrylBurke is online now Member
    Join Date
    Sep 2008
    Location
    Madgaon, Goa, India
    Posts
    11,244
    Rep Power
    19

    Default Re: My ChangeListener isn't listening to changes made by my Action Listener

    Nothing to do with whether the code is called form the constructor or from an ActionListener and everything to do with whether there actually is a change that should be notified. When you add the first tab to a JTabbedPane, it is automatically selected, and that is a change. Adding another tab doesn't change the selection.

    Add this line to the end of the actionPerformed(...) code and see the result for yourself:
    Java Code:
    parentPane.setSelectedIndex(parentPane.getComponentCount() - 1);
    Oh, and using almost any layout other than GroupLayout would have given you much shorter, eminently readable code. Since the JTabbedPane is the only component added to the JFrame, going with the default BorderLayout and adding it at the default position of CENTER would have been a one-liner instead of your 8 lines.

    db
    If you're forever cleaning cobwebs, it's time to get rid of the spiders.

  3. #3
    DarrylBurke's Avatar
    DarrylBurke is online now Member
    Join Date
    Sep 2008
    Location
    Madgaon, Goa, India
    Posts
    11,244
    Rep Power
    19

    Default Re: My ChangeListener isn't listening to changes made by my Action Listener

    Quote Originally Posted by DarrylBurke View Post
    Oh, and using almost any layout other than GroupLayout would have given you much shorter, eminently readable code. Since the JTabbedPane is the only component added to the JFrame, going with the default BorderLayout and adding it at the default position of CENTER would have been a one-liner instead of your 8 lines.
    And the JPanel's label and button could be added to the default FlowLayout, producing virtually the same result and saving another 16 LOC.

    See for yourself how much more readable this is:
    Java Code:
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import javax.swing.*;
    import javax.swing.event.ChangeEvent;
    import javax.swing.event.ChangeListener;
    
    public class EventTest extends JFrame {
    
      private JTabbedPane tabbedPane = new JTabbedPane();
    
      public EventTest() {
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setTitle("Event Test");
        add(tabbedPane);
    
        tabbedPane.addChangeListener(new PaneChangeListener());
        tabbedPane.insertTab("This title text will be replaced by the "
                + "ChangeListener associated with this instance of "
                + "JTabbedPane",
                null,
                null,
                null,
                tabbedPane.getTabCount());
        pack();
        setLocationRelativeTo(null);
      }
    
      private class PaneChangeListener implements ChangeListener {
    
        @Override
        public void stateChanged(ChangeEvent evt) {
          System.out.println("stateChanged");
          JTabbedPane tabbedPane = (JTabbedPane) evt.getSource();
          tabbedPane.setTitleAt(tabbedPane.getTabCount() - 1,
                  "Tab #" + Integer.toString(tabbedPane.getTabCount()));
          tabbedPane.setTabComponentAt(tabbedPane.getTabCount() - 1, new TabComponent(tabbedPane));
          JPanel panel = new JPanel();
          panel.setPreferredSize(new Dimension(500, 100));
          panel.setBackground(Color.GREEN);
          tabbedPane.setComponentAt(tabbedPane.getTabCount() - 1, panel);
        }
      }
    
      public static void main(String[] args) {
        java.awt.EventQueue.invokeLater(new Runnable() {
    
          @Override
          public void run() {
            new EventTest().setVisible(true);
          }
        });
      }
    }
    
    class TabComponent extends JPanel {
    
      JTabbedPane parentPane;
      private static ActionListener defaultActionListener;
    
      public TabComponent(JTabbedPane pane) {
        super();
        parentPane = pane;
        JLabel label = new JLabel() {
    
          @Override
          public String getText() {
            int indexInTabPane = parentPane.indexOfTabComponent(TabComponent.this);
            if (indexInTabPane != -1) {
              return parentPane.getTitleAt(indexInTabPane);
            } else {
              return "Not assigned to a TabbedPane";
            }
          }
    
          @Override
          public void setText(String text) {
            int indexInTabPane = parentPane.indexOfTabComponent(TabComponent.this);
            if (indexInTabPane != -1) {
              parentPane.setTitleAt(indexInTabPane, text);
            }
          }
        };
    
    
        JButton button = new JButton("Add a tab");
        if (defaultActionListener == null) {
          defaultActionListener = new ButtonActionListener();
        }
        button.addActionListener(defaultActionListener);
        button.setFocusable(false);
    
        add(label);
        add(button);
      }
    
      protected class ButtonActionListener implements ActionListener {
    
        @Override
        public void actionPerformed(ActionEvent evt) {
          JButton s = (JButton) evt.getSource();
          parentPane.insertTab("Hey! Where's my TabComponent?",
                  null,
                  null,
                  null,
                  parentPane.getTabCount());
          parentPane.setSelectedIndex(parentPane.getComponentCount() - 1);
        }
      }
    }
    db
    If you're forever cleaning cobwebs, it's time to get rid of the spiders.

  4. #4
    Mr Wogan is offline Member
    Join Date
    Dec 2012
    Posts
    14
    Rep Power
    0

    Default Re: My ChangeListener isn't listening to changes made by my Action Listener

    Right. So a change in the number of tabs in a JTabbedPane is of no concern to a ChangeListener unless the first tab is being added which then sets the selected index which is something that the ChangeListener is concerned with. Thanks for clearing that up. I assumed that using setSelectedIndex() at the end off my ActionListener would cause the ChangeListener to fire because I noticed that when I ran the code and clicked on a newly added tab that, as soon as I did, the ChangeListener would fire and do what I wanted it to. I was reasonably confident that I wasn't using the wrong kind of listener bacause Listeners Supported by Swing Components (The Java™ Tutorials seemed to imply that it was the only one that could be used with a JTabbedPane, but I didn't want to go and throw in a work-around like that without first understanding why what I was trying to do would not work. Is there any sort of event listener I can use to detect a change in the number of tabs in a JTabbedPane, or do I have to stick with switching to a newly created tab and back again to make the CangeListener fire?

  5. #5
    DarrylBurke's Avatar
    DarrylBurke is online now Member
    Join Date
    Sep 2008
    Location
    Madgaon, Goa, India
    Posts
    11,244
    Rep Power
    19

    Default Re: My ChangeListener isn't listening to changes made by my Action Listener

    I was reasonably confident that I wasn't using the wrong kind of listener bacause Listeners Supported by Swing Components (The Java™ Tutorials seemed to imply that it was the only one that could be used with a JTabbedPane
    To know what listeners can be used with a Swing component (or any class really) go through the API looking for addXxxListener(...) methods.

    You can use a PropertyChangeListener and listen for the "indexForNullComponent" property.
    Java Code:
        tabbedPane.addPropertyChangeListener("indexForNullComponent", new PropertyChangeListener() {
    
          public void propertyChange(PropertyChangeEvent evt) {
            JTabbedPane tabbedPane = (JTabbedPane) evt.getSource();
            tabbedPane.setTitleAt(tabbedPane.getTabCount() - 1,
                    "Tab #" + Integer.toString(tabbedPane.getTabCount()));
            tabbedPane.setTabComponentAt(tabbedPane.getTabCount() - 1, new TabComponent(tabbedPane));
            JPanel panel = new JPanel();
            panel.setPreferredSize(new Dimension(500, 100));
            panel.setBackground(Color.GREEN);
            tabbedPane.setComponentAt(tabbedPane.getTabCount() - 1, panel);
          }
        });
    db
    Last edited by DarrylBurke; 12-16-2012 at 06:29 PM.
    If you're forever cleaning cobwebs, it's time to get rid of the spiders.

  6. #6
    Mr Wogan is offline Member
    Join Date
    Dec 2012
    Posts
    14
    Rep Power
    0

    Default Re: My ChangeListener isn't listening to changes made by my Action Listener

    Thanks for the great information. I'm always referring back to the API anyway because my memory is very bad and I constantly forget method names and signatures, but I don't see any mention of which properies can be listened for, but Im assuming that these are the bound properties I've seen mentioned in the JavaBeans tutorial trail, so I guess I should go and have a good read of that before I start asking stupid questions that have probably been answered a million times before already.

    Thanks again for the great information. :)

  7. #7
    DarrylBurke's Avatar
    DarrylBurke is online now Member
    Join Date
    Sep 2008
    Location
    Madgaon, Goa, India
    Posts
    11,244
    Rep Power
    19

    Default Re: My ChangeListener isn't listening to changes made by my Action Listener

    When I want to know what property changes are fired I use this:
    Java Code:
      public void propertyChange(PropertyChangeEvent evt) {
        System.out.println(evt.getPropertyName());
      }
    All aren't bound properties. Bound properties are those which have an accessor and mutator (get../is... and set.. methods).

    db
    If you're forever cleaning cobwebs, it's time to get rid of the spiders.

  8. #8
    Mr Wogan is offline Member
    Join Date
    Dec 2012
    Posts
    14
    Rep Power
    0

    Default Re: My ChangeListener isn't listening to changes made by my Action Listener

    Awesome. thanks again for the useful info.

Similar Threads

  1. Action Listening/Handling Troubles.
    By gaeronf in forum AWT / Swing
    Replies: 4
    Last Post: 11-05-2012, 04:04 AM
  2. action listener
    By skuskusas in forum New To Java
    Replies: 4
    Last Post: 09-04-2012, 07:13 PM
  3. Replies: 2
    Last Post: 12-11-2011, 10:44 PM
  4. GUI Listener not Listening
    By m_patten2 in forum AWT / Swing
    Replies: 2
    Last Post: 11-24-2010, 01:06 AM
  5. Action Listener
    By greatmajestics in forum AWT / Swing
    Replies: 8
    Last Post: 03-25-2010, 05:39 PM

Posting Permissions

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