Varje knapp har sin egen callback-metod: metoden actionPerformed, i en klass som implementerar gränssnittet ActionListener. Det finns flera olika sätt att definera dessa ActionListener-implementerande klasser:
En klass kan vara:import java.awt.*; import java.awt.event.*; import javax.swing.*; class TrippButtonListener implements ActionListener { public void actionPerformed(ActionEvent event) { System.out.println("Tripp!"); } } // class TrippButtonListener public class BoringWindow { private int bwx = 1; private class TrappButtonListener implements ActionListener { public void actionPerformed(ActionEvent event) { System.out.println("Trapp!"); System.out.println("bwx = " + bwx); } } // class TrappButtonListener public BoringWindow() { int kx = 2; final int fkx = 3; class TrullButtonListener implements ActionListener { public void actionPerformed(ActionEvent event) { System.out.println("Trull!"); System.out.println("bwx = " + bwx); // Ger kompileringsfel // local variable kx is accessed from within inner class; needs to be declared final // System.out.println("kx = " + kx); System.out.println("fkx = " + fkx); } } // class TrullButtonListener JFrame frame = new JFrame("Ett tråkigt fönster"); frame.setSize(350, 50); Container cp = frame.getContentPane(); cp.setLayout(new GridLayout(1, 4)); JButton trippknapp = new JButton("Tripp!"); JButton trappknapp = new JButton("Trapp!"); JButton trullknapp = new JButton("Trull!"); JButton hejsanknapp = new JButton("Hejsan!"); cp.add(trippknapp); cp.add(trappknapp); cp.add(trullknapp); cp.add(hejsanknapp); trippknapp.addActionListener(new TrippButtonListener()); trappknapp.addActionListener(new TrappButtonListener()); trullknapp.addActionListener(new TrullButtonListener()); hejsanknapp.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { System.out.println("Hejsan!"); System.out.println("bwx = " + bwx); // Ger kompileringsfel // local variable kx is accessed from within inner class; needs to be declared final // System.out.println("kx = " + kx); System.out.println("fkx = " + fkx); } }); frame.setVisible(true); } // BoringWindow public static void main(String[] args) { final BoringWindow b = new BoringWindow(); int mx = 4; final int fmx = 5; class UnusedButtonListener implements ActionListener { public void actionPerformed(ActionEvent event) { System.out.println("Impossible!"); // Ger kompileringsfel // non-static variable bwx cannot be referenced from a static context // System.out.println("bwx = " + bwx); System.out.println("b.bwx = " + b.bwx); // Ger kompileringsfel // local variable mx is accessed from within inner class; needs to be declared final // System.out.println("mx = " + mx); System.out.println("fmx = " + fmx); } } // class TrullButtonListener // Ger kompileringsfel // non-static variable this cannot be referenced from a static context // new TrappButtonListener(); new TrippButtonListener(); // Ger kompileringsfel // cannot resolve symbol // new TrullButtonListener(); new UnusedButtonListener(); } // main } // class BoringWindow
Om den inre klassen är definierad inuti en metod, kallas den för en lokal klass. Ett objekt av den lokala klasssen har tillgång till de lokala variablerna i metoden där den definierades, men bara om de är final-deklarerade. (De lokala variablerna försvinner ju när metoden avslutas, medan instansen av den lokala klassen kan finnas kvar, så det Java-maskinen gör är att kopiera värdena.)
För att kunna skicka dessa meddelanden, måste ChatButtonWindow-instansen på något sätt ha tillgång till den PrintWriter-ström som är kopplad till servern. Det görs genom att PrintWriter-strömmen skickas med till ChatButtonWindow-klassens konstruktor, och sen lagras i en instansvariabel i ChatButtonWindow-instansen.
Ett alternativ är att göra ChatButtonWindow som en inre klass, som då får direkt tillgång till "omgivande" variabler.
I programmet Client5.java (se nedan) är klasserna ServerListener och ChatButtonWindow inre klasser i den omgivande klassen Client5. Därför kan man använda BufferedReader-strömmen in för att läsa från servern i en ServerListener-instans, och man kan använda PrintWriter-strömmen out för att skriva till servern i en ChatButtonWindow-instans.
Egentligen används PrintWriter-strömmen out inte direkt i ChatButtonWindow. I stället finns det två anonyma ActionListener-klasser, som definieras i ChatButtonWindow-klassens konstruktor. Eftersom de är inre klasser i ChatButtonWindow-klassen, som i sin tur är en inre klass i Client3, kommer de åt PrintWriter-strömmen out.
Programmet Client5.java:
Men, varning: Bara för att det går att göra så här, behöver det inte vara bra! I exemplet ovan använder koden i klassen ChatButtonWindow variabeln out, som finns i den omgivande klassen Client5.import java.net.*; import java.io.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; public class Client5 { public static final int PORT = 2000; private Socket socket; private BufferedReader in; private PrintWriter out; BufferedReader kbd_reader; private class ServerListener extends Thread { public void run() { String lineFromServer; try { while ((lineFromServer = in.readLine()) != null && !lineFromServer.equals("quit")) { System.out.println("Från servern: " + lineFromServer); } } catch (IOException e) { System.out.println("Undantag fångat: " + e); } } } // class ServerListener private class ChatButtonWindow extends JFrame { public ChatButtonWindow() { super("Extra chat buttons"); Container cp = getContentPane(); cp.setLayout(new FlowLayout()); JButton button1 = new JButton("Skicka förolämpning"); cp.add(button1); button1.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { out.println("Ni, min herre, är en apa."); System.out.println("Skickade en förolämpning."); } }); JButton button2 = new JButton("Skicka beröm"); cp.add(button2); button2.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { out.println("Du är bäst!"); System.out.println("Skickade beröm."); } }); JButton button3 = new JButton("Avsluta"); cp.add(button3); button3.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { System.out.println("Avslutar."); System.exit(0); } }); setSize(200, 200); setVisible(true); } } // class ChatButtonWindow public Client5(String serverName) throws IOException { InetAddress addr = InetAddress.getByName(serverName); Socket socket = new Socket(addr, PORT); System.out.println("Den nya socketen: " + socket); in = new BufferedReader( new InputStreamReader( socket.getInputStream())); out = new PrintWriter( new BufferedWriter( new OutputStreamWriter( socket.getOutputStream())), true); // true: PrintWriter is line buffered kbd_reader = new BufferedReader( new InputStreamReader(System.in)); ServerListener t = new ServerListener(); t.start(); ChatButtonWindow w = new ChatButtonWindow(); } // Client5 void listen() throws IOException { String buf; while (true) { buf = kbd_reader.readLine(); System.out.println("Från tangentbordet: " + buf); System.out.println("Till servern: " + buf); out.println(buf); } } public static void main(String[] args) throws IOException { Client5 c; if (args.length >= 1) c = new Client5(args[0]); else c = new Client5(null); c.listen(); } // main } // class Client5
Det gör att man kan få leta en del i programkoden för att hitta var den variabeln definieras, sätts och används. Det är förmodligen bättre att i stället skicka med out till konstruktorn, och låta ChatButtonWindow själv hålla reda på den i en egen variabel.
Den gamla "DOS-klienten" (som egentligen borde heta "textklienten"):
Vad behövs mer i den nya grafiska klienten?
Åtminstone ett fönster!
Kan göras som en klass. Jämför ChatButtonWindow ovan.
Den gamla "DOS-servern" (som egentligen borde heta "textservern"):
String sql = "insert into person values (" + number + ", '" + name + "', '" + phone + "')"; System.out.println("SQL-kommandot: " + sql); int rowCount = stmt.executeUpdate(sql);
Vi har skapat två ytor (av typen Container), med varsin GridLayout, att placera knappar på. Sen placerar vi båda ytorna på "grundytan" i fönstret.class DummyClientWindow { public DummyClientWindow() { JFrame frame = new JFrame("Bil-klienten"); frame.setSize(500, 100); Container cp = frame.getContentPane(); cp.setLayout(new GridLayout(2, 1)); Container cp2 = new Container(); cp2.setLayout(new GridLayout(1, 4)); cp.add(cp2); Container cp3 = new Container(); cp3.setLayout(new GridLayout(1, 1)); cp.add(cp3); JButton showButton= new JButton("Visa"); JButton averageButton = new JButton("Medel"); JButton newButton = new JButton("Ny"); JButton removeButton = new JButton("Ta bort"); JButton exitButton = new JButton("Avsluta"); cp2.add(showButton); cp2.add(averageButton); cp2.add(newButton); cp2.add(removeButton); cp3.add(exitButton); frame.setVisible(true); } // DummyClientWindow } // class DummyClientWindow
Som vanligt går det att ändra storleken på fönstret, och allt innehåll justeras automatiskt så gott det går: