Archief - [JAVA] ActionListener - 'needs to be declared final'

Het archief is een bevroren moment uit een vorige versie van dit forum, met andere regels en andere bazen. Deze posts weerspiegelen op geen enkele manier onze huidige ideeën, waarden of wereldbeelden en zijn op sommige plaatsen gecensureerd wegens ontoelaatbaar. Veel zijn in een andere tijdsgeest gemaakt, al dan niet ironisch - zoals in het ironische subforum Off-Topic - en zouden op dit moment niet meer gepost (mogen) worden. Toch bieden we dit archief nog graag aan als informatiedatabank en naslagwerk. Lees er hier meer over of start een gesprek met anderen.

Poplipo

Legacy Member
Hallo,

voor een programma wil'k een soort chatvenster implementeren.
Dus met een textfield, en dan zo'n actionlistener toestand.

Code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

class ChatVenster {
    
    public void Chat (JFrame ven, Container panel, Graphics gr){
        
        Font letType = new Font ("Calibri", Font.BOLD, 25);

// chatlabel
        
        JLabel lblChat = new JLabel ("Chat");
        lblChat.setBounds (50, 0, 70, 70);
        lblChat.setFont (letType);
        lblChat.setForeground (Color.white);
        
        panel.add (lblChat);
        panel.repaint();
        
// chatvenster
        
        JTextField txtChat = new JTextField ();
        txtChat.setBounds (110, 25, 200, 20);
        txtChat.setForeground (Color.black);
        
        panel.add(txtChat);
        panel.repaint();
        
// chatuitvoerlabel        
        
        JLabel lblUitvoer = new JLabel (txtChat.getText());
        lblUitvoer.setBounds (50, 600, 950, 20);
        lblUitvoer.setFont (letType);

// Action event
        
        txtChat.addActionListener (new ActionListener (){
            public void actionPerformed (ActionEvent gebeurtenis){
                panel.add (lblUitvoer);
                panel.repaint();
            }
        }
        );
                    
    }
    
}

Ik krijg steeds deze error:
local variable lblUitvoer is accessed from within inner class; needs to be declared final
panel.add (lblUitvoer);
^

Dat zegt hij ook van m'n panel
Alsek dan JLabel voor lblUitvoer zet, is er een haakje en ; tekort? :unsure:

Thx in advance :)

Yngwie

Legacy Member
Je maakt die ActionListener aan als een anonymous inner class. Die actionPerformed methode zit dus in een andere class en daardoor probeer je lblUitvoer en panel ergens te gebruiken waar ze niet meer binnen hun scope vallen.

Je kan dit oplossen door beide variabelen te declareren als fields binnen je klasse of het woordje 'final' voor hun declaratie plaatsen.

Wat ik meestal doe bij een actionListener of andere callback is binnen de actionPerformed methode geen logica zetten maar wel een call doen naar een nieuwe methode zodat je uit de inner class geraakt.

MilM

Legacy Member
Je doet gewoon "class ChatVenster implements ActionListener"

en dan declareer je de methode "public void actionPerformed (ActionEvent gebeurtenis)" als gewone methode in uw ChatVenster klasse.

Fraggie

Legacy Member
Zou je ook niet gewoon die variablelen kunnen veranderen in:
ChatVenster.this.(varname)


Dus bv:

ChatVenster.this.panel.add (ChatVenster.this.lblUitvoer);
ChatVenster.this.panel.repaint();

Mits je een attribuut van panel & lblUitvoer maakt.

?

Poplipo

Legacy Member
Wat bedoel je met 'mits je een attribuut van panel en lblUitvoer maakt?

Ik heb es geprobeerd dat de event is:

System.out.println (lblUitvoer);

hij gaf een error van hier tot in tokyo, dus dachtek, hij kan geen JLabel printen, dus heb ik een string ervan gemaakt. Nu doet hij gewoon niets :p

Er is niets mis met m'n event, want als ik een standaard tekstje ingeef dat hij moet printen, doet hij dat.

gaat (var).getText (); niet bij een string ofzo?

Fraggie

Legacy Member
Code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

class ChatVenster {
    private Container panel; // #
    private JLabel lblUitvoer; // #

    public void Chat (JFrame ven, Container panel, Graphics gr){
        this.panel = panel; // #

        Font letType = new Font ("Calibri", Font.BOLD, 25);

// chatlabel

        JLabel lblChat = new JLabel ("Chat");
        lblChat.setBounds (50, 0, 70, 70);
        lblChat.setFont (letType);
        lblChat.setForeground (Color.white);

        panel.add (lblChat);
        panel.repaint();

// chatvenster

        JTextField txtChat = new JTextField ();
        txtChat.setBounds (110, 25, 200, 20);
        txtChat.setForeground (Color.black);

        panel.add(txtChat);
        panel.repaint();

// chatuitvoerlabel

        lblUitvoer = new JLabel (txtChat.getText()); // #
        lblUitvoer.setBounds (50, 600, 950, 20);
        lblUitvoer.setFont (letType);

// Action event

        txtChat.addActionListener (new ActionListener (){
            public void actionPerformed (ActionEvent gebeurtenis){
                ChatVenster.this.panel.add(ChatVenster.this.lblUitvoer); //#
                ChatVenster.this.panel.repaint(); //#
            }
        }
        );

    }
}
Waar ik iets veranderd heb, heb ik een //# gezet.

Poplipo

Legacy Member
Vree wel bedankt, geen errors nu, maar nu gebeurt er niets? :s

Wa hebje eig precies gedaan?


Kzal m'n main en m'n ander deel vd GUI ook geven, als er daar iets kan ingevonden worden


Main:

Code:
import javax.swing.*;
import java.util.*;
import java.awt.*;

class Ganzebord {
	
	public static void main (String [] args){
		
		JFrame venster = new JFrame ("Ganzebord");
		venster.setSize (1000,700);
		venster.setVisible (true);
		venster.setLocation (0,0);
		
		Container paneel = venster.getContentPane ();
		Graphics g = paneel.getGraphics ();
					
		Teken bord = new Teken();
		bord.TekenBordSpelers (venster, paneel, g);
		
		ChatVenster ch = new ChatVenster();
		ch.Chat (venster, paneel, g);

	}
}

GUI:

Code:
import javax.swing.*;
import java.awt.*;


class Teken {
	
	public void TekenBordSpelers (JFrame ven, Container panel, Graphics gr){
		
		String speler1, speler2, speler3, speler4;
/*		
		JLabel lblPrent;
		lblPrent = new JLabel (new ImageIcon("Ganzenbord.png"));
		lblPrent.setBounds (50,50,800,572);
		lblPrent.setVisible (true);
		panel.add (lblPrent);
		panel.repaint();
*/
		panel.setBackground (Color.gray);
		Font lettType = new Font ("Arial", Font.BOLD, 20);


//speler1
		speler1 = JOptionPane.showInputDialog (ven, "Hoe heet speler 1? (groen)");
		JLabel lblNaam1 = new JLabel (speler1);
		lblNaam1.setForeground (Color.green);
		lblNaam1.setBounds (870, 60, 100, 100);
		lblNaam1.setFont (lettType);
		
		panel.add(lblNaam1);
		panel.repaint();

		
//speler2		
		speler2 = JOptionPane.showInputDialog (ven, "Hoe heet speler 2? (geel)");		
		JLabel lblNaam2 = new JLabel (speler2);
		lblNaam2.setForeground (Color.yellow);
		lblNaam2.setBounds (870, 100, 100, 100);
		lblNaam2.setFont (lettType);
		
		panel.add(lblNaam2);
		panel.repaint();
				
		
//speler3		
		speler3 = JOptionPane.showInputDialog (ven, "Hoe heet speler 3? (blauw)");		
		JLabel lblNaam3 = new JLabel (speler3);
		lblNaam3.setForeground (Color.blue);
		lblNaam3.setBounds (870, 140, 100, 100);
		lblNaam3.setFont (lettType);
		
		panel.add (lblNaam3);
		panel.repaint();
		
		
//speler4
		speler4 = JOptionPane.showInputDialog (ven, "Hoe heet speler 4? (rood)");		
		JLabel lblNaam4 = new JLabel (speler4);
		lblNaam4.setForeground (Color.red);
		lblNaam4.setBounds (870, 180, 100, 100);
		lblNaam4.setFont (lettType);
		
		panel.add (lblNaam4);
		panel.repaint();
		
	}
}

Indien ik hier de kleuren van de background nog niet veranderd had, nu wel :p

MilM

Legacy Member
Ik begrijp toch niet veel van uw 'opmaak'

Waarom noemt uw enige methode anders als de naam van uw klasse zelf?
Ik verwacht eerder dat dit dan de constructor zou zijn.
En dan heb je klassevariabelen en implementeer je de ActionListener

Iets in die aard dus:

Code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class ChatVenster implements ActionListener {
    
    
	private Container panel;
	private JLabel lblUitvoer;
	
	public ChatVenster (Container panel){
        this.panel = panel;
        Font letType = new Font ("Calibri", Font.BOLD, 25);
        JLabel lblChat = new JLabel ("Chat");
        lblChat.setBounds (50, 0, 70, 70);
        lblChat.setFont (letType);
        lblChat.setForeground (Color.white);
        
        panel.add (lblChat);
        panel.repaint();
        
        JTextField txtChat = new JTextField ();
        txtChat.setBounds (110, 25, 200, 20);
        txtChat.setForeground (Color.black);
        
        panel.add(txtChat);
        panel.repaint();  
        
        lblUitvoer = new JLabel (txtChat.getText());
        lblUitvoer.setBounds (50, 600, 950, 20);
        lblUitvoer.setFont (letType);
        
        txtChat.addActionListener (this);              
    }
    
    public void actionPerformed (ActionEvent gebeurtenis){
        panel.add (lblUitvoer);
        panel.repaint();
    }
}

Ik ken natuurlijk de volledige context niet.

PS: probeer ook JPanel te gebruiken ipv Container.

Poplipo

Legacy Member
Heb dus de volledige context in m'n vorige post gezet.

Kbegrijp wel je bedoeling met die constructoren, maar hoe 'start' je dan chatvenster?

MilM

Legacy Member
ChatVenster ch = new ChatVenster(venster, paneel, g);

edit:

ik was "this.panel = panel;" nog vergeten.
Je gebruikt nergens dat frame en de Graphics, dus deze moet je ook niet meegeven aan de constructor. (zie aanpassing constructor in mijn code hierboven)

Het enigste wat je nu dan nog in uw main moet zetten is:
ChatVenster ch = new ChatVenster(paneel);

Poplipo

Legacy Member
Stom v mij :$

Ik krijg nu ne gigantische NullPointerException, de eerste lijn hiervan is:

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
de lijnen daarna, is er maar 1 van die op iets slaat, dat is de 1e denkek, kzal hem toch helemaal posten:

*edit

Error opgelost

Blijft weigeren de label te tekenen, niets mis met de event, kzal es testen of het geen conflict is met die andere gui

*edit2

tis om mottig van te komen, als ik die andere gui als commentaarlijn zet, zet hij plots alles door elkaar :s

MilM

Legacy Member
Je zal iets in de aard van het volgend emoeten doen:

Code:
    public void actionPerformed (ActionEvent gebeurtenis){
       lblUitvoer = new JLabel (txtChat.getText());
    	panel.add (lblUitvoer);
        panel.validate();
    }

Je zal wel nog voor de positie van de labels moeten zorgen, want nu gaan ze over elkaar lappen.
Je kunt eventueel met een Array van Labels werken die onder elkaar staan.

Je kan ook een TextArea gebruiken.
Probeer dit eens:
Code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class ChatVenster implements ActionListener {

	private JTextArea lblUitvoer;
	private JTextField txtChat;

	public ChatVenster(Container panel) {
		Font letType = new Font("Calibri", Font.BOLD, 25);
		JLabel lblChat = new JLabel("Chat");
		lblChat.setBounds(50, 0, 70, 70);
		lblChat.setFont(letType);
		lblChat.setForeground(Color.white);

		//panel.add(lblChat);

		txtChat = new JTextField(10);
		txtChat.setBounds(110, 25, 200, 25);
		txtChat.setForeground(Color.black);

		panel.add(txtChat);

		lblUitvoer = new JTextArea(40, 20);

		JPanel chatbox = new JPanel();
		chatbox.add(new JScrollPane(lblUitvoer));
		chatbox.setBounds(200, 600, 500, 100);
		panel.add(chatbox);

		txtChat.addActionListener(this);
		panel.validate();
	}

	public void actionPerformed(ActionEvent gebeurtenis) {
		if (txtChat.getText().length() > 0) {
			lblUitvoer.append(txtChat.getText() + "\r\n");
			txtChat.setText("");
		}
	}

	public static void main(String[] args) {
		JFrame venster = new JFrame("Ganzebord");
		venster.setSize(1000, 700);
		venster.setVisible(true);
		venster.setLocation(0, 0);

		Container paneel = venster.getContentPane();
		Graphics g = paneel.getGraphics();

		Teken bord = new Teken();
		bord.TekenBordSpelers(venster, paneel, g);

		new ChatVenster(paneel);
	}
}

Runnen en uw scherm daarna iets vergroten zodat uw invoer veld zichtbaar wordt.
Werken met die setBounds is trouwens één grote hel.
Probeer eens te zoeken op layoutmanager zoals een GridLayout
Als je nog meet flexibiliteit wil, dan kun je gaan naar een GridBagLayout

Poplipo

Legacy Member
Dus chatvenster en m'n main in 1 klasse?

I'll try 1 dezer, thx!

Poplipo

Legacy Member
Wow, net geprobeerd. Heel handig en slim gezien ^^ Nu nog wa knutselen want die JPanel staat net waar het ganzebord zelf komt, ma da kunde gij nie weten :p

Vreet wel bedankt!!

*edit die JPanel wou gelijk nie verplaatsen :s
Heb het dan toch maar in het gewone uitvoervenster gezet, ma idpv een JLabel te maken dat de text van eht uitvoervenster haalt, hebbek gewoon System.out.println("De speler zegt: " txtChat.getText());

Ma toch bedankt voor de moeite!

MilM

Legacy Member
Main klasse mag overal staan eigenlijk, maar om het proper te houden ga je het natuurlijk niet zomaar ergens zetten. Ikzelf zet het altijd in de klasse van mijn 'JFrame' (tenzij je meerdere hoofdframes hebt). Dus eigenlijk de klasse die zelf opgeroepen wordt in de main methode en waarmee alles begint.

Een aparte klasse voor de main kan ook.

Ik zou eens een voorbeeldje zoeken van een LayoutManager.
Kan redelijk snel gaan éénmaal je het door hebt hoe alles werkt en zal u veel helpen bij de positionering van uw componenten, want met die setBounds kan ik bijv totaal niet werken :p (veel teveel gepruts)

Hier een tut: Using Layout Managers (The Java™ Tutorials > Creating a GUI with JFC/Swing > Laying Out Components Within a Container)

Bij default heb je de FlowLayout die alles gewoon naast elkaar zet. Meestal zal zoiets niet voldoen voor al uw Panels/Componenten (mss wel voor sommige, zoals bijv een panel met enkel buttons naast elkaar).

Een andere layoutmanager is de BorderLayout. Daar heb je 5 posities: North, South, East, West, Center
Deze kan gebruikt worden wanneer je bijv 2 of 3 componenten hebt.
Hier zij je aan dit buttonpanel hoe dit geordend wordt: How to Use BorderLayout (The Java™ Tutorials > Creating a GUI with JFC/Swing > Laying Out Components Within a Container)

Je zou bijv on top over de hele breedte een invoerregel kunnen hebben om te chatten.
Op West zou je dan uw TextArea in een scrollpanel kunnen hebben met de historie van de chat en dan op EAST uw ganzenbord bijv.

Je kan dit ook gaan nesten natuurlijk door bijv. dit panel zelf dan in een ander panel als CENTER te gaan zetten enzoverder om meer geavanceerde schikkingen te kunnen bekomen met enkel die layoutmanager te gebruiken.

GridLayout is dan weer een rooster waar je uw componenten schikt volgens het 'rij' en 'kolom' principe.

En GridBagLayout is een iets meer geavanceerde layoutmanager waarmee je enorm ver kan gaan en voldoet voor 99% van de applicaties.

Met een layoutmanager kun je ook gaan instellen wat er moet gebeuren als je bijv het venster vergroot. Als je het scherm verbreed, kan je er dna bijv voor kiezen dat het ganzenbord zelf evengroot blijft, maar enkel de TextArea breden wordt en zo het scherm 'opvult'
Maar daar kun je achteraf mee prutsen en toevoegen éénmaal alles geschikt is met de layoutmanager.
Het archief is een bevroren moment uit een vorige versie van dit forum, met andere regels en andere bazen. Deze posts weerspiegelen op geen enkele manier onze huidige ideeën, waarden of wereldbeelden en zijn op sommige plaatsen gecensureerd wegens ontoelaatbaar. Veel zijn in een andere tijdsgeest gemaakt, al dan niet ironisch - zoals in het ironische subforum Off-Topic - en zouden op dit moment niet meer gepost (mogen) worden. Toch bieden we dit archief nog graag aan als informatiedatabank en naslagwerk. Lees er hier meer over of start een gesprek met anderen.
Terug
Bovenaan