javaswingjtableglazedlists

GlazedList update EventList in JTable


How can I update EventList list to update JTable? This is what I do:

        String[] headers = new String[]{"MNO", "NAME", "ID/REG No", "PHONE"};
        String[] properties = new String[]{"milkNo", "fullName", "nationalId", "phone1"};


        TextFilterator<Member> personTextFilterator = new TextFilterator<Member>() {

            @Override
            public void getFilterStrings(List list, Member m) {
                list.add(m.getFullName());
                list.add(m.getMilkNo());
                list.add(m.getNationalId());
                list.add(m.getPhone1());
            }
        };

        MatcherEditor<Member> textMatcherEditor = new TextComponentMatcherEditor<Member>(txtFilter, personTextFilterator);

        FilterList<Member> filterList = new FilterList<Member>(eventList, textMatcherEditor);

        TableFormat tf = GlazedLists.tableFormat(properties, headers);
        model = new EventTableModel<Member>(filterList, tf);

        selectionModel = new EventSelectionModel<Member>(filterList);
        tblMembers.setSelectionModel(selectionModel);

        tblMembers.setModel(model);

The probem is when i filter the records in table, and select a record and trying to update it, it create a new record in the table instead

int updatedRow = tblMembers.convertRowIndexToModel(tblMembers.getSelectedRow());
eventList.set(updatedRow, updatedMember);

Solution

  • Once you're using GlazedLists to power your JTable then you should only use EventTableModel and EventSelectionModel to interact with your JTable. The mistake you've made is to query the JTable directly for the selected row and get its index - but the JTable doesn't understand that it's backed by an EventList which maybe sorting/filtering items within. Therefore row i in the JTable will not necessarily correspond to element i in your EventList.

    In your code you have actually set up an EventSelectionModel, now it's just a matter of using it.

    if (!selectionModel.isSelectionEmpty()) {
        EventList<Member> selectedMembers = selectionModel.getSelected();
        for (int i = 0; i<selectedMembers.size(); i++) {
            Member member = selectedMembers.get(i);
            //update accordingly...
            //member.setXXX(...); 
            selectedMembers.set(i, member);
         }
    }
    

    You have to remember that JTables support multiple row selections (although that can be configured allow only single row selection) and therefore the EventSelectionModel sensibly will return an EventList of all the selected rows, even if only one is selected. Therefore you need to iterate over the returned list. There is an additional convenience that the EventList containing the selected items is backed by the source EventList, so you can make changes directly to that sub-list.

    Advanced

    Of course EventLists are great for observing the fundamental changes to a list: deletion, insertion, updating (ie replacing an existing object at index i with another object). However, wouldn't it be great if instead of calling selectedMembers.set(), we could update an object within an EventList directly and then let GlazedLists detect that property change?

    Well that's possible too. There's a useful list type called ObservableElementList and here it will listen to property changes for each object it contains and refresh accordingly. This is much more convenient than having to locate the object within the list and called eventList.set().

    You first need to make your class support property listeners, i.e., is a proper Java bean, and then use a ObservableElementList.Connector.

    I've created a small but complete example of this in action. In this example there is a text input at the top for filtering, a table containing a short list of authors, and a button at the bottom which will update the name of any selected row.

    import ca.odell.glazedlists.BasicEventList;
    import ca.odell.glazedlists.EventList;
    import ca.odell.glazedlists.FilterList;
    import ca.odell.glazedlists.GlazedLists;
    import ca.odell.glazedlists.ObservableElementList;
    import ca.odell.glazedlists.TextFilterator;
    import ca.odell.glazedlists.matchers.MatcherEditor;
    import ca.odell.glazedlists.swing.EventSelectionModel;
    import ca.odell.glazedlists.swing.EventTableModel;
    import ca.odell.glazedlists.swing.TextComponentMatcherEditor;
    import java.awt.BorderLayout;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.beans.PropertyChangeListener;
    import java.beans.PropertyChangeSupport;
    import java.util.List;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JScrollPane;
    import javax.swing.JTable;
    import javax.swing.JTextField;
    
    public class GlazedListSelectionObservable {
    
        private JFrame frame;
        private JTable table;
        private JTextField txtInput;
    
        private EventList<Person> people;
        private EventSelectionModel<Person> selectionModel;
    
        public GlazedListSelectionObservable() {
    
            setupGui();
            setupGlazedLists();
            populatedList();
            frame.setVisible(true);
        }
    
        private void setupGui() {
    
            frame = new JFrame("GlazedLists Selection Example");
            frame.setSize(600, 600);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
            txtInput = new JTextField();
            table = new JTable();
            frame.getContentPane().add(new JScrollPane(table), BorderLayout.CENTER);
    
            JButton updateTableButton = new JButton("Update selected row");
    
            updateTableButton.addActionListener(new ActionListener() {
    
                @Override
                public void actionPerformed(ActionEvent e) {
                    if (!selectionModel.isSelectionEmpty()) {
                        EventList<Person> selectedPeople = selectionModel.getSelected();
                        for (Person person: selectedPeople) {
                            person.setFirstName("David");
                            person.setLastName("Baldacci");
                        }
                    }
                }
            });
    
            frame.getContentPane().add(txtInput, BorderLayout.NORTH);
            frame.getContentPane().add(updateTableButton, BorderLayout.SOUTH);
    
        }
    
        private void populatedList() {
            people.add(new Person("John", "Grisham"));
            people.add(new Person("Patricia", "Cornwell"));
            people.add(new Person("Nicholas", "Sparks"));
            people.add(new Person("Andy", "Weir"));
            people.add(new Person("Elizabeth", "George"));        
        }
    
        private void setupGlazedLists() {
            people = new BasicEventList<Person>();
            MatcherEditor<Person> textMatcherEditor = new TextComponentMatcherEditor<Person>(txtInput, new PersonTextFilterator());
    
            ObservableElementList.Connector<Person> personConnector = GlazedLists.beanConnector(Person.class);
            EventList<Person> observedPeople = new ObservableElementList<Person>(people, personConnector);
    
            FilterList<Person> filteredPeople = new FilterList<Person>(observedPeople, textMatcherEditor);
    
            EventTableModel model = new EventTableModel(filteredPeople, GlazedLists.tableFormat(new String[]{"firstName", "lastName"} , new String[]{"First Name", "Last Name"}));
    
            selectionModel = new EventSelectionModel<Person>(filteredPeople);
    
            table.setModel(model);
            table.setSelectionModel(selectionModel);
        }
    
        class PersonTextFilterator implements TextFilterator<Person> {
    
            @Override
            public void getFilterStrings(List<String> list, Person person) {
                list.add(person.getFirstName());
                list.add(person.getLastName());
            }
    
        }
    
        public class Person {
    
            private String firstName;
            private String lastName;
    
            private final PropertyChangeSupport support = new PropertyChangeSupport(this);
    
            public Person() {
            }
    
            public Person(String firstName, String lastName) {
                this.firstName = firstName;
                this.lastName = lastName;
            }
    
            public String getFirstName() {
                return firstName;
            }
    
            public String getLastName() {
                return lastName;
            }
    
            public void setFirstName(String firstName) {
                final String oldFirstName = this.firstName;
                this.firstName = firstName;
                support.firePropertyChange("firstName", oldFirstName, firstName);
            }
    
            public void setLastName(String lastName) {
                final String oldLastName = this.lastName;
                this.lastName = lastName;
                support.firePropertyChange("lastName", oldLastName, lastName);
            }
    
            public void addPropertyChangeListener(PropertyChangeListener l) {
                support.addPropertyChangeListener(l);
            }
    
            public void removePropertyChangeListener(PropertyChangeListener l) {
                support.removePropertyChangeListener(l);
            }
    
        }
    
        public static void main(String[] args) {
            javax.swing.SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                  new GlazedListSelectionObservable();
                }
            });
        }
    
    }