Question

I am currently writing a program that has 28 labels created and arranged in a grid within a for loop and I would like to add a mouseListener to each of the labels. I can add a mouseListener to each of the labels, but when I try to specify the label number and change that label's border color, I get a "local variable is accessed from inner class needs to be declared final" error. I understand the reasoning behind this error, but I cannot make the "labelNum" variable final as it needs to change since it's in the loop. What is a workaround solution here?

for (int c = 0; c < 7; c++) {
    for (int d = 0; d < 4; d++) {
                labelNum = c*4+d;
                inventorySlotLabels[labelNum] = new JLabel();
                inventorySlotLabels[labelNum].setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
                inventorySlotLabels[labelNum].setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0)));
                inventorySlotLabels[labelNum].setBounds(15+d*40+d*10, 15+c*30+c*10, 40, 30);
                inventorySlotLabels[labelNum].setIcon(new javax.swing.ImageIcon(getClass().getResource("Item.png")));

                inventorySlotLabels[labelNum].addMouseListener(new java.awt.event.MouseAdapter() {
                    public void mouseClicked(java.awt.event.MouseEvent evt) {
                        headerLabel.setText("Inventory Slot Clicked");
                        inventorySlotLabels[labelNum].setBorder(BorderFactory.createLineBorder(Color.WHITE));

                    }
                }
    }
}
Was it helpful?

Solution

public void mouseClicked(java.awt.event.MouseEvent evt) 
{
    headerLabel.setText("Inventory Slot Clicked");
    inventorySlotLabels[labelNum].setBorder(BorderFactory.createLineBorder(Color.WHITE));
}

Don't depend on a label number variable. Get the label from the MouseEvent:

public void mouseClicked(java.awt.event.MouseEvent evt) 
{
    headerLabel.setText("Inventory Slot Clicked");
    //inventorySlotLabels[labelNum].setBorder(BorderFactory.createLineBorder(Color.WHITE));
    JLabel label = (JLabel)evt.getSource();
    label.setBorder( ... );
}

Edit:

Actually, there is no reason to create 28 MouseListeners. Before your loop the code should be more like:

MouseListener ml = new MouseListener()
{
    public void mouseClicked(java.awt.event.MouseEvent evt) 
    {
        headerLabel.setText("Inventory Slot Clicked");
        //inventorySlotLabels[labelNum].setBorder(BorderFactory.createLineBorder(Color.WHITE));
        JLabel label = (JLabel)evt.getSource();
        label.setBorder( ... );
    }
};

Then in your loop you just use:

inventorySlotLabels[labelNum].addMouseListener( ml );

Whether you use an annoymouse inner class or an inner class, the better design approach is to use the source object from the MouseEvent, there is no need to create 28 MouseListener just to store a variable to index the label in the Array. In fact you don't even need to keep an Array when you use this approach which will simplify your code even more because now you are no longer restricted to a hardcoded value to determine the size of your array.

Also, there is no need to read the Icon 28 times. Just read the Icon once outside the loop and add the Icon to each label. You also don't need to create 28 Borders. Just create the Border once and share it.

Finally, you should NOT be using setBounds(...) to position and size the label. Use an appropriate layout manager, probably GridLayout, to display the labels in a grid.

OTHER TIPS

"I get a "local variable is accessed from inner class needs to be declared final" error. I understand the reasoning behind this error, but I cannot make the "labelNum" variable final as it needs to change since it's in the loop.What is a workaround solution here?"

Simple answer: The work around is to declare the objects globally as class members

Why anonymous inner class? use a simple inner class!

class InventoryMouseListener extends java.awt.event.MouseAdapter {
     private int labelNum;
     public InventoryMouseListener(int labelNum) {
         this.labelNum = labelNum;
     }
     @Override
     public void mouseClicked(java.awt.event.MouseEvent evt) {
         headerLabel.setText("Inventory Slot Clicked");
         inventorySlotLabels[labelNum].setBorder(BorderFactory.createLineBorder(Color.WHITE));
     }
}

and adding listener like

inventorySlotLabels[labelNum]
   .addMouseListener(new InventoryMouseListener(labelNum));

would work

Simple fix: declare labelNum final. Yes, it seems weird, but it works.

for (int c = 0; c < 7; c++) {
  for (int d = 0; d < 4; d++) {
    final int labelNum = c*4+d;
    ...

Alternate fix is just like MJafars (use an inner class) so I deleted it.

And, as noted in my original (now largely deleted) post, @Camickr has the proper solution, to look at the event source, and only create one listener.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top