0

My problem: I have a dynamic list of objects (dynamic in the sense that I don't know how many elemets it will contain). I need a button for each of them, and each button needs to "tell me" wich item has been selected. I don't reallt want to use checkboxes or radiobuttons (and as far as I know those can't store/return an object, right?) So, my solution was: Subclass JButton into ItemButton, which is a JButton, but I also has a field to store my Item. This way, in the listener attached to it I can just call a returnItem method, and I'll have the selected item, ready for use.

This is an inner class:

class toolButton extends JButton {
    toolButton(String name, int i) {
        super(name);
        this.index = i;     
    }

    public int getSelectionIndex() {
        return this.index;
    }

    private int index;
}

The method that generates my button list:

public void showItemList(ArrayList<Item> arrayList) {
    for (int i = 0; i < arrayList.size(); i++) {
        toolButton but = new toolButton(arrayList.get(i).getName()", i);   //getName() returns the name of the Item to be displayed
        this.add(but);
        but.addMouseListener(new MouseAdapter() {
            // Simple anonymous class for the listener
            public void mousePressed(MouseEvent e) {
                selected = but.getSelectionIndex(); //This sets the index number in the class that uses this method
            }
        }); 
    }       
}

It works, for what I'm doing at least. But I was wondering if there is a better method, or it this is at least acceptable, if it isn't frowned upon althoger. I don't think using getSource() on the actionEvent whould be of much use in this case. And I can't really write a listener for each button because I don't know how many there are.

Edit: What I'm trying to model is a simple dispenser, a dispenser has a collection of items (each of which has a name and a price). The dispenser class has a method "buy(Item item)". So what I want to do is have a button represent each avaiable item in the dispenser, and when the button is pressed, I want a reference to an Item to pass to the method buy(Item) (the button doesn't have to call the buy(Item) method itself). As I'm sure you understood, this is a class exercise, so I can't change the requirments.

Paul
  • 473
  • 6
  • 19
  • Just some thoughts: 1. the proper event handler for JButton would typically be the ActionListener, as in but.addActionListener(new Action(...)) 2. Typenames should start with an uppercase letter 3. If you could describe the requirements you're writing a solution for we could give better advice on suitable ways to tackle them. – rexford Jul 11 '16 at 21:51
  • Imo your solution is fine. This is an implementation of the decorator pattern. https://en.m.wikipedia.org/wiki/Decorator_pattern – Tinman Jul 11 '16 at 21:52
  • @Tinman thank you for your feedback, haven't studied patterns yet, I'll read about it! – Paul Jul 11 '16 at 21:54
  • I'm still unclear on your exact need, but `I have a dynamic list of objects` consider using a UI element geared towards displaying list data eg JList, JTable. – copeg Jul 11 '16 at 21:57
  • @copeg I added my requirements to the question – Paul Jul 11 '16 at 22:02
  • @rexford I followed your advice, added some more info, thank you! – Paul Jul 11 '16 at 22:04
  • Also consider the approach examined [here](http://stackoverflow.com/a/37063037/230513). – trashgod Jul 12 '16 at 01:34

1 Answers1

1

If the item names in the arrayList argument suffice to resolve the corresponding item categories, you will not have to subclass JButton, but can use it as is:

public void showItemList(ArrayList<Item> arrayList){
    for(int i = 0; i < arrayList.size(); i++){
        JButton but = new JButton(arrayList.get(i).getName());
        getContentPane().add(but);
        but.addActionListener(new ActionListener(){
           public void actionPerformed(ActionEvent event){
               String itemName = event.getActionCommand();
           } 
        });
    }
}

Some notes:

  • If you are subclassing JFrame, you should use its contentpane instead of adding buttons directly to it: getContentPane.add(button). If you're using a JPanel, your code is ok.
  • In your case, you should use the ActionListener instead of the MouseListener. As an added value, you get to query the label of the clicked button: event.getActionCommand(), so you know which item was selected and you don't have to implement a subclass to keep track of the index.
rexford
  • 5,192
  • 5
  • 27
  • 40
  • The name only is not enough for my case. I noticed you're using event.getActionCommand to retrieve the name of the item, but you never called but.setActionCommand(item.getName()) to set the ActionCommand to be the name of the item. Does that mean that ActionCommand, in absence of an explicit call of setActionCommand just checks the name of the JButton? (I think that's what you mean by "you get to query the label of the clicked button", but I want to make sure) – Paul Jul 11 '16 at 22:25
  • When a JButton is clicked, the method actionPerformed(ActionEvent) gets called for all registered ActionListeners. The ActionEvent argument contains information about the event, like the JButton object itself, which you can get by calling event.getSource(), and the label of the button event.getActionCommand(). This event object is created for you, you do not get to set its properties yourself, as in event.setActionCommand(String) – rexford Jul 11 '16 at 22:33
  • But if I do not call setActionCommand() to specify the action command for my button, what does event.getActionCommand() return? – Paul Jul 11 '16 at 22:42
  • See [`getActionCommand()`](https://docs.oracle.com/javase/8/docs/api/java/awt/event/ActionEvent.html#getActionCommand--). – trashgod Jul 12 '16 at 01:35