3

I'd like to be able to display a mutable list (using Swing) such that the first item is at the bottom in a fixed position, and subsequent items appear above it. Just the way a stack of stuff would appear in reality. The behavior is that of a FIFO queue (add to the top, remove from the bottom).

I can imagine a solution involving "padding" the list and then sorting it in reverse, or something like that, but I wondered if there might be a more direct way.

Example:
item[0]="Adam"
item[1]="Baker"
item[2]="Charlie"

should appear in 5-row list as:

+----------
|
|
| Charlie
| Baker
| Adam
+----------
Chap
  • 3,649
  • 2
  • 46
  • 84
  • See also [Creating a Sorted `JList` Component](http://java.sun.com/developer/technicalArticles/J2SE/Desktop/sorted_jlist/). – trashgod Jul 02 '11 at 20:18
  • @Chap: if I understand you correctly then you can use some LIFO based collection that enter elements in reverse order so the last element will be the first for the JList. you can use this [link](http://stackoverflow.com/questions/302460/java-collections-lifo-structure) for LIFO Collection. – Asad Rasheed Jul 02 '11 at 20:47

4 Answers4

4

Provide an implementation of ListModel and that can wrap whatever data structure is appropriate.

mKorbel
  • 109,525
  • 20
  • 134
  • 319
alphazero
  • 27,094
  • 3
  • 30
  • 26
  • Doesn't ListModel just say what items are in the JList and in what order? If you have one item, the JList will still show it on top, but I think Chap wants it on the bottom. – Ken Jul 02 '11 at 20:17
  • Yes - I have updated original post to show visually what I'm after. – Chap Jul 02 '11 at 20:21
  • JList assumes that the "first" item should be displayed at the **top**. I guess I'm looking for a "JUpsideDownList" which displays the first item at the **bottom**, instead of my having to futz around with cleverly rearranging the data in the model. – Chap Jul 02 '11 at 20:37
  • You can do this in a ListModel. Assuming theList is a java.util.List, Your ListModel implementation can return theList.get( theList.size() - index ) in order to return the objects in reverse order. That way, your actual List class can maintain the 0th item in the 0th index, but it will appear last because the ListModel flips it. – Jesse Barnum Jul 02 '11 at 20:58
  • @Jesse Barnum is correct. By providing the impl. you are in control. e.g. return a padded list in reversed order. – alphazero Jul 02 '11 at 22:08
  • @Jesse, @alphazero: to clarify - you're saying I will need to pad theList to size 5, and then I can get it to be displayed "upside down" with **`theList.get(theList.size() - index)`**? Too bad about having to pad, but still an improvement. – Chap Jul 02 '11 at 23:54
  • The padding can happen in the list model too, no need to add empty items to the actual list. Just return null for the rows that you want to appear as empty, and return the number of rows as whatever you want (doesn't have to match the size of the java.util.List) – Jesse Barnum Jul 03 '11 at 03:06
4

If you don't want to create a custom model, then you can use the DefaultListModel. Then instead of using:

model.addElement( element );

you can use:

model.add(0, element);

and the elements will be displayed in the order you wish.

The following code also show how you might make the list look bigger than it really is:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;

public class ListBottom2 extends JFrame
{
    JList list;
    JTextField textField;

    public ListBottom2()
    {
        DefaultListModel model = new DefaultListModel();
        model.add(0, "Adam");
        model.add(0, "Baker");
        model.add(0, "Charlie");
        list = new JList(model);
        list.setVisibleRowCount(5);

        JPanel box = new JPanel( new BorderLayout() );
        box.setBackground( list.getBackground() );
        box.add(list, BorderLayout.SOUTH);

        JScrollPane scrollPane = new JScrollPane( box );
        scrollPane.setPreferredSize(new Dimension(200, 95));
        add( scrollPane );

        textField = new JTextField("Use Enter to Add");
        getContentPane().add(textField, BorderLayout.NORTH );
        textField.addActionListener( new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                JTextField textField = (JTextField)e.getSource();
                DefaultListModel model = (DefaultListModel)list.getModel();
//              model.addElement( textField.getText() );
                model.add(0, textField.getText());
                int size = model.getSize() - 1;
                list.scrollRectToVisible( list.getCellBounds(size, size) );
                textField.setText("");
            }
        });
    }

    public static void main(String[] args)
    {
        ListBottom2 frame = new ListBottom2();
        frame.setDefaultCloseOperation( EXIT_ON_CLOSE );
        frame.pack();
        frame.setLocationRelativeTo( null );
        frame.setVisible( true );
    }
}
camickr
  • 321,443
  • 19
  • 166
  • 288
  • This does what I want. I gather that **model.add(0,"Charlie")** adds the element to the _beginning_ of the list. However, I'm not quite sure how you managed to get the list to be "bottom justified." Is **BorderLayout.SOUTH** somehow key to the solution? – Chap Jul 05 '11 at 15:20
  • Yes, the BorderLayout is the trick. the list is displayed in the south. If there is any extra space avaiable to the "box" panel it is allocated to the "center" of the BorderLayout. As the box height grows/shrinks, the spaces in the center will grow/shrink while the list stays the same. – camickr Jul 05 '11 at 20:25
  • Okay, that's exactly what I hoped for. – Chap Jul 05 '11 at 22:54
2

Just reverse your array. In one line: (This isn't exactly efficient, but it'll work):

JList list = new JList(Collections.reverse(Arrays.asList(items)).toArray());

Note: It doesn't make sense to have a different UI component which reads the data differently. A ListModel holds the data according to the contract between itself and JList. Creating a new contract is pointless, when its trivial to reorganize the data based on how you want to visualize it, and more importantly, based on UI standards of the operating system.

If anything, you want a reverse ListModel, but, on the same note, it doesn't make sense to have a full implementation of ListModel that just goes in the opposite direction, when really, all you need to do is reverse the order of the backing data structure.

So, that's what you do.

Edit to add:

I read more of what you're trying to do and it looks like you want a fixed size list where data starts at the end (like a stack). In that case, what you really need to do is implement your own ListModel. Take a look at AbstractListModel and you should be able to extend it and provide your own data.

If you do that, your class will then be essentially (consider this p-code, this may not be 100% right):

class ReverseListModel extends AbstractListModel {

  public Object getElementAt(int i) {
    int dataIndex = fixedSize - i;
    if(dataIndex > data.length) 
      return "";
    else
      return data[dataIndex];
  }

  public int getSize() {
    return fixedSize;
  }
}
Reverend Gonzo
  • 39,701
  • 6
  • 59
  • 77
  • I'd rather not tie myself down to a fixed-size list, but I'm undecided yet. I'm simulating an old-fashioned card reader, with a Hopper containing Decks (of Cards). The "first deck" is always at the bottom, where cards are read from. Logically, it's just a queue, but physically it's subject to the design of an IBM card reader (and the laws of gravity :-). I'm not sure why a "different UI" component is a bad idea - it just needs to _display_ a normal list "on its head". – Chap Jul 03 '11 at 00:18
0

Collections utility is enough to reverse a collections Collections.reverse(list)

Thomson Ignesious
  • 689
  • 1
  • 8
  • 25