4

I am working on a viewer, which uses a JList to show thumbnails of the pages of a document. The user can open a page by selecting it through in the JList, or throught other mechanisms, like entering the number in a text box.

When using the latter alternative, I want that the JList also selects the page. I do this using setSelectedIndex(), but this triggers an event, which causes the page to be loaded again, as if the user had clicked and selected the page in the JList, and this is causing me some problems.

How I see it, the index should be set some way (perhaps in the model) so that only the UI of the JList updates, without firing an event that the index has changed.

Is this possible? Or is there a better way to solve my issue?

Andrei Vajna II
  • 4,642
  • 5
  • 35
  • 38

2 Answers2

7
  1. You can remove all ListSelectionListener from the list, make a selection and then add them again.

  2. You can create your own ListSelectionModel with a method that doesn't throw the event and set it as a selection model to your JList, and then use getSelectionModel().yourSelectIndexMethod(index).

  3. You can also divert all your other methods of selection to the list, just find the corresponding entry if selecting the page by other means and select the item in the list. This way the item is selected and the page is loaded once.

Code for option 2:

public class ListTest extends JPanel{

private static final String[] items = new String[]{"1", "2", "3"};
private JList mylist;
private JComboBox myCombo;
private JTextArea myTA;

public ListTest() {
    setLayout(new BorderLayout());
    myCombo = new JComboBox(items);
    myCombo.addActionListener(new ActionListener(){

        @Override
        public void actionPerformed(ActionEvent e){
            valueSelectedCombo(myCombo.getSelectedIndex());
        }
    });
    JPanel pn = new JPanel();
    pn.setLayout(new BoxLayout(pn, BoxLayout.X_AXIS));
    pn.add(myCombo);
    pn.add(Box.createHorizontalGlue());
    pn.add(new JButton(new AbstractAction("Clear"){

        @Override
        public void actionPerformed(ActionEvent e){
            myTA.setText("");
        }
    }));
    add(pn, BorderLayout.NORTH);
    add(new JScrollPane(getJList()), BorderLayout.WEST);
    add(new JScrollPane(myTA = new JTextArea()), BorderLayout.CENTER);
}

private void valueSelectedList(int index){
    myTA.setText(myTA.getText() + "\n" + items[index]);
}

private void valueSelectedCombo(int index){
    myTA.setText(myTA.getText() + "\n" + items[index]);
    ((CustomSelectionModel)mylist.getSelectionModel()).setSelectionSilent(index);
}

private JList getJList(){
    if (mylist == null){
        mylist = new JList(items);
        mylist.setSelectionModel(new CustomSelectionModel());
        mylist.addListSelectionListener(new ListSelectionListener(){

            @Override
            public void valueChanged(ListSelectionEvent e){
                if (!e.getValueIsAdjusting()){
                    valueSelectedList(mylist.getSelectedIndex());
                }
            }
        });

        mylist.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        mylist.setPreferredSize(new Dimension(100, 106));

    }
    return mylist;
}

private static class CustomSelectionModel extends DefaultListSelectionModel{

    private boolean isSilent = false;

    public void setSelectionSilent(int firstIndex){
        isSilent = true;
        setSelectionInterval(firstIndex, firstIndex);
        isSilent = false;
    }
    protected void fireValueChanged(int firstIndex, int lastIndex, boolean isAdjusting){
        if (isSilent){
            return;
        }
        super.fireValueChanged(firstIndex, lastIndex, isAdjusting);
    }
}

public static void main(String[] args){
    JFrame frame = new JFrame("test");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    // Add content to the window.
    frame.add(new ListTest());

    // Display the window.
    frame.pack();
    frame.setSize(300, 200);
    frame.setVisible(true);
}

}
Taisin
  • 491
  • 2
  • 6
  • 3. I'm not sure I understand this. I want to have the page selected in the JList, so simply bypassing this is not an option. 2. If I do that, wouldn't it disable all ListSelectionEvents? Thus, when the user selects a page, no events will be triggered and no page would be loaded. 1. How do I remove all listeners? I can't find a method like that. The only way I see it is to getListSelectionListeners() and remove them one by one. Really cumbersome.... – Andrei Vajna II Jun 22 '10 at 13:06
  • 3 - all your other then list selections just go to the list and select the corresponding item. This way, the item in the list is selected and the page is loaded once. 2. You create a custom method in the model and call it when you just have to show the selection. No other methods are affected. 1. Yes, that's the only way. The easiest way, actually, would be to just load the page once unless the reload it explicitly requested. – Taisin Jun 22 '10 at 13:28
  • I updated the code, so you can see one way to do the option 2. – Taisin Jun 22 '10 at 13:32
  • +1 Very nice example. Note `text.append("\n" + items[index])` as an alternative in `valueSelectedCombo()`. – trashgod Jun 22 '10 at 15:27
  • @trashgod thanks for the note. Actually, I should have gone with `Document doc = myTA.getDocument(); doc.insertString(doc.getLength(), "\n", null); doc.insertString(doc.getLength(), items[index], null);` but well, for the test I opted for a simpler way. – Taisin Jun 24 '10 at 09:15
  • -1 for the invalid implementation of a ListSelectionModel: as per its contract, the model is _required_ to fire an event if its selection state has changed. Or the other way round: client code can safely assume that the value of getXXSelectedIndex is not changed until it receives a notification. – kleopatra May 11 '11 at 15:34
1

It looks like setSelectedIndex() is just a convenient way to set the selection in the ListSelectionModel. Maybe your ListModel could flag or cache the result so it won't get loaded a second time.

Catalina Island
  • 7,027
  • 2
  • 23
  • 42