0

I am making a game in which i have a main menu. I want to load the menu items from an array making it much more efficient to add new items to the menu. I want to have a mouse listener active for each JLabel the for loops creates so i can check if the mouse is hovering over it and to see when it has been clicked. How would i achieve this?

Here's the code i'm using currently, it creates the JLabels fine although the mouse listeners do no work.

private String[] items = { "Exit", "Mods", "Settings", "New Game", "Play Game" };

private String item_hover_left = "[";
private String item_hover_right = "]";

    private void createItems() {

        GameSound sound = new GameSound();

        int initialY = 951;

        JLabel[] item = new JLabel[items.length];

        for (i = 0; i < items.length; i++) {

            int y = initialY - (101 * i);

            item[i] = new JLabel();
            item[i].setText(items[i]);
            item[i].setForeground(ui_colour);
            item[i].setFont(new Font("Overseer", Font.PLAIN, 50));
            item[i].setHorizontalAlignment(SwingConstants.CENTER);
            item[i].setBounds(1582, y, 328, 99);
            add(item[i]);
            repaint();
            item[i].addMouseListener(new MouseAdapter() {

                public void mousePressed(MouseEvent pressed) {

                    if (pressed.getButton() == MouseEvent.BUTTON1) {

                        sound.playSound("menu_ok");

                    }

                }

                public void mouseEntered(MouseEvent entered) {

                    item[i].setText(item_hover_left + " " + items[i] + " " + item_hover_right);
                    sound.playSound("menu_hover");

                }

                public void mouseExited(MouseEvent exited) {

                    item[i].setText(items[i]);

                }

            });
        }
    }
Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: 5
  at net.fallout.menu.MainMenu$1.mouseEntered(MainMenu.java:87)
  at java.awt.Component.processMouseEvent(Unknown Source)
  at javax.swing.JComponent.processMouseEvent(Unknown Source)
  at java.awt.Component.processEvent(Unknown Source)
  at java.awt.Container.processEvent(Unknown Source)
  at java.awt.Component.dispatchEventImpl(Unknown Source)
  at java.awt.Container.dispatchEventImpl(Unknown Source)
  at java.awt.Component.dispatchEvent(Unknown Source)
  at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
  at java.awt.LightweightDispatcher.retargetMouseEnterExit(Unknown Source)
  at java.awt.LightweightDispatcher.trackMouseEnterExit(Unknown Source)
  at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
  at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
  at java.awt.Container.dispatchEventImpl(Unknown Source)
  at java.awt.Window.dispatchEventImpl(Unknown Source)
  at java.awt.Component.dispatchEvent(Unknown Source)
  at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
  at java.awt.EventQueue.access$500(Unknown Source)
  at java.awt.EventQueue$3.run(Unknown Source)
  at java.awt.EventQueue$3.run(Unknown Source)
  at java.security.AccessController.doPrivileged(Native Method)
  at > java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
  at  java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
  at java.awt.EventQueue$4.run(Unknown Source)
  at java.awt.EventQueue$4.run(Unknown Source)
  at java.security.AccessController.doPrivileged(Native Method)
  at > java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
  at java.awt.EventQueue.dispatchEvent(Unknown Source)
  at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
  at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
  at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
  at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
  at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
  at java.awt.EventDispatchThread.run(Unknown Source)
daniu
  • 14,137
  • 4
  • 32
  • 53
Bill Nye
  • 831
  • 1
  • 10
  • 15
  • What isn't working exactly, do you have any errors ? – Arnaud Jun 15 '18 at 06:42
  • The mouse listener isn't working, and yes there are alot of errors. – Bill Nye Jun 15 '18 at 06:43
  • at java.awt.Component.dispatchEvent(Unknown Source) at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source) at java.awt.LightweightDispatcher.retargetMouseEnterExit(Unknown Source) at java.awt.LightweightDispatcher.trackMouseEnterExit(Unknown Source) at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source) Here are the first few errors – Bill Nye Jun 15 '18 at 06:44
  • 1
    The top part of this stack trace is missing, please add it . – Arnaud Jun 15 '18 at 06:45
  • 1
    i added it to the question – Bill Nye Jun 15 '18 at 06:47
  • Possible duplicate of [What causes a java.lang.ArrayIndexOutOfBoundsException and how do I prevent it?](https://stackoverflow.com/questions/5554734/what-causes-a-java-lang-arrayindexoutofboundsexception-and-how-do-i-prevent-it) – AxelH Jun 15 '18 at 06:49
  • You are using a instance member `i` in `mouseenterer` (you did not used a local variable because it was not compiling I guess..). You can get the item with `event.getSource()` or simply use a `final int index = i;` before create the listener – AxelH Jun 15 '18 at 06:52

3 Answers3

1

In your anonymous class, you have this method :

public void mouseEntered(MouseEvent entered) {
    item[i].setText(item_hover_left + " " + items[i] + " " + item_hover_right);
    sound.playSound("menu_hover");
}

In it, you are using i, I can say it is a class member variable since it can only be that or a final local variable but i is increment in the loop, so it can only be a class member.

The problem is that i changes with the for loop and when you are done creating your label, i = items.length because of for (i = 0; i < items.length; i++) {. And when the event happen, item[i] will point outside of the array, explaining the exception.

First, don't use a class member for i, this doesn't make sense to keep that value and will give you problem (like this one).
I know you did because it was not compiling because you can use it in the method of the listener your are implementing, but you can use a final variable in the loop to solve this :

//declare `i` here
for (int i = 0; i < items.length; i++) {
    //declare `index` final and assign `i`, each MouseAdapter instance will use the correct `index`
    final int index = i;

    item[i].addMouseListener(new MouseAdapter() {
        public void mouseEntered(MouseEvent entered) {
            item[index].setText(item_hover_left + " " + items[index] + " " + item_hover_right);
            sound.playSound("menu_hover");
        }
    }

Alternative suggested by daniu since I focused too much on the index problem would be to declare the label final instead.

for (int i = 0; i < items.length; i++) {
    final JLabel label = new JLabel();
    item[i] = label;

    label.addMouseListener(new MouseAdapter() {
        public void mouseEntered(MouseEvent entered) {
            label.setText(item_hover_left + " " + label + " " + item_hover_right);
            sound.playSound("menu_hover");
        }
    }
}

Of course there is another way, you can get the source of the event with EventObject.getSource. You know it will be a JLabel since this is an anonymous class that can't be used somewhere else.

public void mouseEntered(MouseEvent entered) {
    JLabel label = (JLabel)entered.getSource();
    label.setText(item_hover_left + " " + label + " " + item_hover_right);
    sound.playSound("menu_hover");
}
AxelH
  • 14,325
  • 2
  • 25
  • 55
  • Instead of creating a new `index` variable, you can just as well create `final JLabel currentLabel = item[i]` and `final String text = items[i]` and use those in the handler methods. That retains the anonymous class, removes the index and is clearer code IMO. – daniu Jun 15 '18 at 07:17
  • 1
    I focused so much on the `i` problem that I don't even check the rest of the code .. but this is indeed much cleaner! @daniu. I am mostly here to provide the reason, not necessarily the best approch ;) – AxelH Jun 15 '18 at 07:21
1

I would recommend you to create a separate class for the MouseListener, say an inner one, instead of the anonymous one you're using.

class YourMouseListener extends MouseAdapter {
    JLabel label;
    String text;
    YourMouseListener(JLabel label, String text) {
        this.label = label;
        this.text = text;
    }
    // ...
    // use this.label instead of item[i], like this
    public void mouseEntered(MouseEvent entered) {

        label.setText(item_hover_left + " " + text + " " + item_hover_right);
        sound.playSound("menu_hover");

    }
    // ...
}

and then use this class in your loop

JLabel[] item = new JLabel[items.length];

for (i = 0; i < items.length; i++) {


    item[i] = new JLabel(items[i]);
    // ...
    add(item[i]);
    item[i].addMouseListener(new YourMouseListener(item[i], items[i]));
    repaint();
}

You probably don't even need the label member because you can just use the event's target to set the text to.

daniu
  • 14,137
  • 4
  • 32
  • 53
  • I thought about that but I feel creating a class just because of the index problem to be a bit to much. But that works – AxelH Jun 15 '18 at 07:00
  • @AxelH Well an anonymous class isn't really that much different - it does create a class of it's own as well after all (even if it does feel lighter somehow). An inner class you can at least test. – daniu Jun 15 '18 at 07:08
  • Yes, but not in the source code, especially if you need 10 different listeners (so 10 java file). (and in this logic, why would you ever create an anonymous class then ;) ) It will become quickly an issue. Just to get the correct `JLabel` where you could get it from the event ;) See the last part of my answer. – AxelH Jun 15 '18 at 07:09
  • You forgot to point the problem of the index used as a class member BTW. Explaining why the exception occurs. – AxelH Jun 15 '18 at 07:14
  • @AxelH Well I did mean this as an inner class to the component creating the menu, so no file number explosion here. – daniu Jun 15 '18 at 07:15
  • But the "why" is missing... You give a alternative implementation not explain why. The anonymous class doesn't have anything to do with the problem. – AxelH Jun 15 '18 at 07:17
  • @AxelH You're right, I refuse to actually answer the question! Since you do well enough in your answer. I'll upvote that instead ;) – daniu Jun 15 '18 at 07:28
  • I appreciate that ! ;) – AxelH Jun 15 '18 at 07:29
0

Here is the solution i used.

private void createItems() {

        int initialY = 951;

        JLabel[] item = new JLabel[items.length];

        for (int i = 0; i < item.length; i++) {

            final int selected = i;

            int y = initialY - (101 * selected);

            item[selected] = new JLabel();
            item[selected].setText(items[selected]);
            item[selected].setForeground(ui_colour);
            item[selected].setFont(new Font("Overseer", Font.PLAIN, 50));
            item[selected].setHorizontalAlignment(SwingConstants.CENTER);
            item[selected].setBounds(1582, y, 328, 99);
            add(item[selected]);
            item[i].addMouseListener(new ItemListener(item[i], selected));
            repaint();

        }
    }

class ItemListener extends MouseAdapter {
                GameSound sound = new GameSound();
                JLabel item_label;
        Integer item_index;

        ItemListener(JLabel label, int index) {
            this.item_label = label;
            this.item_index = index;
        }

        public void mousePressed(MouseEvent pressed) {

            if (pressed.getButton() == MouseEvent.BUTTON1) {

                sound.playSound("menu_ok");
                item_selected = item_index;
                checkItem();

            }

        }

        public void mouseEntered(MouseEvent entered) {

            item_label.setText(item_hover_left + " " + items[item_index] + " "
+ item_hover_right);            sound.playSound("menu_hover");

        }

        public void mouseExited(MouseEvent exited) {

            item_label.setText(items[item_index]);

        }

    }
Bill Nye
  • 831
  • 1
  • 10
  • 15