Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
MODUL LATIHAN PROGRAM JAVA Latihan program java 1 public class Hello { /** * My first java program */ public static void main(String[] args) { //menampilkan string ”Hello world” pada layar System.out.println("Hello world!"); } } Latihan program java 2a public class OutputVariable { public static void main( String[] args ){ int value = 10; char x; x = ‘A’; System.out.println( value ); System.out.println( “The value of x=” + x ); } } Latihan program java 2b public class { public static void main (String[] args) { int var_a, var_b; var_a = 10; var_b = 500; System.out.println(“Variabel yang terdapat dalam program :”); System.out.println(“var_a = ” + var_a); System.out.println(“var_b = ” + var_b); } } Latihan program java 2c public class { public static void main (String[] args) { int bil_1, bil_2, hasil; bil_1 = 10; bil_2 = 500; hasil = bil_1 + bil_2; System.out.print(“Hasil Penjumlahan kedua bilangan :”); System.out.println(hasil); } } Latihan program java 2d public class { public static void main (String[] args) { float panjang = 12.5f; float lebar = 5.5f; float luas = panjang * lebar; System.out.println(“Luas persegi panjang tersebut =” + luas); } } Latihan program input keyboard java 2.1e import java.util.Scanner; public class { public static void main (String[] args) { Scanner masukan = new Scanner(System.in); int var_a, var_b; System.out.print(“Masukkan nilai var var_a :”); var_a = masukan.nextInt(); System.out.print(“Masukkan nilai var var_b :”); var_b = masukan.nextInt(); System.out.println(); System.out.println(“Variabel yang terdapat dalam program :”); System.out.println(“var_a = ” + var_a); System.out.println(“var_b = ” + var_b); } } Latihan program input keyboard java 2.2e import java.util.Scanner; public class { public static void main (String[] args) { Scanner masukan = new Scanner(System.in); float celcius, reamur; System.out.print(“Masukkan nilai suhu (celcius) : ”); celcius = masukan.nextFloat(); reamur = 0.8f * celcius; System.out.println(); System.out.print(“nilai suhu reamur dari input : ”); System.out.println(reamur); } } Latihan program mengunakan operator if java 3a import java.util.Scanner; public class { public static void main (String[] args) { Scanner masukan = new Scanner(System.in); int nilai; System.out.print(“Masukkan nilai akhir mata kuliahnya :”); nilai = masukan.nextInt(); if (nilai < 55) System.out.println(“Mahasiwa tersebut tidak lulus”); } } Latihan program mengunakan operator if - else java 3b import java.util.Scanner; public class { public static void main (String[] args) { Scanner masukan = new Scanner(System.in); System.out.print(“Masukkan nilai mata kuliahnya :”); int nilai = masukan.nextInt(); if (nilai >= 80) System.out.println(“Nilainya A”); else if (nilai >= 70) System.out.println(“Nilainya B”); else if (nilai >= 55) System.out.println(“Nilainya C”); else if (nilai >= 40) System.out.println(“Nilainya D”); else System.out.println(“Nilainya E”); } } Latihan program mengunakan operator if - else java 3b.1 import java.util.Scanner; public class { public static void main (String[] args) { Scanner masukan = new Scanner(System.in); System.out.println(“Masukkan sebuah bilangan : “); int bil = masukan.nextInt(); boolean prima = true; for(int i=2; i<bil; i++){ if ((bil % i)== 0) { prima = false; break;} } if (prima) System.out.print(bil + “ adalah bilangan PRIMA”); else System.out.print(bil + “ adalah BUKAN bilangan PRIMA”); } } Latihan program mengunakan operator switch java 3c import java.util.Scanner; public class { public static void main (String[] args) { Scanner masukan = new Scanner(System.in); System.out.print(“Masukkan angka 1 - 3 : ”); int bil = masukan.nextInt(); switch (bil) { case 1 : System.out.println(“Satu”);break; case 2 : System.out.println(“Dua”);break; case 3 : System.out.println(“Tiga”);} } } Latihan program mengunakan operator switch java 3d import java.util.Scanner; public class { public static void main (String[] args) { Scanner masukan = new Scanner(System.in); System.out.print(“Masukkan nilai Matematika-nya :”); int matematika = masukan.nextInt(); System.out.print(“Masukkan nilai Fisika-nya :”); int fisika = masukan.nextInt(); if ((matematika > 80) && (fisika > 70)) System.out.println(“Siswa tersebut DITERIMA”); else System.out.println(“Siswa tersebut TIDAK DITERIMA”); } } Latihan program mengunakan operator for java 3e public class{ public static void main (String[] args) { for(int i=1; i<5; i++) System.out.println(“Hello World!!”); } } Latihan program mengunakan operator for java 3e import java.util.Scanner; public class { public static void main (String[] args) { Scanner masukan = new Scanner(System.in); System.out.println(“Masukkan batas bilangannya : “); int batas = masukan.nextInt(); System.out.println(“Bilangan genap dari 2 sampai bil tsb : “); for(int i=2; i<=batas; i=i+2) System.out.print(i + “ “); } } Latihan program mengunakan operator array java 3f public class ArrayKota{ public static void main(String[] args){ String[] kota; //deklarasi variabel array kota = new String[3]; // membuat objek array // String[] kota = new String[3]; // mengisi elemen array kota[0] = "Jakarta"; kota[1] = "Surabaya"; kota[2] = "Semarang"; // menampilkan elemen array System.out.println(kota[0]); System.out.println(kota[1]); System.out.println(kota[2]); } } Latihan program mengunakan operator array java 3g public class ArrayKota2{ public static void main(String[] args){ String[] kota = {“Jakarta”, “Surabaya”, “Semarang”}; // menampilkan elemen array System out println(kota[0]); System.out.println(kota[1]); System.out.println(kota[2]); } } Latihan program mengunakan operator array java 3h class ArrayMultidimensi { public static void main(String[] args) { String[][] nama = { {"Pak ", "Bu “, “Mbak”}, {"Joko", "Susi"} }; System.out.println(nama[0][0] + nama[1][0]); System.out.println(nama[0][1] + nama[1][1]); System.out.println(nama[0][2] + nama[1][0]); } } Latihan program mengunakan operator java 3b public class aritmatikaDemo { public static void main(String[] args) { //sedikit angka int i = 37; int j = 42; double x = 27.475; double y = 7.22; System.out.println("Variable values..."); System.out.println(" i = " + i); System.out.println(" j = " + j); System.out.println(" x = " + x); System.out.println(" y = " + y); //penjumlahan angka System.out.println("Adding..."); System.out.println(" i + j = " + (i + j)); System.out.println(" x + y = " + (x + y)); //pengurangan angka System.out.println("Subtracting..."); System.out.println(" i - j = " + (i - j)); System.out.println(" x - y = " + (x - y)); //perkalian angka System.out.println("Multiplying..."); System.out.println(" i * j = " + (i * j)); System.out.println(" x * y = " + (x * y)); //pembagian angka System.out.println("Dividing..."); System.out.println(" i / j = " + (i / j)); System.out.println(" x / y = " + (x / y)); //menghitung hasil modulus dari pembagian System.out.println("Computing the remainder..."); System.out.println(" i % j = " + (i % j)); System.out.println(" x % y = " + (x % y)); //tipe penggabungan System.out.println("Mixing tipes..."); System.out.println(" j + y = " + (j + y)); System.out.println(" i * x = " + (i * x)); } } Latihan program mengunakan relasi java 4 public class RelasiDemo { public static void main(String[] args) { //beberapa nilai int i = 37; int j = 42; int k = 42; System.out.println("Nilai variabel..."); System.out.println(" i = " + i); System.out.println(" j = " + j); System.out.println(" k = " + k); //lebih besar dari System.out.println("Lebih besar dari..."); System.out.println(" i > j = " + (i > j)); //false System.out.println(" j > i = " + (j > i)); //true System.out.println(" k > j = " + (k > j)); //false //lebih besar atau sama dengan System.out.println("Lebih besar dari atau sama dengan..."); System.out.println(" i >= j = " + (i >= j)); //false System.out.println(" j >= i = " + (j >= i)); //true System.out.println(" k >= j = " + (k >= j)); //true //lebih kecil dari System.out.println("Lebih kecil dari..."); System.out.println(" i < j = " + (i < j)); //true System.out.println(" j < i = " + (j < i)); //false System.out.println(" k < j = " + (k < j)); //false //lebih kecil atau sama dengan System.out.println("Lebih kecil dari atau sama dengan..."); System.out.println(" i <= j = " + (i <= j)); //true System.out.println(" j <= i = " + (j <= i)); //false System.out.println(" k <= j = " + (k <= j)); //true //sama dengan System.out.println("Sama dengan..."); System.out.println(" i == j = " + (i == j)); //false System.out.println(" k == j = " + (k == j)); //true //tidak sama dengan System.out.println("Tidak sama dengan..."); System.out.println(" i != j = " + (i != j)); //true System.out.println(" k != j = " + (k != j)); //false } } Java program tes and java 5 public class TestAND { public static void main( String[] args ){ int i = 0; int j = 10; boolean test= false; //demonstrasi && test = (i > 10) && (j++ > 9); System.out.println(i); System.out.println(j); System.out.println(test); //demonstrasi & test = (i > 10) & (j++ > 9); System.out.println(i); System.out.println(j); System.out.println(test); } } Latihan java tes OR 6 public class TestOR { public static void main( String[] args ){ int i = 0; int j = 10; boolean test= false; //demonstrasi || test = (i < 10) || (j++ > 9); System.out.println(i); System.out.println(j); System.out.println(test); //demonstrasi | test = (i < 10) | (j++ > 9); System.out.println(i); System.out.println(j); System.out.println(test); } } Latihan program java test XOR 7 public class TestXOR { public static void main( String[] args ){ boolean val1 = true; boolean val2 = true; System.out.println(val1 ^ val2); val1 = false; val2 = true; System.out.println(val1 ^ val2); val1 = false; val2 = false; System.out.println(val1 ^ val2); val1 = true; val2 = false; System.out.println(val1 ^ val2); } } Latihan program conditional operator1 java 8 public class ConditionalOperator { public static void main( String[] args ){ String status = ""; int grade = 80; //mendapatkan status pelajar status = (grade >= 60)?"Passed":"Fail"; //print status System.out.println( status ); } } Latihan program conditional operator2 java 9 class ConditionalOperator { public static void main( String[] args ){ int score = 0; char answer = 'a'; score = (answer == 'a') ? 10 : 0; System.out.println("Score = " + score ); } } Latihan program tambahan java 10 package learn.core; import java.util.*; import java.text.*; public class NumberFormatDemo { static public void displayNumber(Locale currentLocale) { Integer quantity = new Integer(123456); Double amount = new Double(345987.246); NumberFormat numberFormatter; String quantityOut; String amountOut; numberFormatter = NumberFormat.getNumberInstance(currentLocale); quantityOut = numberFormatter.format(quantity); amountOut = numberFormatter.format(amount); System.out.println(quantityOut + " " + currentLocale.toString()); System.out.println(amountOut + " " + currentLocale.toString()); } static public void displayCurrency(Locale currentLocale) { Double currency = new Double(9876543.21); NumberFormat currencyFormatter; String currencyOut; currencyFormatter = NumberFormat.getCurrencyInstance(currentLocale); currencyOut = currencyFormatter.format(currency); System.out.println(currencyOut + " " + currentLocale.toString()); } static public void displayPercent(Locale currentLocale) { Double percent = new Double(0.75); NumberFormat percentFormatter; String percentOut; percentFormatter = NumberFormat.getPercentInstance(currentLocale); percentOut = percentFormatter.format(percent); System.out.println(percentOut + " " + currentLocale.toString()); } static public void main(String[] args) { Locale[] locales = { Locale.getDefault(), new Locale("fr","FR"), new Locale("de","DE"), new Locale("en","US") }; for (int i = 0; i < locales.length; i++) { System.out.println(); displayNumber(locales[i]); displayCurrency(locales[i]); displayPercent(locales[i]); } } } Latihan program tambahan java 11 package learn.core; import java.math.*; public class BigIntegerDemo { public static void main(String args[]) { // Compute the factorial of 1000 BigInteger total = BigInteger.valueOf(1); for(int i = 2; i <= 1000; i++) total = total.multiply(BigInteger.valueOf(i)); System.out.println(total.toString()); } } Latihan program tambahan java 12 package learn.reuse.composition; class WaterSource { private String s; WaterSource() { System.out.println("WaterSource()"); s = new String("Constructed"); } public String toString() { return s; } } public class SprinklerSystem { private String valve1, valve2, valve3, valve4; WaterSource source; int i; float f; void print() { System.out.println("valve1 = " + valve1); System.out.println("valve2 = " + valve2); System.out.println("valve3 = " + valve3); System.out.println("valve4 = " + valve4); System.out.println("i = " + i); System.out.println("f = " + f); System.out.println("source = " + source); } public static void main(String[] args) { SprinklerSystem x = new SprinklerSystem(); x.print(); } } MODUL LATIHAN PROGRAM JAVA GUI LATIHAN JAVA GUI 1 package learn.swing; import javax.swing.*; public class HelloWorldSwing { public static void main(String[] args) { JFrame frame = new JFrame("HelloWorldSwing"); final JLabel label = new JLabel("Hello World"); frame.getContentPane().add(label); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.pack(); frame.setVisible(true); } } LATIHAN JAVA GUI 1a Berikut ini contoh program penanganan event terkait mouse. Terdapat dua listener terkait dengan event mouse yaitu MouseListener dan MouseMotionListener. Berikut ini program lengkapnya: import java.awt.*; import java.awt.event.*; import javax.swing.*; public class MouseEventHandling extends JFrame implements MouseListener, MouseMotionListener { private JLabel statusBar; public MouseEventHandling () { super ("Mencoba Beberapa Mouse Event Handling"); statusBar = new JLabel(); getContentPane().add(statusBar,BorderLayout.SOUTH); addMouseListener (this); addMouseMotionListener (this); setSize (300,100); setLocationRelativeTo(null); setVisible(true); } //MouseListener event handler public void mouseClicked (MouseEvent e) { statusBar.setText("Clicked at ["+ e.getX() + "," + e.getY() + "]"); } public void mousePressed (MouseEvent e) { statusBar.setText("Pressed at ["+ e.getX() + "," + e.getY() + "]"); } public void mouseReleased (MouseEvent e) { statusBar.setText("Released at ["+ e.getX() + "," + e.getY() + "]"); } public void mouseEntered (MouseEvent e) { statusBar.setText("Entered at ["+ e.getX() + "," + e.getY() + "]"); getContentPane().setBackground(Color.GREEN); } public void mouseExited (MouseEvent e) { statusBar.setText("Mouse outside window"); getContentPane().setBackground(Color.WHITE); } //MouseMotionListener event handler public void mouseDragged (MouseEvent e) { statusBar.setText("Dragged at ["+ e.getX() + "," + e.getY() + "]"); } public void mouseMoved (MouseEvent e) { statusBar.setText("Moved at ["+ e.getX() + "," + e.getY() + "]"); } public static void main (String args[]) { MouseEventHandling test = new MouseEventHandling(); test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } } LATIHAN JAVA GUI 2 Class Painter dapat digunakan untuk membuat program kecil yang membentuk garis dengan mouse (saat mouse di-drag). Berikut ini program lengkapnya: import java.awt.*; import java.awt.event.*; import javax.swing.*; public class Painter extends JFrame { private int pointCount = 0; private Point points[] = new Point[1000]; public Painter () { super ("Program menggambar sederhana"); getContentPane().add(new JLabel("Drag mouse to draw"), BorderLayout.SOUTH); addMouseMotionListener ( new MouseMotionAdapter() { public void mouseDragged (MouseEvent e) { if (pointCount < points.length) { points[pointCount] = e.getPoint(); ++pointCount; repaint(); } } } //end of anonymous class ); //end method addMotionListener setSize (300,150); setLocationRelativeTo(null); setVisible(true); } public void paint (Graphics g) { super.paint(g); for (int i = 0; i < points.length && points[i] != null; i++) { g.setColor(Color.red); g.fillOval (points[i].x, points[i].y, 4,4); } } public static void main (String args[]) { Painter test = new Painter(); test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } } LATIHAN JAVA GUI 3 Contoh program sederhana yang menampilkan form login di dalam JFrame. Inputan username berupa JTextField dan inputan password berupa JPasswordField serta tombol dibuat dengan JButton. Form sederhana ini juga mendemonstrasikan adanya Listener berupa ActionListener. import java.awt.*; import java.awt.event.*; import javax.swing.*; public class SimpleLogin extends JFrame implements ActionListener { private JLabel label1, label2; private JTextField txtUser; private JPasswordField pwdPass; private JButton btnLogin, btnExit; public SimpleLogin() { super ("Login here..."); Container container = getContentPane(); container.setLayout(new FlowLayout()); label1 = new JLabel ("Username : "); label2 = new JLabel ("Password : "); txtUser = new JTextField (20); txtUser.setToolTipText("Input Username"); pwdPass = new JPasswordField(20); btnLogin = new JButton ("Login"); btnLogin.addActionListener(this); btnExit = new JButton ("Exit"); btnExit.addActionListener(this); container.add(label1); container.add(txtUser); container.add(label2); container.add(pwdPass); container.add(btnLogin); container.add(btnExit); setSize (300,200); setVisible (true); } public static void main (String args[]) { SimpleLogin test = new SimpleLogin(); test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } private String user = "", pass = ""; public void actionPerformed (ActionEvent e) { if (e.getSource() == btnLogin) { user = txtUser.getText(); pass = pwdPass.getText(); if (user.equals("achmatim") && pass.equals("otim")) { JOptionPane.showMessageDialog(null, "Login successfull"); } else { JOptionPane.showMessageDialog(null, "Username and password dosn't match!"); txtUser.setText(""); pwdPass.setText(""); txtUser.requestFocus(true); } } else if (e.getSource() == btnExit){ JOptionPane.showMessageDialog(null,"Thanks to try my program. See you.."); System.exit(0); } } } LATIHAN JAVA GUI 4 Latihan timer program java import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.Image; import java.awt.Toolkit; import javax.swing.JFrame; /** * * @author */ public class TimerThread extends JFrame implements Runnable{ Thread th; boolean running; int i; long detik; int menit; int jam; Image start; public TimerThread(){ running=true; th=new Thread(this); setTitle("Timer Thread"); setSize(200, 100); int w=Toolkit.getDefaultToolkit().getScreenSize().width; int h=Toolkit.getDefaultToolkit().getScreenSize().height; setLocation(w/2-this.getWidth()/2, h/2-this.getHeight()/2); setVisible(true); } public void paint(Graphics g){ g.clearRect(0, 0,500, 500); g.setColor(Color.blue); Font f=new Font("Times new Roman", 1,20); g.setFont(f); g.drawString("Time : "+jam+":"+menit+":"+detik, 20,70); } public void run() { while(running){ try{ Thread.sleep(20); }catch(Exception e){} if(i==60){ detik=detik+1; i=1; } if(detik==60){ menit=menit+1; detik=1; } if(menit==60){ jam=jam+1; menit=1; } i++; repaint(); } } } LATIHAN JAVA GUI 5 Pada latihan Java kali ini penggunaan pallete pada Netbean akan saling di kombinasikan, sehingga user akan lebih mudah dalam menjalankan aplikasi yang akan kita buat. Penggunaan Radio Button, Check Box, Combo Box akan di terapkan pada contoh berikut ini, diharapkan dengan latihan ini kita dapat mengkolaborasikan dan tahu fungsi masing-masing pallete tersebut. Ketentuan Soal: Panel Jenis Poli: Jenis Poli | Nama Dokter |Waktu Praktek Umum | Mahfud Akbar |Pagi Gigi | Mutia Khanza | Sore Saraf | Annisa Susilowati |Malam Panel Ruangan: Nama Ruang |Harga/Hari Kenanga |200000 Mawar |300000 Melati |400000 Jika user klik salah satu radio button pada Panel Jenis Poli Umum, Gigi atau Saraf maka Field text pada Nama Dokter dan Waktu Praktek akan muncul otomatis. Lama Inap di input manual, kemudian Pilih Ruangan pada Combo Box Nama Ruang, maka otomatis Harga/hari, Diskon, dan Total akan muncul sesuai pilihan. Data Pasien : No. Pasien dan Nama di input Manual Jika Check Box pada Perincian Biaya di pilih maka total akan bertamah otomatis. Bersih button akan membersihkan isian pada text field dan dll. Keluar maka akan menutup aplikasi. Variable name dan nama masing-masing Text Field, Combo Box, Check Box, Radio Button menyesuaikan Coding. Berikut Codingnya Deklarasi Global Variable Integer: public class Quiz extends javax.swing.JFrame { int biayaadmin, konsultasi, obat, diskon,totalharga, total; Coding pada Radio Button Umum: private void jrbtnumumActionPerformed(java.awt.event.ActionEvent evt) { if(jrbtnumum.isSelected()){ jtxtnadok.setText(“Mahfud Akbar”); jtxtwktpraktek.setText(“Pagi”); } } Coding pada Radio Button Gigi: private void jrbtngigiActionPerformed(java.awt.event.ActionEvent evt) { if(jrbtngigi.isSelected()){ jtxtnadok.setText(“Mutia Khanza”); jtxtwktpraktek.setText(“Sore”); } } Coding pada Radio Button Saraf: private void jrbtnsarafActionPerformed(java.awt.event.ActionEvent evt) { if(jrbtnsaraf.isSelected()){ jtxtnadok.setText(“Annisa Susilowati”); jtxtwktpraktek.setText(“Malam”); } } Coding pada Combo Box Nama Ruang: private void jconaruangActionPerformed(java.awt.event.ActionEvent evt) { if(jconaruang.getSelectedItem().equals(“Kenanga”)){ jtxtharga.setText(“200000″); int harga=Integer.parseInt(jtxtharga.getText()); int lama=Integer.parseInt(jtxtlama.getText()); int totalharga=harga*lama; int potongan1=totalharga*10/100; int potongan2=totalharga*0; if (lama>4){ diskon=potongan1; jtxtdiskon.setText(Integer.toString(potongan1)); total=totalharga-diskon+biayaadmin+obat+konsultasi; jtxttotal.setText(String.valueOf(total)); } else { diskon=potongan2; jtxtdiskon.setText(Integer.toString(potongan2)); total=totalharga-diskon+biayaadmin+obat+konsultasi; jtxttotal.setText(String.valueOf(total)); } } else if(jconaruang.getSelectedItem().equals(“Mawar”)){ jtxtharga.setText(“300000″); int harga=Integer.parseInt(jtxtharga.getText()); int lama=Integer.parseInt(jtxtlama.getText()); int totalharga=harga*lama; int potongan1=totalharga*10/100; int potongan2=totalharga*0; if (lama>4){ diskon=potongan1; jtxtdiskon.setText(Integer.toString(potongan1)); total=totalharga-diskon+biayaadmin+obat+konsultasi; jtxttotal.setText(String.valueOf(total)); } else { diskon=potongan2; jtxtdiskon.setText(Integer.toString(potongan2)); total=totalharga-diskon+biayaadmin+obat+konsultasi; jtxttotal.setText(String.valueOf(total)); } } else if(jconaruang.getSelectedItem().equals(“Melati”)){ jtxtharga.setText(“400000″); int harga=Integer.parseInt(jtxtharga.getText()); int lama=Integer.parseInt(jtxtlama.getText()); int totalharga=harga*lama; int potongan1=totalharga*10/100; int potongan2=totalharga*0; if (lama>4){ diskon=potongan1; jtxtdiskon.setText(Integer.toString(potongan1)); total=totalharga-diskon+biayaadmin+obat+konsultasi; jtxttotal.setText(String.valueOf(total)); } else { diskon=potongan2; jtxtdiskon.setText(Integer.toString(potongan2)); total=totalharga-diskon+biayaadmin+obat+konsultasi; jtxttotal.setText(String.valueOf(total)); } } } Coding pada Checck Box Administrasi: private void jchkadminActionPerformed(java.awt.event.ActionEvent evt) { if(jchkadmin.isSelected()) { biayaadmin=5000; jtxtadmin.setText(String.valueOf(biayaadmin)); int harga = Integer.parseInt(jtxtharga.getText()); int lama = Integer.parseInt(jtxtlama.getText()); int totaldiskon=Integer.parseInt(jtxtdiskon.getText()); total=(harga*lama)-totaldiskon+biayaadmin+konsultasi+obat; jtxttotal.setText(String.valueOf(total)); } else { biayaadmin=0; jtxtadmin.setText(String.valueOf(biayaadmin)); int harga = Integer.parseInt(jtxtharga.getText()); int lama = Integer.parseInt(jtxtlama.getText()); int totaldiskon=Integer.parseInt(jtxtdiskon.getText()); total=(harga*lama)-totaldiskon+biayaadmin+konsultasi+obat; jtxttotal.setText(String.valueOf(total)); } } Coding pada Checck Box Konsultasi: private void jchkkonsultasiActionPerformed(java.awt.event.ActionEvent evt) { if(jchkkonsultasi.isSelected()) { konsultasi=20000; jtxtkonsultasi.setText(String.valueOf(konsultasi)); int harga = Integer.parseInt(jtxtharga.getText()); int lama = Integer.parseInt(jtxtlama.getText()); int totaldiskon=Integer.parseInt(jtxtdiskon.getText()); total=(harga*lama)-totaldiskon+biayaadmin+konsultasi+obat; jtxttotal.setText(String.valueOf(total)); } else { konsultasi=0; jtxtkonsultasi.setText(String.valueOf(konsultasi)); int harga = Integer.parseInt(jtxtharga.getText()); int lama = Integer.parseInt(jtxtlama.getText()); int totaldiskon=Integer.parseInt(jtxtdiskon.getText()); total=(harga*lama)-totaldiskon+biayaadmin+konsultasi+obat; jtxttotal.setText(String.valueOf(total)); } } Coding pada Checck Box Obat: private void jchkobatActionPerformed(java.awt.event.ActionEvent evt) { if(jchkobat.isSelected()) { obat=50000; jtxtobat.setText(String.valueOf(obat)); int harga = Integer.parseInt(jtxtharga.getText()); int lama = Integer.parseInt(jtxtlama.getText()); int totaldiskon=Integer.parseInt(jtxtdiskon.getText()); total=(harga*lama)-totaldiskon+biayaadmin+konsultasi+obat; jtxttotal.setText(String.valueOf(total)); } else { obat=0; jtxtobat.setText(String.valueOf(obat)); int harga = Integer.parseInt(jtxtharga.getText()); int lama = Integer.parseInt(jtxtlama.getText()); int totaldiskon=Integer.parseInt(jtxtdiskon.getText()); total=(harga*lama)-totaldiskon+biayaadmin+konsultasi+obat; jtxttotal.setText(String.valueOf(total)); } } Bersih Button: private void jbtnbersihActionPerformed(java.awt.event.ActionEvent evt) { jchkkonsultasi.setSelected(false); jtxtkonsultasi.setText(” “); jchkadmin.setSelected(false); jtxtadmin.setText(” “); jchkobat.setSelected(false); jtxtobat.setText(” “); jconaruang.setSelectedIndex(0); jtxtlama.setText(” “); jtxtharga.setText(” “); jtxtnadok.setText(” “); jtxtdiskon.setText(” “); rbtngrpjepol.clearSelection(); jtxttotal.setText(” “); jtxtnopasien.setText(” “); jtxtnamapasien.setText(” “); jtxtwktpraktek.setText(” “); } Keluar Button: private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) { dispose(); LATIHAN JAVA GUI BORDER 6 import javax.swing.*; import java.awt.*; class border extends JFrame { Button but1= new Button("proses 1"); Button but2= new Button("proses 2"); Button but3= new Button("proses 3"); border() { super("PROGRAM BORDER LAYOUT"); setLocation(200,200); setSize(200,120); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLayout(new BorderLayout()); add(but1); add(but2, BorderLayout.NORTH); add(but3, BorderLayout.WEST); setVisible(true); } public static void main(String args[]) { new border(); } } LATIHAN JAVA GUI BORDER 7 import javax.swing.*; import java.awt.*; import java.awt.event.*; class cekbok extends JFrame { JLabel cop1=new JLabel("PROGRAM PEMESANAN MAKANAN"); JLabel cop2=new JLabel("================================"); JLabel lmakan=new JLabel("Daftar Makanan"); JLabel lminum=new JLabel("Daftar Minuman"); JCheckBox goreng=new JCheckBox("Nasi goreng"); JCheckBox uduk=new JCheckBox("Nasi Uduk"); JCheckBox teh=new JCheckBox("Es Teh"); JCheckBox kopi=new JCheckBox("Kopi Racik"); cekbok() { super("PROGRAM MENGHITUNG ZAKAT"); setLocation(200,100); setSize(280,200); setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); } void objek() { getContentPane().setLayout(null); getContentPane().add(cop1); getContentPane().add(cop2); getContentPane().add(lmakan); getContentPane().add(lminum); getContentPane().add(goreng); getContentPane().add(uduk); getContentPane().add(teh); getContentPane().add(kopi); cop1.setBounds(40,20,300,20); cop2.setBounds(30,40,550,20); lmakan.setBounds(30,60,100,20); lminum.setBounds(150,60,100,20); goreng.setBounds(30,90,100,20); uduk.setBounds(30,120,100,20); teh.setBounds(150,90,100,20); kopi.setBounds(150,120,100,20); setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); setVisible(true); } public static void main(String args[]) { cekbok ZZ=new cekbok(); ZZ.objek(); } } LATIHAN JAVA GUI BORDER 8 import java.awt.*; import java.awt.event.*; import javax.swing.*; public class combo { public static void main(String[] args) { JFrame frame = new JFrame(); final JLabel lb=new JLabel(""); final String [] items = { " roti ", " Madu " }; final JComboBox comboBox = new JComboBox(items); JPanel pn=new JPanel(); frame.add(pn,BorderLayout.WEST); pn.add(comboBox,BorderLayout.CENTER); pn.add(lb,BorderLayout.NORTH); comboBox.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent ae) { String mn=(String)comboBox.getSelectedItem(); lb.setText(mn); } }); frame.setSize(200, 200); frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); frame.setVisible(true); } } LATIHAN JAVA GUI BORDER 9 import javax.swing.*; import java.awt.*; class theflow extends JFrame { Button but1= new Button("proses 1"); Button but2= new Button("proses 2"); Button but3= new Button("proses 3"); theflow () { super("PROGRAM FLOW LAYOUT"); setLocation(200,200); setSize(200,120); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLayout(new FlowLayout()); add(but1); add(but2); add(but3); setVisible(true); } public static void main(String args[]) { new theflow (); } } LATIHAN JAVA GUI BORDER 10 import javax.swing.*; import java.awt.*; import java.awt.event.*; public class gambar extends JFrame { JLabel lb=new JLabel(new ImageIcon("Foto.png")); //Gambar yang akan ditambahkan JButton bt=new JButton(new ImageIcon("Foto.png")); gambar() { super("INI ADALAH FORM"); setLocation(200,300); setSize(320,180); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setVisible(true); } void tampilan() { getContentPane().add(lb); getContentPane().add(bt); getContentPane().setLayout(new FlowLayout()); setVisible(true); } public static void main(String args[]) { gambar f=new gambar(); f.tampilan(); } } LATIHAN JAVA GUI BORDER 11 import javax.swing.*; import java.awt.*; import javax.swing.table.*; import java.awt.event.*; import java.sql.*; class menunya extends JFrame { JMenuBar mb=new JMenuBar(); JMenu mn=new JMenu("File"); JMenu mn2=new JMenu("Open"); JRadioButton bt=new JRadioButton("buku"); JMenuItem mnt=new JMenuItem("Close"); JMenuItem unta=new JMenuItem("Unta"); menunya() { super("PROGRAM MENGHITUNG ZAKAT"); setLocation(200,100); setSize(400,350); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } void objek() { getContentPane().add(mb,BorderLayout.NORTH); mb.add(mn); mn2.add(bt); mn.add(mn2); mn.add(mnt); mn.add(unta); mb.setBounds(10,10,100,30); setVisible(true); } public static void main(String args[]) { menunya ZZ=new menunya(); ZZ.objek(); } } LATIHAN JAVA GUI BORDER 12 import javax.swing.*; import java.awt.*; import java.awt.event.*; import javax.swing.border.*; import javax.swing.JButton; public class radioB extends JFrame { JRadioButton temuan=new JRadioButton("Zakat Temuan"); JRadioButton fitrah=new JRadioButton("Zakat Fitrah"); JRadioButton dagang=new JRadioButton("Perniagaan/Usaha"); ButtonGroup kelompok=new ButtonGroup(); radioB() { setLocation(50,110); setSize(300,200); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } void tampilan() { getContentPane().setLayout(new FlowLayout()); getContentPane().add(temuan); getContentPane().add(fitrah); getContentPane().add(dagang); kelompok.add(temuan); kelompok.add(fitrah); kelompok.add(dagang); setVisible(true); } public static void main(String args[]) { radioB rb=new radioB(); rb.tampilan(); } } MODUL TUGAS TAMBAHAN JAVA 1. Buatlah tampilan program sebagai berikut ini : * ** *** **** ***** 1 1 12 123 1234 12345 2 4 * ** *** **** ***** 5 Bilangan 1-50 yang ganjil = 1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 41 43 45 47 49 6 2,3,5,7,11,13,17,19,23,29,31,37,41,43,47 Jawaban ya : 1. package ukk; public class Soal1 { public static void main (String [] args){ for (int b=0; b<=4; b++) { for (int a=b; a>=0; a--) { System.out.print("*"); } System.out.println(""); } } } 2. package ukk; public class Soal2 { public static void main (String [] args){ { int baris,kolom; baris = 1; while (baris <= 5) { kolom = 1; while (kolom<= baris) { System.out.print(kolom); kolom++; } System.out.println(); baris++; } } } } package ukk; public class Soal4 { public static void main (String [] args){ for (int b=0; b<=4; b++) { for (int a=b; a<=8; a++) { System.out.print(" "); } for (int a=b; a>=0; a--) { System.out.print("*"); } System.out.println(""); } } } package ukk; public class Soal5 { public static void main(String[] args) { int i; System.out.print("Bilangan 1-50 yang ganjil = "); for (i = 1; i <= 50; i++) { if (i%2 !=0 ) { System.out.print(" " + i); } } } } package ukk; public class Soal6 { public static void main(String[] args) { char prima[] = new char[51]; for (int i = 2; i <= 50; i++) { if (prima[i] != '*') { System.out.print(i+","); for (int j = i * 2; j <= 50; j+=i) prima[j]='*'; } } } } 2. A Menus and toolbars in Java Swing A menubar is one of the most visible parts of the GUI application. It is a group of commands located in various menus. While in console applications you had to remember all those arcane commands, here we have most of the commands grouped into logical parts. There are accepted standards that further reduce the amount of time spending to learn a new application. In Java Swing, to implement a menubar, we use three objects. A JMenuBar, a JMenu and a JMenuItem. Simple menu We begin with a simple menubar example. package com.zetcode; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import import import import import import javax.swing.ImageIcon; javax.swing.JFrame; javax.swing.JMenu; javax.swing.JMenuBar; javax.swing.JMenuItem; javax.swing.SwingUtilities; public class Example extends JFrame { public Example() { initUI(); } public final void initUI() { JMenuBar menubar = new JMenuBar(); ImageIcon icon = new ImageIcon(getClass().getResource("exit.png")); JMenu file = new JMenu("File"); file.setMnemonic(KeyEvent.VK_F); JMenuItem eMenuItem = new JMenuItem("Exit", icon); eMenuItem.setMnemonic(KeyEvent.VK_E); eMenuItem.setToolTipText("Exit application"); eMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { System.exit(0); } }); file.add(eMenuItem); menubar.add(file); setJMenuBar(menubar); setTitle("Simple menu"); setSize(300, 200); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { Example ex = new Example(); ex.setVisible(true); } }); } } Our example will show a menu with one item. Selecting the exit menu item we close the application. JMenuBar menubar = new JMenuBar(); Here we create a menubar. ImageIcon icon = new ImageIcon(getClass().getResource("exit.png")); We will display an icon in the menu. JMenu file = new JMenu("File"); file.setMnemonic(KeyEvent.VK_F); We create a menu object. The menus can be accessed via the keybord as well. To bind a menu to a particular key, we use the setMnemonic() method. In our case, the menu can be opened with the ALT + F shortcut. eMenuItem.setToolTipText("Exit application"); This code line creates a tooltip for a menu item. Figure: Simple menu Submenu Each menu can also have a submenu. This way we can group similar commnads into groups. For example we can place commands that hide/show various toolbars like personal bar, address bar, status bar or navigation bar into a submenu called toolbars. Within a menu, we can seperate commands with a separator. It is a simple line. It is common practice to separate commands like new, open, save from commands like print, print preview with a single separator. Menus commands can be launched via keyboard shortcuts. For this, we define menu item accelerators. package com.zetcode; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import import import import import import import javax.swing.ImageIcon; javax.swing.JFrame; javax.swing.JMenu; javax.swing.JMenuBar; javax.swing.JMenuItem; javax.swing.KeyStroke; javax.swing.SwingUtilities; public class Example extends JFrame { public Example() { initUI(); } public final void initUI() { JMenuBar menubar = new JMenuBar(); ImageIcon iconNew = new ImageIcon(getClass().getResource("new.png")); ImageIcon iconOpen = new ImageIcon(getClass().getResource("open.png")); ImageIcon iconSave = new ImageIcon(getClass().getResource("save.png")); ImageIcon iconExit = new ImageIcon(getClass().getResource("exit.png")); JMenu file = new JMenu("File"); file.setMnemonic(KeyEvent.VK_F); JMenu imp = new JMenu("Import"); imp.setMnemonic(KeyEvent.VK_M); JMenuItem newsf = new JMenuItem("Import newsfeed list..."); JMenuItem bookm = new JMenuItem("Import bookmarks..."); JMenuItem mail = new JMenuItem("Import mail..."); imp.add(newsf); imp.add(bookm); imp.add(mail); JMenuItem fileNew = new JMenuItem("New", iconNew); fileNew.setMnemonic(KeyEvent.VK_N); JMenuItem fileOpen = new JMenuItem("Open", iconOpen); fileNew.setMnemonic(KeyEvent.VK_O); JMenuItem fileSave = new JMenuItem("Save", iconSave); fileSave.setMnemonic(KeyEvent.VK_S); JMenuItem fileExit = new JMenuItem("Exit", iconExit); fileExit.setMnemonic(KeyEvent.VK_C); fileExit.setToolTipText("Exit application"); fileExit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_W, ActionEvent.CTRL_MASK)); fileExit.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { System.exit(0); } }); file.add(fileNew); file.add(fileOpen); file.add(fileSave); file.addSeparator(); file.add(imp); file.addSeparator(); file.add(fileExit); menubar.add(file); setJMenuBar(menubar); setTitle("Submenu"); setSize(360, 250); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { Example ex = new Example(); ex.setVisible(true); } }); } } In this example, we create a submenu, a menu separator and an accelerator key. JMenu imp = new JMenu("Import"); ... file.add(imp); A submenu is just like any other normal menu. It is created the same way. We simply add a menu to existing menu. fileExit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_W, ActionEvent.CTRL_MASK)); An accelerator is a key shortcut that launches a menu item. In our case, by pressing Ctrl + W we close the application. file.addSeparator(); A separator is a horizontal line that visually separates the menu items. This way we can group items into some logical places. Figure: Submenu JCheckBoxMenuItem A menu item that can be selected or deselected. If selected, the menu item typically appears with a checkmark next to it. If unselected or deselected, the menu item appears without a checkmark. Like a regular menu item, a check box menu item can have either text or a graphic icon associated with it, or both. package com.zetcode; import import import import java.awt.BorderLayout; java.awt.event.ActionEvent; java.awt.event.ActionListener; java.awt.event.KeyEvent; import import import import import import import import javax.swing.BorderFactory; javax.swing.JCheckBoxMenuItem; javax.swing.JFrame; javax.swing.JLabel; javax.swing.JMenu; javax.swing.JMenuBar; javax.swing.SwingUtilities; javax.swing.border.EtchedBorder; public class Example extends JFrame { private JLabel statusbar; public Example() { initUI(); } public final void initUI() { JMenuBar menubar = new JMenuBar(); JMenu file = new JMenu("File"); file.setMnemonic(KeyEvent.VK_F); JMenu view = new JMenu("View"); view.setMnemonic(KeyEvent.VK_V); JCheckBoxMenuItem sbar = new JCheckBoxMenuItem("Show StatuBar"); sbar.setState(true); sbar.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { if (statusbar.isVisible()) { statusbar.setVisible(false); } else { statusbar.setVisible(true); } } }); view.add(sbar); menubar.add(file); menubar.add(view); setJMenuBar(menubar); statusbar = new JLabel(" Statusbar"); statusbar.setBorder(BorderFactory.createEtchedBorder( EtchedBorder.RAISED)); add(statusbar, BorderLayout.SOUTH); setTitle("JCheckBoxMenuItem"); setSize(360, 250); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { Example ex = new Example(); ex.setVisible(true); } }); } } The example shows a JCheckBoxMenuItem. By selecting the menu item, we toggle the visibility of the statusbar. JCheckBoxMenuItem sbar = new JCheckBoxMenuItem("Show StatuBar"); sbar.setState(true); We create the JCheckBoxMenuItem and check it by default. The statusbar is initially visible. if (statusbar.isVisible()) { statusbar.setVisible(false); } else { statusbar.setVisible(true); } Here we toggle the visibility of the statusbar. statusbar = new JLabel(" Statusbar"); statusbar.setBorder(BorderFactory.createEtchedBorder( EtchedBorder.RAISED)); The statusbar is a simple JLabel component. We put a raised EtchedBorder around the label, so that it is visible. Figure: JCheckBoxMenuItem A popup menu Another type of a menu is a popup menu. It is sometimes called a context menu. It is usually shown, when we right click on a component. The idea is to provide only the commands, that are relevant to the current context. Say we have an image. By right clicking on the image, we get a window with commands to save, rescale, move etc the image. package com.zetcode; import import import import import import import import import java.awt.Toolkit; java.awt.event.ActionEvent; java.awt.event.ActionListener; java.awt.event.MouseAdapter; java.awt.event.MouseEvent; javax.swing.JFrame; javax.swing.JMenuItem; javax.swing.JPopupMenu; javax.swing.SwingUtilities; public class PopupMenu extends JFrame { private JPopupMenu menu; private Toolkit toolkit; public PopupMenu(String title) { super(title); this.initUI(); } private void initUI() { toolkit = this.getToolkit(); menu = new JPopupMenu(); JMenuItem menuItemBeep = new JMenuItem("Beep"); menuItemBeep.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { toolkit.beep(); } }); menu.add(menuItemBeep); JMenuItem menuItemClose = new JMenuItem("Close"); menuItemClose.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { System.exit(0); } }); menu.add(menuItemClose); this.addMouseListener(new MouseAdapter() { @Override public void mouseReleased(MouseEvent e) { if (e.getButton() == MouseEvent.BUTTON3) { menu.show(e.getComponent(), e.getX(), e.getY()); } } }); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setSize(250, 200); this.setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { PopupMenu pm = new PopupMenu("JPopupMenu"); pm.setVisible(true); } }); } } Our example shows a demonstrational popup menu with two commands. The first option of the popup menu will beep a sound, the second one will close the window. In our example, we create a submenu, menu separators and create an accelerator key. menu = new JPopupMenu(); To create a popup menu, we have a class called JPopupMenu. JMenuItem menuItemBeep = new JMenuItem("Beep"); The menu item is the same, as with the standard JMenu this.addMouseListener(new MouseAdapter() { @Override public void mouseReleased(MouseEvent e) { if (e.getButton() == MouseEvent.BUTTON3) { menu.show(e.getComponent(), e.getX(), e.getY()); } } }); The popup menu is shown, where we clicked with the mouse button. The MouseEvent.BUTTON3 constant is here to enable the popup menu only for the mouse right click. Figure: Popup menu JToolbar Menus group commands that we can use in an application. Toolbars provide a quick access to the most frequently used commands. In Java Swing, the JToolBar class creates a toolbar in an application. package com.zetcode; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import import import import import import import javax.swing.ImageIcon; javax.swing.JButton; javax.swing.JFrame; javax.swing.JMenu; javax.swing.JMenuBar; javax.swing.JToolBar; javax.swing.SwingUtilities; public class Example extends JFrame { public Example() { initUI(); } public final void initUI() { JMenuBar menubar = new JMenuBar(); JMenu file = new JMenu("File"); menubar.add(file); setJMenuBar(menubar); JToolBar toolbar = new JToolBar(); ImageIcon icon = new ImageIcon(getClass().getResource("exit.png")); JButton exitButton = new JButton(icon); toolbar.add(exitButton); exitButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { System.exit(0); } }); add(toolbar, BorderLayout.NORTH); setTitle("Simple toolbar"); setSize(300, 200); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { Example ex = new Example(); ex.setVisible(true); } }); } } The example creates a toolbar with one exit button. JToolBar toolbar = new JToolBar(); This is the JToolBar constructor. JButton exitButton = new JButton(icon); toolbar.add(exitButton); We create a button and add it to the toolbar. Figure: Simple toolbar Toolbars Say, we wanted to create two toolbars. The next example shows, how we could do it. package com.zetcode; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import import import import import import import javax.swing.BoxLayout; javax.swing.ImageIcon; javax.swing.JButton; javax.swing.JFrame; javax.swing.JPanel; javax.swing.JToolBar; javax.swing.SwingUtilities; public class Example extends JFrame { public Example() { initUI(); } public final void initUI() { JToolBar toolbar1 = new JToolBar(); JToolBar toolbar2 = new JToolBar(); JPanel panel = new JPanel(); panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); ImageIcon newi = new ImageIcon( getClass().getResource("new.png")); ImageIcon open = new ImageIcon( getClass().getResource("open.png")); ImageIcon save = new ImageIcon( getClass().getResource("save.png")); ImageIcon exit = new ImageIcon( getClass().getResource("exit.png")); JButton newb = new JButton(newi); JButton openb = new JButton(open); JButton saveb = new JButton(save); toolbar1.add(newb); toolbar1.add(openb); toolbar1.add(saveb); toolbar1.setAlignmentX(0); JButton exitb = new JButton(exit); toolbar2.add(exitb); toolbar2.setAlignmentX(0); exitb.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { System.exit(0); } }); panel.add(toolbar1); panel.add(toolbar2); add(panel, BorderLayout.NORTH); setTitle("Toolbars"); setSize(360, 250); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { Example ex = new Example(); ex.setVisible(true); } }); } } We show only one way, how we could create toolbars. Of course, there are several possibilities. We put a JPanel to the north of the BorderLayout manager. The panel has a vertical BoxLayout. We add the two toolbars into this panel. JToolBar toolbar1 = new JToolBar(); JToolBar toolbar2 = new JToolBar(); Creation of two toolbars. JPanel panel = new JPanel(); panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); The panel has a vertical BoxLayout. toolbar1.setAlignmentX(0); The toolbar is left aligned. panel.add(toolbar1); panel.add(toolbar2); add(panel, BorderLayout.NORTH); We add the toolbars to the panel. Finally, the panel is located into the north part of the frame. A simple custom dialog In the following example we create a simple custom dialog. It is a sample about dialog, found in most GUI applications, usually located in the help menu. package zetcode; import import import import import java.awt.Dimension; java.awt.Font; java.awt.event.ActionEvent; java.awt.event.ActionListener; java.awt.event.KeyEvent; import import import import import import import import import import import javax.swing.Box; javax.swing.BoxLayout; javax.swing.ImageIcon; javax.swing.JButton; javax.swing.JDialog; javax.swing.JFrame; javax.swing.JLabel; javax.swing.JMenu; javax.swing.JMenuBar; javax.swing.JMenuItem; javax.swing.SwingUtilities; class AboutDialog extends JDialog { public AboutDialog() { initUI(); } public final void initUI() { setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS)); add(Box.createRigidArea(new Dimension(0, 10))); ImageIcon icon = new ImageIcon("notes.png"); JLabel label = new JLabel(icon); label.setAlignmentX(0.5f); add(label); add(Box.createRigidArea(new Dimension(0, 10))); JLabel name = new JLabel("Notes, 1.23"); name.setFont(new Font("Serif", Font.BOLD, 13)); name.setAlignmentX(0.5f); add(name); add(Box.createRigidArea(new Dimension(0, 50))); JButton close = new JButton("Close"); close.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { dispose(); } }); close.setAlignmentX(0.5f); add(close); setModalityType(ModalityType.APPLICATION_MODAL); setTitle("About Notes"); setDefaultCloseOperation(DISPOSE_ON_CLOSE); setLocationRelativeTo(null); setSize(300, 200); } } public class SimpleDialog extends JFrame { public SimpleDialog() { initUI(); } public final void initUI() { JMenuBar menubar = new JMenuBar(); JMenu file = new JMenu("File"); file.setMnemonic(KeyEvent.VK_F); JMenu help = new JMenu("Help"); help.setMnemonic(KeyEvent.VK_H); JMenuItem about = new JMenuItem("About"); help.add(about); about.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { AboutDialog ad = new AboutDialog(); ad.setVisible(true); } }); menubar.add(file); menubar.add(help); setJMenuBar(menubar); setTitle("Simple Dialog"); setSize(300, 200); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { SimpleDialog sd = new SimpleDialog(); sd.setVisible(true); } }); } } The sample code will popup a small dialog box. The dialog will display an icon a text and one close button. class AboutDialog extends JDialog { The custom dialog is based on the JDialog class. setModalityType(ModalityType.APPLICATION_MODAL); Here we make the dialog modal. setDefaultCloseOperation(DISPOSE_ON_CLOSE); Here we set the defaul close operation. AboutDialog ad = new AboutDialog(); ad.setVisible(true); Here we display the about dialog, from the menu of the main frame. Figure: Simple custom dialog Message boxes Message boxes provide information to the user. package zetcode; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import import import import import javax.swing.JButton; javax.swing.JFrame; javax.swing.JOptionPane; javax.swing.JPanel; javax.swing.SwingUtilities; public class MessageBoxes extends JFrame { private JPanel panel; public MessageBoxes() { initUI(); } public final void initUI() { panel = new JPanel(); panel.setLayout(new GridLayout(2, 2)); JButton JButton JButton JButton error = new JButton("Error"); warning = new JButton("Warning"); question = new JButton("Question"); information = new JButton("Information"); error.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { JOptionPane.showMessageDialog(panel, "Could not open file", "Error", JOptionPane.ERROR_MESSAGE); } }); warning.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { JOptionPane.showMessageDialog(panel, "A deprecated call", "Warning", JOptionPane.WARNING_MESSAGE); } }); question.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { JOptionPane.showMessageDialog(panel, "Are you sure to quit?", "Question", JOptionPane.QUESTION_MESSAGE); } }); information.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { JOptionPane.showMessageDialog(panel, "Download completed", "Question", JOptionPane.INFORMATION_MESSAGE); } }); panel.add(error); panel.add(warning); panel.add(question); panel.add(information); add(panel); setTitle("Message Boxes"); setSize(300, 200); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { MessageBoxes mb = new MessageBoxes(); mb.setVisible(true); } }); } } The example shows an error, question, warning and information message boxes. panel.setLayout(new GridLayout(2, 2)); We use a GridLayout layout manager to organize buttons, that will popup message boxes. JButton JButton JButton JButton error = new JButton("Error"); warning = new JButton("Warning"); question = new JButton("Question"); information = new JButton("Information"); Here are the four buttons, that we will use. JOptionPane.showMessageDialog(panel, "Could not open file", "Error", JOptionPane.ERROR_MESSAGE); To create a message box, we call the showMessageDialog static method of the JOptionPane class. We provide the component name, message text, title and a message type. The message type is determined by the constant we choose. Available constants are: ERROR_MESSAGE WARNING_MESSAGE QUESTION_MESSAGE INFORMATION_MESSAGE Figure: Question message box JFileChooser JFileChooser is a standard dialog for selecting a file from the file system. package zetcode; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import import import import java.io.BufferedReader; java.io.File; java.io.FileReader; java.io.IOException; import import import import import import import import import import import import javax.swing.BorderFactory; javax.swing.ImageIcon; javax.swing.JButton; javax.swing.JFileChooser; javax.swing.JFrame; javax.swing.JPanel; javax.swing.JScrollPane; javax.swing.JTextArea; javax.swing.JToolBar; javax.swing.SwingUtilities; javax.swing.filechooser.FileFilter; javax.swing.filechooser.FileNameExtensionFilter; public class FileChooserDialog extends JFrame { private JPanel panel; private JTextArea area; public FileChooserDialog() { initUI(); } public final void initUI() { panel = new JPanel(); panel.setLayout(new BorderLayout()); ImageIcon open = new ImageIcon("open.png"); JToolBar toolbar = new JToolBar(); JButton openb = new JButton(open); openb.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { JFileChooser fileopen = new JFileChooser(); FileFilter filter = new FileNameExtensionFilter("c files", "c"); fileopen.addChoosableFileFilter(filter); int ret = fileopen.showDialog(panel, "Open file"); if (ret == JFileChooser.APPROVE_OPTION) { File file = fileopen.getSelectedFile(); String text = readFile(file); area.setText(text); } } }); toolbar.add(openb); area = new JTextArea(); area.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); JScrollPane pane = new JScrollPane(); pane.getViewport().add(area); panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); panel.add(pane); add(panel); add(toolbar, BorderLayout.NORTH); setTitle("FileChooserDialog"); setSize(400, 300); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); } public String readFile(File file) { StringBuffer fileBuffer = null; String fileString = null; String line = null; try { FileReader in = new FileReader(file); BufferedReader brd = new BufferedReader(in); fileBuffer = new StringBuffer(); while ((line = brd.readLine()) != null) { fileBuffer.append(line).append( System.getProperty("line.separator")); } in.close(); fileString = fileBuffer.toString(); } catch (IOException e) { return null; } return fileString; } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { FileChooserDialog fcd = new FileChooserDialog(); fcd.setVisible(true); } }); } } The code example will demonstrate how to use a file chooser dialog in order to load file contents into the text area component. JFileChooser fileopen = new JFileChooser(); This is the constructor of the file chooser dialog. FileFilter filter = new FileNameExtensionFilter("c files", "c"); fileopen.addChoosableFileFilter(filter); Here we define the file filter. In our case, we will have c files with extension .c. We have also the default All files option. int ret = fileopen.showDialog(panel, "Open file"); Here we show the file chooser dialog. Upon clicking on the open file button, the return value is equal to JFileChooser.APPROVE_OPTION. if (ret == JFileChooser.APPROVE_OPTION) { File file = fileopen.getSelectedFile(); String text = readFile(file); area.setText(text); } Here we get the name of the selected file. We read the contents of the file and set the text into the textarea. Figure: JFileChooser dialog JColorChooser JColorChooser is a standard dialog for selecting a color. package zetcode; import import import import java.awt.BorderLayout; java.awt.Color; java.awt.event.ActionEvent; java.awt.event.ActionListener; import import import import import import import import javax.swing.BorderFactory; javax.swing.ImageIcon; javax.swing.JButton; javax.swing.JColorChooser; javax.swing.JFrame; javax.swing.JPanel; javax.swing.JToolBar; javax.swing.SwingUtilities; public class ColorChooserDialog extends JFrame { private JPanel panel; private JPanel display; public ColorChooserDialog() { initUI(); } public final void initUI() { panel = new JPanel(); panel.setLayout(new BorderLayout()); ImageIcon open = new ImageIcon("color.png"); JToolBar toolbar = new JToolBar(); JButton openb = new JButton(open); openb.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { JColorChooser clr = new JColorChooser(); Color color = clr.showDialog(panel, "Choose Color", Color.white); display.setBackground(color); } }); toolbar.add(openb); display = new JPanel(); display.setBackground(Color.WHITE); panel.setBorder(BorderFactory.createEmptyBorder(30, 50, 30, 50)); panel.add(display); add(panel); add(toolbar, BorderLayout.NORTH); setTitle("ColorChooserDialog"); setSize(400, 300); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { ColorChooserDialog ccd = new ColorChooserDialog(); ccd.setVisible(true); } }); } } In the example, we have a white panel. We will change the background color of the panel by selecting a color from the color chooser dialog. JColorChooser clr = new JColorChooser(); Color color = clr.showDialog(panel, "Choose Color", Color.white); display.setBackground(color); This code shows a color chooser dialog. The showDialog() method returns the selected color value. We change the display panel background to the newly selected color. Figure: JColorChooser dialog ButtonModel The model is used for various kinds of buttons like push buttons, check boxes, radio boxes and for menu items. The following example illustrates the model for a JButton. We can manage only the state of the button, because no data can be associated with a push button. import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import import import import import import import import javax.swing.DefaultButtonModel; javax.swing.JButton; javax.swing.JCheckBox; javax.swing.JFrame; javax.swing.JLabel; javax.swing.JPanel; javax.swing.event.ChangeEvent; javax.swing.event.ChangeListener; public class ButtonModel extends JFrame { private private private private JButton ok; JLabel enabled; JLabel pressed; JLabel armed; public ButtonModel() { setTitle("ButtonModel"); JPanel panel = new JPanel(); panel.setLayout(null); ok = new JButton("ok"); JCheckBox cb = new JCheckBox("Enabled", true); ok.setBounds(40, 30, 80, 25); ok.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { DefaultButtonModel model = (DefaultButtonModel) ok.getModel(); if (model.isEnabled()) enabled.setText("Enabled: true"); else enabled.setText("Enabled: false"); if (model.isArmed()) armed.setText("Armed: true"); else armed.setText("Armed: false"); if (model.isPressed()) pressed.setText("Pressed: true"); else pressed.setText("Pressed: false"); } }); cb.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (ok.isEnabled()) ok.setEnabled(false); else ok.setEnabled(true); } }); cb.setBounds(180, 30, 100, 25); enabled = new JLabel("Enabled: true"); enabled.setBounds(40, 90, 90, 25); pressed = new JLabel("Pressed: false"); pressed.setBounds(40, 120, 90, 25); armed = new JLabel("Armed: false"); armed.setBounds(40, 150, 90, 25); panel.add(ok); panel.add(cb); panel.add(enabled); panel.add(pressed); panel.add(armed); add(panel); setSize(350, 250); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); setVisible(true); } public static void main(String[] args) { new ButtonModel(); } } In our example, we have a button, check box and three labels. The labels represent three properties of the button. Whether it is pressed, disabled or armed. ok.addChangeListener(new ChangeListener() { We use a lightweight ChangeListener to listen for button state changes. DefaultButtonModel model = (DefaultButtonModel) ok.getModel(); Here we get the default button model. if (model.isEnabled()) enabled.setText("Enabled: true"); else enabled.setText("Enabled: false"); We query the model, whether the button is enabled or not. We update the label accordingly. if (ok.isEnabled()) ok.setEnabled(false); else ok.setEnabled(true); The check box enables or disables the button. To enable the ok button, we call the setEnable() method. So we change the state of the button. Where is the model? The answer lies in the AbstractButton.java file. public void setEnabled(boolean b) { if (!b && model.isRollover()) { model.setRollover(false); } super.setEnabled(b); model.setEnabled(b); } The answer is, that internally, we the Swing toolkit works with a model. The setEnable() is another convenience method for programmers. Figure: ButtonModel Custom ButtonModel In the previous example, we used a default button model. In the following code example we will use our own button model. package zetcode; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.*; public class ButtonModel2 extends JFrame { private private private private JButton ok; JLabel enabled; JLabel pressed; JLabel armed; public ButtonModel2(String title) { super(title); this.initUI(); } private void initUI() { setTitle("ButtonModel"); JPanel panel = new JPanel(); panel.setLayout(null); ok = new JButton("ok"); JCheckBox cb = new JCheckBox("Enabled", true); ok.setBounds(40, 30, 80, 25); cb.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (ok.isEnabled()) ok.setEnabled(false); else ok.setEnabled(true); } }); cb.setBounds(180, 30, 100, 25); enabled = new JLabel("Enabled: true"); enabled.setBounds(40, 90, 120, 25); pressed = new JLabel("Pressed: false"); pressed.setBounds(40, 120, 120, 25); armed = new JLabel("Armed: false"); armed.setBounds(40, 150, 120, 25); ButtonModel model = new DefaultButtonModel() { @Override public void setEnabled(boolean b) { if (b) enabled.setText("Enabled: true"); else enabled.setText("Enabled: false"); super.setEnabled(b); } @Override public void setArmed(boolean b) { if (b) armed.setText("Armed: true"); else armed.setText("Armed: false"); super.setArmed(b); } @Override public void setPressed(boolean b) { if (b) pressed.setText("Pressed: true"); else pressed.setText("Pressed: false"); super.setPressed(b); } }; ok.setModel(model); panel.add(ok); panel.add(cb); panel.add(enabled); panel.add(pressed); panel.add(armed); add(panel); setSize(350, 250); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); setVisible(true); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { ButtonModel2 ex = new ButtonModel2("ButtonModel"); ex.setVisible(true); } }); } } This example does the same thing as the previous one. The difference is that we do not use a change listener and we use a custom button model. ButtonModel model = new DefaultButtonModel() { We create a button model and overwrite the necessary methods. @Override public void setEnabled(boolean b) { if (b) enabled.setText("Enabled: true"); else enabled.setText("Enabled: false"); super.setEnabled(b); } We overwrite the setEnabled() method and add some functionality there. We must not forget to call the parent method as well to procede with the processing. ok.setModel(model); We set the custom model for the button. JList models Several components have two models. The JList component has the following models: ListModel and ListSelectionModel. The ListModel handles data. And the ListSelectionModel works with the GUI. The following example shows both models. import import import import import import java.awt.BorderLayout; java.awt.Dimension; java.awt.event.ActionEvent; java.awt.event.ActionListener; java.awt.event.MouseAdapter; java.awt.event.MouseEvent; import import import import import import import import import import import javax.swing.BorderFactory; javax.swing.Box; javax.swing.BoxLayout; javax.swing.DefaultListModel; javax.swing.JButton; javax.swing.JFrame; javax.swing.JList; javax.swing.JOptionPane; javax.swing.JPanel; javax.swing.JScrollPane; javax.swing.ListSelectionModel; public class List extends JFrame { private DefaultListModel model; private JList list; public List() { setTitle("JList models"); model = new DefaultListModel(); model.addElement("Amelie"); model.addElement("Aguirre, der Zorn Gottes"); model.addElement("Fargo"); model.addElement("Exorcist"); model.addElement("Schindler list"); JPanel panel = new JPanel(); panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); JPanel leftPanel = new JPanel(); JPanel rightPanel = new JPanel(); leftPanel.setLayout(new BorderLayout()); rightPanel.setLayout(new BoxLayout(rightPanel, BoxLayout.Y_AXIS)); list = new JList(model); list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); list.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); list.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { if(e.getClickCount() == 2){ int index = list.locationToIndex(e.getPoint()); Object item = model.getElementAt(index); String text = JOptionPane.showInputDialog("Rename item", item); String newitem = null; if (text != null) newitem = text.trim(); else return; if (!newitem.isEmpty()) { model.remove(index); model.add(index, newitem); ListSelectionModel selmodel = list.getSelectionModel(); selmodel.setLeadSelectionIndex(index); } } } }); JScrollPane pane = new JScrollPane(); pane.getViewport().add(list); leftPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); leftPanel.add(pane); JButton removeall = new JButton("Remove All"); JButton add = new JButton("Add"); add.setMaximumSize(removeall.getMaximumSize()); JButton rename = new JButton("Rename"); rename.setMaximumSize(removeall.getMaximumSize()); JButton delete = new JButton("Delete"); delete.setMaximumSize(removeall.getMaximumSize()); add.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { String text = JOptionPane.showInputDialog("Add a new item"); String item = null; if (text != null) item = text.trim(); else return; if (!item.isEmpty()) model.addElement(item); } }); delete.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { ListSelectionModel selmodel = list.getSelectionModel(); int index = selmodel.getMinSelectionIndex(); if (index >= 0) model.remove(index); } }); rename.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { ListSelectionModel selmodel = list.getSelectionModel(); int index = selmodel.getMinSelectionIndex(); if (index == -1) return; Object item = model.getElementAt(index); String text = JOptionPane.showInputDialog("Rename item", item); String newitem = null; if (text != null) { newitem = text.trim(); } else return; if (!newitem.isEmpty()) { model.remove(index); model.add(index, newitem); } } }); removeall.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { model.clear(); } }); rightPanel.add(add); rightPanel.add(Box.createRigidArea(new Dimension(0,4))); rightPanel.add(rename); rightPanel.add(Box.createRigidArea(new Dimension(0,4))); rightPanel.add(delete); rightPanel.add(Box.createRigidArea(new Dimension(0,4))); rightPanel.add(removeall); rightPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 20)); panel.add(leftPanel); panel.add(rightPanel); add(panel); setSize(350, 250); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); setVisible(true); } public static void main(String[] args) { new List(); } } The example shows a list component and four buttons. The buttons control the data in the list component. The example is a bit larger, because we did some additional checks there. We do not allow to input empty spaces into the list component. model = new DefaultListModel(); model.addElement("Amelie"); model.addElement("Aguirre, der Zorn Gottes"); ... We create a list model and add elements into it. list = new JList(model); list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); list.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); We create a list component. The parameter of the constructor is the model, we have created. We put the list into the single selection mode. We also put some space around the list. if (text != null) item = text.trim(); else return; if (!item.isEmpty()) model.addElement(item); We add only items that are not equal to null and are not empty. e.g. that contain at least one character other than white space. It makes no sense to add white spaces or null values into the list. ListSelectionModel selmodel = list.getSelectionModel(); int index = selmodel.getMinSelectionIndex(); if (index >= 0) model.remove(index); This is the code, that runs when we press the delete button. In order to delete an item from the list, it must be selected. So we must figure out the currently selected item. For this, we call the getSelectionModel() method. This is a GUI work, so we use a ListSelectionModel. Removing an item is working with data. For that we use the list data model. So, in our example we used both list models. We called add(), remove() and clear() methods of the list data model to work with our data. And we used a list selection model in order to find out the selected item, which is a GUI job. Figure: List Models A document model This is an excellent example of a separation of a data from the visual representation. In a JTextPane component, we have a StyledDocument for setting the style of the text data. import import import import java.awt.BorderLayout; java.awt.Dimension; java.awt.event.ActionEvent; java.awt.event.ActionListener; import import import import import import import import import import import javax.swing.BorderFactory; javax.swing.ImageIcon; javax.swing.JButton; javax.swing.JFrame; javax.swing.JPanel; javax.swing.JScrollPane; javax.swing.JTextPane; javax.swing.JToolBar; javax.swing.text.Style; javax.swing.text.StyleConstants; javax.swing.text.StyledDocument; public class DocumentModel extends JFrame { private StyledDocument doc; private JTextPane textpane; public DocumentModel() { setTitle("Document Model"); JToolBar toolbar = new JToolBar(); ImageIcon ImageIcon ImageIcon ImageIcon bold = new ImageIcon("bold.png"); italic = new ImageIcon("italic.png"); strike = new ImageIcon("strike.png"); underline = new ImageIcon("underline.png"); JButton JButton JButton JButton boldb italb strib undeb = = = = new new new new JButton(bold); JButton(italic); JButton(strike); JButton(underline); toolbar.add(boldb); toolbar.add(italb); toolbar.add(strib); toolbar.add(undeb); add(toolbar, BorderLayout.NORTH); JPanel panel = new JPanel(); panel.setLayout(new BorderLayout()); panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); JScrollPane pane = new JScrollPane(); textpane = new JTextPane(); textpane.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); doc = textpane.getStyledDocument(); Style style = textpane.addStyle("Bold", null); StyleConstants.setBold(style, true); style = textpane.addStyle("Italic", null); StyleConstants.setItalic(style, true); style = textpane.addStyle("Underline", null); StyleConstants.setUnderline(style, true); style = textpane.addStyle("Strike", null); StyleConstants.setStrikeThrough(style, true); boldb.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { doc.setCharacterAttributes(textpane.getSelectionStart(), textpane.getSelectionEnd() textpane.getSelectionStart(), textpane.getStyle("Bold"), false); } }); italb.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { doc.setCharacterAttributes(textpane.getSelectionStart(), textpane.getSelectionEnd() textpane.getSelectionStart(), textpane.getStyle("Italic"), false); } }); strib.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { doc.setCharacterAttributes(textpane.getSelectionStart(), textpane.getSelectionEnd() textpane.getSelectionStart(), textpane.getStyle("Strike"), false); } }); undeb.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { doc.setCharacterAttributes(textpane.getSelectionStart(), textpane.getSelectionEnd() textpane.getSelectionStart(), textpane.getStyle("Underline"), false); } }); pane.getViewport().add(textpane); panel.add(pane); add(panel); setSize(new Dimension(380, 320)); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); setVisible(true); } public static void main(String[] args) { new DocumentModel(); } } The example has a text pane and a toolbar. In the toolbar, we have four buttons, that change attributes of the text. doc = textpane.getStyledDocument(); Here we get the styled document, which is a model for the text pane component. Style style = textpane.addStyle("Bold", null); StyleConstants.setBold(style, true); A style is a set of text attributes, such as color, size. Here we register a bold style for the text pane component. The registered styles can be retrieved at any time. doc.setCharacterAttributes(textpane.getSelectionStart(), textpane.getSelectionEnd() - textpane.getSelectionStart(), textpane.getStyle("Bold"), false); Here we change the attributes of the text. The parameters are the offset, length of the selection, the style and the boolean value replace. The offset is the beginning of the text, where we apply the bold text. We get the length value by substracting the selection end and selection start values. Boolean value false means, we are not replacing an old style with a new one, but we merge them. This means, if the text is underlined and we make it bold, the result is an underlined bold text. Figure: Document model Drag and drop functionality is one of the most visible aspects of the graphical user interface. Drag and drop operation enables users to do complex things intuitively. Usually, we can drag and drop two things. Data or some graphical objects. If we drag an image from one application to another, we drag and drop binary data. If we drag a tab in Firefox and move it to another place, we drag and drop a graphical component. The sheer amount of various classes involved with drag and drop operations in Java Swing toolkit might be overwhelming. The best way how to cope with this complexity is to create a small example for all situations. And slowly make it to more complex examples. The component, where the drag operation begins must have a DragSource object registered. A DropTarget is an object responsible for accepting drops in an drag and drop operation. A Transferable encapsulates data being transferred. The transferred data can be of various type. A DataFlavor object provides information about the data being transferred. Several Swing components have already a built-in support for drag and drop operations. In such cases, a Swing programmer uses a TransferHandler to manage the drag and drop functionality. In situations, where there is no built-in support, the programmer has to create everything from scratch. A simple drag and drop example We will demonstrate a simple drag and drop example. We will work with built-in drag and drop support. We will utilize a TransferHandler class. import import import import javax.swing.JButton; javax.swing.JFrame; javax.swing.JTextField; javax.swing.TransferHandler; public class SimpleDnD extends JFrame { JTextField field; JButton button; public SimpleDnD() { setTitle("Simple Drag & Drop"); setLayout(null); button = new JButton("Button"); button.setBounds(200, 50, 90, 25); field = new JTextField(); field.setBounds(30, 50, 150, 25); add(button); add(field); field.setDragEnabled(true); button.setTransferHandler(new TransferHandler("text")); setSize(330, 150); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); setVisible(true); } public static void main(String[] args) { new SimpleDnD(); } } In our example we have a text field and a button. We can drag a text from the field and drop it onto the button. field.setDragEnabled(true); The text field has a built in support for dragging. We must enable it. button.setTransferHandler(new TransferHandler("text")); The TransferHandler is a class responsible for transfering data between components. The constructor takes a property name as a parameter. Figure: Simple drag & drop example Icon drag & drop Some of the Java Swing components do not have built in drag support. JLabel component is such a component. We have to code the drag functionality ourselves. We will drag and drop icons. In the previous example, we used a text property. This time we will use an icon property. import import import import java.awt.FlowLayout; java.awt.event.MouseAdapter; java.awt.event.MouseEvent; java.awt.event.MouseListener; import import import import import import import javax.swing.ImageIcon; javax.swing.JButton; javax.swing.JComponent; javax.swing.JFrame; javax.swing.JLabel; javax.swing.JPanel; javax.swing.TransferHandler; public class IconDnD extends JFrame { public IconDnD() { setTitle("Icon Drag & Drop"); JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 50, 15)); ImageIcon icon1 = new ImageIcon("sad.png"); ImageIcon icon2 = new ImageIcon("plain.png"); ImageIcon icon3 = new ImageIcon("crying.png"); JButton button = new JButton(icon2); button.setFocusable(false); JLabel label1 JLabel label2 = new JLabel(icon1, JLabel.CENTER); = new JLabel(icon3, JLabel.CENTER); MouseListener listener = new DragMouseAdapter(); label1.addMouseListener(listener); label2.addMouseListener(listener); label1.setTransferHandler(new TransferHandler("icon")); button.setTransferHandler(new TransferHandler("icon")); label2.setTransferHandler(new TransferHandler("icon")); panel.add(label1); panel.add(button); panel.add(label2); add(panel); pack(); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); setVisible(true); } class DragMouseAdapter extends MouseAdapter { public void mousePressed(MouseEvent e) { JComponent c = (JComponent) e.getSource(); TransferHandler handler = c.getTransferHandler(); handler.exportAsDrag(c, e, TransferHandler.COPY); } } public static void main(String[] args) { new IconDnD(); } } In the code example, we have two labels and a button. Each component displays an icon. The two labels enable drag gestures, the button accepts a drop gesture. MouseListener listener = new DragMouseAdapter(); label1.addMouseListener(listener); label2.addMouseListener(listener); The drag support is not enabled by default for the label. We register a custom mouse adapter for both labels. label1.setTransferHandler(new TransferHandler("icon")); button.setTransferHandler(new TransferHandler("icon")); label2.setTransferHandler(new TransferHandler("icon")); Each of the three components has a TransferHandler class for an icon property. The TransferHandler is needed for both drag sources and drag targets as well. JComponent c = (JComponent) e.getSource(); TransferHandler handler = c.getTransferHandler(); handler.exportAsDrag(c, e, TransferHandler.COPY); These code lines initiate the drag support. We get the drag source. In our case it is a label instance. We get it's transfer handler object. And finally initiate the drag support with the exportAsDrag() method call. Figure: Icon drag & drop example Custom JList drop example Some components do not have a default drop support. One of them is a JList component. There is a good reason for this. We don't know, if the data will be inserted into one row, or two or more rows. So we must implement manually the drop support for the list component. The comma separated text will be inserted into two or more rows. Text without a comma will go into one row. import import import import java.awt.Dimension; java.awt.FlowLayout; java.awt.datatransfer.DataFlavor; java.awt.datatransfer.Transferable; import import import import import import javax.swing.DefaultListModel; javax.swing.DropMode; javax.swing.JFrame; javax.swing.JList; javax.swing.JPanel; javax.swing.JScrollPane; import javax.swing.JTextField; import javax.swing.ListSelectionModel; import javax.swing.TransferHandler; public class ListDrop extends JFrame { JTextField field; DefaultListModel model; public ListDrop() { setTitle("ListDrop"); JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 15, 15)); JScrollPane pane = new JScrollPane(); pane.setPreferredSize(new Dimension(180, 150)); model = new DefaultListModel(); JList list = new JList(model); list.setDropMode(DropMode.INSERT); list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); list.setTransferHandler(new ListHandler()); field = new JTextField(""); field.setPreferredSize(new Dimension(150, 25)); field.setDragEnabled(true); panel.add(field); pane.getViewport().add(list); panel.add(pane); add(panel); pack(); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); setVisible(true); } private class ListHandler extends TransferHandler { public boolean canImport(TransferSupport support) { if (!support.isDrop()) { return false; } return support.isDataFlavorSupported(DataFlavor.stringFlavor); } public boolean importData(TransferSupport support) { if (!canImport(support)) { return false; } Transferable transferable = support.getTransferable(); String line; try { line = (String) transferable.getTransferData(DataFlavor.stringFlavor); } catch (Exception e) { return false; } JList.DropLocation dl = (JList.DropLocation) support.getDropLocation(); int index = dl.getIndex(); String[] data = line.split(","); for (String item: data) { if (!item.isEmpty()) model.add(index++, item.trim()); } return true; } } public static void main(String[] args) { new ListDrop(); } } In the above example, we have a text field and a list component. The text in the text field can be dragged and dropped into the list. If the text is comma separated, the words will be split into rows. If not, the text will be inserted into one row. list.setDropMode(DropMode.INSERT); Here we specify a drop mode. The DropMode.INSERT specifies, that we are going to insert new items into the list component. If we chose DropMode.INSERT, we would drop new items onto the existing ones. list.setTransferHandler(new ListHandler()); We set a custom transfer handler class. field.setDragEnabled(true); We enable the drag support for the text field component. public boolean canImport(TransferSupport support) { if (!support.isDrop()) { return false; } return support.isDataFlavorSupported(DataFlavor.stringFlavor); } This method tests suitability of a drop operation. Here we filter out the clipboard paste operations and allow only String drop operations. If the method returns false, the drop operation is cancelled. public boolean importData(TransferSupport support) { ... } The importData() method transfers the data from the clipboard or from the drag and drop operation to the drop location. Transferable transferable = support.getTransferable(); The Transferable is the class, where the data is bundled. line = (String) transferable.getTransferData(DataFlavor.stringFlavor); We retrieve our data. JList.DropLocation dl = (JList.DropLocation) support.getDropLocation(); int index = dl.getIndex(); We get a drop location for the list. We retrieve the index, where the data will be inserted. String[] data = line.split(","); for (String item: data) { if (!item.isEmpty()) model.add(index++, item.trim()); } Here we split the text into parts and insert it into one or more rows. Figure: JList drop example The previous examples used components with built-in drag and drop support. Next we are going to create a drag and drop functionality from scratch. Drag Gesture In the following example we will inspect a simple drag gesture. We will work with several classes needed to create a drag gesture. A DragSource, DragGestureEvent, DragGestureListener, Transferable. import import import import import import import import import import java.awt.Color; java.awt.Cursor; java.awt.Dimension; java.awt.FlowLayout; java.awt.datatransfer.DataFlavor; java.awt.datatransfer.Transferable; java.awt.dnd.DnDConstants; java.awt.dnd.DragGestureEvent; java.awt.dnd.DragGestureListener; java.awt.dnd.DragSource; import javax.swing.JFrame; import javax.swing.JPanel; public class DragGesture extends JFrame implements DragGestureListener, Transferable { public DragGesture() { setTitle("Drag Gesture"); JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 50, 15)); JPanel left = new JPanel(); left.setBackground(Color.red); left.setPreferredSize(new Dimension(120, 120)); DragSource ds = new DragSource(); ds.createDefaultDragGestureRecognizer(left, DnDConstants.ACTION_COPY, this); panel.add(left); add(panel); pack(); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); setVisible(true); } public void dragGestureRecognized(DragGestureEvent event) { System.out.println("grag gesture"); Cursor cursor = null; if (event.getDragAction() == DnDConstants.ACTION_COPY) { cursor = DragSource.DefaultCopyDrop; } event.startDrag(cursor, this); } public static void main(String[] args) { new DragGesture(); } public Object getTransferData(DataFlavor flavor) { return null; } public DataFlavor[] getTransferDataFlavors() { return new DataFlavor[0]; } public boolean isDataFlavorSupported(DataFlavor flavor) { return false; } } This simple example demostrates a drag gesture. The drag gesture is created, when we click on a component and move a mouse pointer, while the button is pressed. The example will show, how we can create a DragSource for a component. public class DragGesture extends JFrame implements DragGestureListener, Transferable { The DragGesture implements two interfaces. The DragGestureListener will listen for drag gestures. The Transferable handles data for a transfer operation. In the example, we will not transfer any data. We will only demonstrate a drag gesture. So the three necessary methods of the Transferable interface are left unimplemented. DragSource ds = new DragSource(); ds.createDefaultDragGestureRecognizer(left, DnDConstants.ACTION_COPY, this); Here we create a DragSource object and register it for the left panel. The DragSource is the entity responsible for the initiation of the Drag and Drop operation. The createDefaultDragGestureRecognizer() associates a drag source and DragGestureListener with a particular component. public void dragGestureRecognized(DragGestureEvent event) { } The dragGestureRecognized() method responds to a drag gesture. Cursor cursor = null; if (event.getDragAction() == DnDConstants.ACTION_COPY) { cursor = DragSource.DefaultCopyDrop; } event.startDrag(cursor, this); The startDrag() method of the DragGestureEvent finally starts the drag operation. We will specify two parameters. The cursor type and the Transferable object. public Object getTransferData(DataFlavor flavor) { return null; } public DataFlavor[] getTransferDataFlavors() { return new DataFlavor[0]; } public boolean isDataFlavorSupported(DataFlavor flavor) { return false; } The object that implements the Transferable interface must implement these three methods. As I have already mentioned, we left these methods unimplemented for now. A complex drag and drop example In the following example, we create a complex drag and drop example. We create a drag source a drop target and a transferable object. import import import import import import import import import import import import import import import import java.awt.Color; java.awt.Cursor; java.awt.Dimension; java.awt.FlowLayout; java.awt.datatransfer.DataFlavor; java.awt.datatransfer.Transferable; java.awt.datatransfer.UnsupportedFlavorException; java.awt.dnd.DnDConstants; java.awt.dnd.DragGestureEvent; java.awt.dnd.DragGestureListener; java.awt.dnd.DragSource; java.awt.dnd.DropTarget; java.awt.dnd.DropTargetAdapter; java.awt.dnd.DropTargetDropEvent; java.awt.event.ActionEvent; java.awt.event.ActionListener; import import import import javax.swing.JButton; javax.swing.JColorChooser; javax.swing.JFrame; javax.swing.JPanel; public class ComplexExample extends JFrame implements DragGestureListener { JPanel panel; JPanel left; public ComplexExample() { setTitle("Complex Example"); panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 50, 15)); JButton openb = new JButton("Choose Color"); openb.setFocusable(false); left = new JPanel(); left.setBackground(Color.red); left.setPreferredSize(new Dimension(100, 100)); openb.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { JColorChooser clr = new JColorChooser(); Color color = clr.showDialog(panel, "Choose Color", Color.white); left.setBackground(color); } }); JPanel right = new JPanel(); right.setBackground(Color.white); right.setPreferredSize(new Dimension(100, 100)); new MyDropTargetListener(right); DragSource ds = new DragSource(); ds.createDefaultDragGestureRecognizer(left, DnDConstants.ACTION_COPY, this); panel.add(openb); panel.add(left); panel.add(right); add(panel); pack(); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); setVisible(true); } public void dragGestureRecognized(DragGestureEvent event) { Cursor cursor = null; JPanel panel = (JPanel) event.getComponent(); Color color = panel.getBackground(); if (event.getDragAction() == DnDConstants.ACTION_COPY) { cursor = DragSource.DefaultCopyDrop; } event.startDrag(cursor, new TransferableColor(color)); } class MyDropTargetListener extends DropTargetAdapter { private DropTarget dropTarget; private JPanel panel; public MyDropTargetListener(JPanel panel) { this.panel = panel; dropTarget = new DropTarget(panel, DnDConstants.ACTION_COPY, this, true, null); } public void drop(DropTargetDropEvent event) { try { Transferable tr = event.getTransferable(); Color color = (Color) tr.getTransferData(TransferableColor.colorFlavor); if (event.isDataFlavorSupported(TransferableColor.colorFlavor)) { event.acceptDrop(DnDConstants.ACTION_COPY); this.panel.setBackground(color); event.dropComplete(true); return; } event.rejectDrop(); } catch (Exception e) { e.printStackTrace(); event.rejectDrop(); } } } public static void main(String[] args) { new ComplexExample(); } } class TransferableColor implements Transferable { protected static DataFlavor colorFlavor = new DataFlavor(Color.class, "A Color Object"); protected static DataFlavor[] supportedFlavors = { colorFlavor, DataFlavor.stringFlavor, }; Color color; public TransferableColor(Color color) { this.color = color; } public DataFlavor[] getTransferDataFlavors() { return supportedFlavors; } public boolean isDataFlavorSupported(DataFlavor flavor) { if (flavor.equals(colorFlavor) || flavor.equals(DataFlavor.stringFlavor)) return true; return false; } public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException { if (flavor.equals(colorFlavor)) return color; else if (flavor.equals(DataFlavor.stringFlavor)) return color.toString(); else throw new UnsupportedFlavorException(flavor); } } The code example shows a button and two panels. The button displays a color chooser dialog and sets a color for the first panel. The color can be dragged into the second panel. This example will enhance the previous one. We will add a drop target and a custom transferable object. new MyDropTargetListener(right); We register a drop target listener with the right panel. event.startDrag(cursor, new TransferableColor(color)); The startDrag() method has two parameters. A cursor and a Transferable object. public MyDropTargetListener(JPanel panel) { this.panel = panel; dropTarget = new DropTarget(panel, DnDConstants.ACTION_COPY, this, true, null); } In the MyDropTargetListener we create a drop target object. Transferable tr = event.getTransferable(); Color color = (Color) tr.getTransferData(TransferableColor.colorFlavor); if (event.isDataFlavorSupported(TransferableColor.colorFlavor)) { event.acceptDrop(DnDConstants.ACTION_COPY); this.panel.setBackground(color); event.dropComplete(true); return; } We get the data being transferred. In our case it is a color object. Here we set the color of the right panel. event.rejectDrop(); If the conditions for a drag and drop operation are not fulfilled, we reject it. protected static DataFlavor colorFlavor = new DataFlavor(Color.class, "A Color Object"); In the TransferableColor, we create a new DataFlavor object. protected static DataFlavor[] supportedFlavors = { colorFlavor, DataFlavor.stringFlavor, }; Here we specify, what data flavors we support. In our case it is a custom defined color flavor and a pre-defined DataFlavor.stringFlavor. public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException { if (flavor.equals(colorFlavor)) return color; else if (flavor.equals(DataFlavor.stringFlavor)) return color.toString(); else throw new UnsupportedFlavorException(flavor); } Return an object for a specific data flavor. Figure: A complex example Layout management In this part of the JRuby Swing programming tutorial, we will introduce layout managers. When we design the GUI of our application, we decide what components we will use and how we will organize those components in the application. To organize our components, we use specialized non visible objects called layout managers. The Swing toolkit has two kind of components. Containers and children. The containers group children into suitable layouts. To create layouts, we use layout managers. Absolute positioning In most cases, programmers should use layout managers. There are a few situations, where we can use absolute positioning. In absolute positioning, the programmer specifies the position and the size of each component in pixels. The size and the position of a component do not change, if you resize a window. Applications look different on various platforms, and what looks OK on Linux, might not look OK on Mac. Changing fonts in your application might spoil the layout. If you translate your application into another language, you must redo your layout. For all these issues, use the absolute positioning only when you have a reason to do so. #!/usr/local/bin/jruby # # # # # # # # ZetCode JRuby Swing tutorial In this program, we lay out three images using absolute positioning. author: Jan Bodnar website: www.zetcode.com last modified: December 2010 include Java import import import import import java.awt.Color javax.swing.ImageIcon javax.swing.JLabel javax.swing.JPanel javax.swing.JFrame class Example < JFrame def initialize super "Absolute" self.initUI end def initUI panel = JPanel.new panel.setLayout nil panel.setBackground Color.new 66, 66, 66 self.getContentPane.add panel rot = ImageIcon.new "rotunda.jpg" rotLabel = JLabel.new rot rotLabel.setBounds 20, 20, rot.getIconWidth, rot.getIconHeight min = ImageIcon.new "mincol.jpg" minLabel = JLabel.new min minLabel.setBounds 40, 160, min.getIconWidth, min.getIconHeight bar = ImageIcon.new "bardejov.jpg" barLabel = JLabel.new bar barLabel.setBounds 170, 50, bar.getIconWidth, bar.getIconHeight panel.add rotLabel panel.add minLabel panel.add barLabel self.setDefaultCloseOperation JFrame::EXIT_ON_CLOSE self.setSize 350, 300 self.setLocationRelativeTo nil self.setVisible true end end Example.new In this example, we show three images using absolute positioning. panel.setLayout nil Containers in Swing already have a default layout manager. JPanel has a FlowLayout manager as its default layout manager. We use the setLayout method with a nil parameter to remove the default layout manager and use absolute positioning instead. rot = ImageIcon.new "rotunda.jpg" rotLabel = JLabel.new rot rotLabel.setBounds 20, 20, rot.getIconWidth, rot.getIconHeight We create an ImageIcon object. We put the icon into the JLabel component to display it. Then we use the setBounds method to position the label on the panel. The first two parameters are the x, y positions of the label. The 3th and 4th parameters are the width and the height of the icon. panel.add rotLabel We add the label to the panel container. Figure: Absolute positioning Buttons example In the following example, we will position two buttons in the bottom right corner of the window. #!/usr/local/bin/jruby # # # # # # # # # ZetCode JRuby Swing tutorial In this program, we use the BoxLayout manager to position two buttons in the bottom right corner of the window. author: Jan Bodnar website: www.zetcode.com last modified: December 2010 include Java import import import import java.awt.Dimension javax.swing.JButton javax.swing.JPanel javax.swing.JFrame import javax.swing.BoxLayout import javax.swing.Box class Example < JFrame def initialize super "Buttons" self.initUI end def initUI basic = JPanel.new basic.setLayout BoxLayout.new basic, BoxLayout::Y_AXIS self.add basic basic.add Box.createVerticalGlue bottom = JPanel.new bottom.setLayout BoxLayout.new bottom, BoxLayout::X_AXIS bottom.setAlignmentX 1.0 okButton = JButton.new "OK" closeButton = JButton.new "Close" bottom.add bottom.add bottom.add bottom.add okButton Box.createRigidArea Dimension.new 5, 0 closeButton Box.createRigidArea Dimension.new 15, 0 basic.add bottom basic.add Box.createRigidArea Dimension.new 0, 15 self.setDefaultCloseOperation JFrame::EXIT_ON_CLOSE self.setSize 300, 200 self.setLocationRelativeTo nil self.setVisible true end end Example.new We will create two panels. The basic panel has a vertical box layout. The bottom panel has a horizontal one. We will put a bottom panel into the basic panel. We will right align the bottom panel. The space between the top of the window and the bottom panel is expandable. It is done by the vertical glue. basic = JPanel.new basic.setLayout BoxLayout.new basic, BoxLayout::Y_AXIS ... bottom = JPanel.new bottom.setLayout BoxLayout.new bottom, BoxLayout::X_AXIS The basic panel has a vertical box layout. The bottom panel has a horizontal box layout. bottom.setAlignmentX 1.0 The bottom panel is right aligned. basic.add Box.createVerticalGlue We create a vertical glue. The glue is vertically expandable white space, which will push the horizontal box with the buttons to the bottom. okButton = JButton.new "OK" closeButton = JButton.new "Close" These are the two buttons, that will go into the bottom right corner of the window. bottom.add okButton bottom.add Box.createRigidArea Dimension.new 5, 0 We put the OK button into the horizontal box. We put some rigid space next to the button. So that there is some space between the two buttons. basic.add Box.createRigidArea Dimension.new 0, 15 We put some space between the buttons and the border of the window. Figure: Buttons example Windows example The following example creates the windows dialog using the GroupLayout manager. The dialog comes from the JDeveloper application. The GroupLayout manager divides the creation of the layout into two steps. In one step, we lay out components alongside the horizontal axis. In the second step, we lay out components along the vertical axis. This is an unusual idea within layout managers, but it works well. There are two types of arrangements. Sequential and parallel. In both kinds of layouts we can arrange components sequentially or in parallel. In a horizontal layout, a row of components is called a sequential group. A column of components is called a parallel group. In a vertical layout, a column of components is called a sequential group. And a row of components is called a parallel group. You must understand these definitions right in order to work with the GroupLayout manager. #!/usr/local/bin/jruby # # # # # # # # # ZetCode JRuby Swing tutorial In this program, GroupLayout manager to create a Windows example. author: Jan Bodnar website: www.zetcode.com last modified: December 2010 include Java import import import import import import import import import java.awt.Dimension java.awt.Color javax.swing.JButton javax.swing.SwingConstants javax.swing.JFrame javax.swing.JLabel javax.swing.JTextArea javax.swing.BorderFactory javax.swing.GroupLayout class Example < JFrame def initialize super "Windows" self.initUI end def initUI layout = GroupLayout.new self.getContentPane self.getContentPane.setLayout layout layout.setAutoCreateGaps true layout.setAutoCreateContainerGaps true self.setPreferredSize Dimension.new 350, 300 windows = JLabel.new "Windows" area = JTextArea.new area.setEditable false area.setBorder BorderFactory.createLineBorder Color.gray activateButton = JButton.new "Activate" closeButton = JButton.new "Close" helpButton = JButton.new "Help" okButton = JButton.new "OK" sg = layout.createSequentialGroup pg1 = layout.createParallelGroup pg2 = layout.createParallelGroup pg1.addComponent windows pg1.addComponent area pg1.addComponent helpButton sg.addGroup pg1 pg2.addComponent activateButton pg2.addComponent closeButton pg2.addComponent okButton sg.addGroup pg2 layout.setHorizontalGroup sg sg1 = layout.createSequentialGroup sg2 = layout.createSequentialGroup pg1 = layout.createParallelGroup pg2 = layout.createParallelGroup sg1.addComponent windows pg1.addComponent area sg2.addComponent activateButton sg2.addComponent closeButton pg1.addGroup sg2 sg1.addGroup pg1 pg2.addComponent helpButton pg2.addComponent okButton sg1.addGroup pg2 layout.setVerticalGroup sg1 layout.linkSize SwingConstants::HORIZONTAL, okButton, helpButton, closeButton, activateButton self.pack self.setDefaultCloseOperation JFrame::EXIT_ON_CLOSE self.setLocationRelativeTo nil self.setVisible true end end Example.new We use GroupLayout manager to create a layout which consists of six components. Groups of components are formed along both axes. sg = layout.createSequentialGroup pg1 = layout.createParallelGroup pg2 = layout.createParallelGroup pg1.addComponent windows pg1.addComponent area pg1.addComponent helpButton sg.addGroup pg1 pg2.addComponent activateButton pg2.addComponent closeButton pg2.addComponent okButton sg.addGroup pg2 layout.setHorizontalGroup sg In the first step, we have a horizontal layout. It consists of two parallel groups of three components. sg1 = layout.createSequentialGroup sg2 = layout.createSequentialGroup pg1 = layout.createParallelGroup pg2 = layout.createParallelGroup sg1.addComponent windows pg1.addComponent area sg2.addComponent activateButton sg2.addComponent closeButton pg1.addGroup sg2 sg1.addGroup pg1 pg2.addComponent helpButton pg2.addComponent okButton sg1.addGroup pg2 layout.setVerticalGroup sg1 Vertical layout is a bit more complex. First, we add a single component. Then we add a parallel group of a single component and a sequential group of two components. Finally, we add a parallel group of two components. layout.linkSize SwingConstants::HORIZONTAL, okButton, helpButton, closeButton, activateButton This code makes all buttons the same size. We only need to set their width, because their height is already the same by default. Figure: Windows example Basic shapes First we draw some basic Java 2D shapes. BasicShapes.java package com.zetcode; import import import import import java.awt.Color; java.awt.Graphics; java.awt.Graphics2D; java.awt.RenderingHints; java.awt.geom.Ellipse2D; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.SwingUtilities; class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.setColor(new Color(150, 150, 150)); RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g2d.setRenderingHints(rh); g2d.fillRect(30, 20, 50, 50); g2d.fillRect(120, 20, 90, 60); g2d.fillRoundRect(250, 20, 70, 60, 25, 25); g2d.fill(new Ellipse2D.Double(10, 100, 80, 100)); g2d.fillArc(120, 130, 110, 100, 5, 150); g2d.fillOval(270, 130, 50, 50); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } } public class BasicShapes extends JFrame { public BasicShapes() { initUI(); } private void initUI() { setTitle("Basic Shapes"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); add(new Surface()); setSize(350, 250); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { BasicShapes bs = new BasicShapes(); bs.setVisible(true); } }); } } In this example, we draw six basic shapes on the panel. A square, a rectangle, a rounded rectangle, an ellipse, an arc and a circle. g2d.setColor(new Color(150, 150, 150)); The shapes will be drawn in a gray background. g2d.fillRect(20, 20, 50, 50); g2d.fillRect(120, 20, 90, 60); The fillRect() method is used to draw both a rectangle and a square. The first two parameters are x, y coordinates of a shape to be drawn. The last two parameters are the width and the height of the shape. g2d.fillRoundRect(250, 20, 70, 60, 25, 25); Here we create a rounded rectangle. The last two parameters are the horizontal and vertical diameters of the arc at the four corners. Figure: Basic shapes General path More complex shapes can be constructed with a GeneralPath class. It represents a geometric path constructed from straight lines, and quadratic and cubic Bézier curves. In the next example, we will create a star with this class. Star.java package com.zetcode; import import import import import import import java.awt.Graphics; java.awt.Graphics2D; java.awt.RenderingHints; java.awt.geom.GeneralPath; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities; class Surface extends JPanel { private double points[][] = { { 0, 85 }, { 75, 75 }, { 100, 10 }, { 125, 75 }, { 200, 85 }, { 150, 125 }, { 160, 190 }, { 100, 150 }, { 40, 190 }, { 50, 125 }, { 0, 85 } }; private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D)g; g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g2d.translate(25, 5); GeneralPath star = new GeneralPath(); star.moveTo(points[0][0], points[0][1]); for (int k = 1; k < points.length; k++) star.lineTo(points[k][0], points[k][1]); star.closePath(); g2d.fill(star); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } } public class Star extends JFrame { public Star() { initUI(); } private void initUI() { setTitle("Star"); add(new Surface()); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(350, 250); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Star sr = new Star(); sr.setVisible(true); } }); } } We will create a star from a series of points. private double points[][] = { { 0, 85 }, { 75, 75 }, { 100, 10 }, { 125, 75 }, { 200, 85 }, { 150, 125 }, { 160, 190 }, { 100, 150 }, { 40, 190 }, { 50, 125 }, { 0, 85 } }; These are the coordinates of the star. GeneralPath star = new GeneralPath(); Here we instantiate the GeneralPath class. star.moveTo(points[0][0], points[0][1]); We move to the initial coordinate of the GeneralPath. for (int k = 1; k < points.length; k++) star.lineTo(points[k][0], points[k][1]); Here we connect all the coordinates of the star. star.closePath(); g2d.fill(star); We close the path and fill the interior of the star. Figure: Star Colors The Color class is used to work with colors in Java 2D. To fill rectangles with the current color, we use the fillRect() method. Colors.java package com.zetcode; import import import import import import java.awt.Color; java.awt.Graphics; java.awt.Graphics2D; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities; class Surface extends JPanel { public void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.setColor(new Color(125, 167, 116)); g2d.fillRect(10, 10, 90, 60); g2d.setColor(new Color(42, 179, 231)); g2d.fillRect(130, 10, 90, 60); g2d.setColor(new Color(70, 67, 123)); g2d.fillRect(250, 10, 90, 60); g2d.setColor(new Color(130, 100, 84)); g2d.fillRect(10, 100, 90, 60); g2d.setColor(new Color(252, 211, 61)); g2d.fillRect(130, 100, 90, 60); g2d.setColor(new Color(241, 98, 69)); g2d.fillRect(250, 100, 90, 60); g2d.setColor(new Color(217, 146, 54)); g2d.fillRect(10, 190, 90, 60); g2d.setColor(new Color(63, 121, 186)); g2d.fillRect(130, 190, 90, 60); g2d.setColor(new Color(31, 21, 1)); g2d.fillRect(250, 190, 90, 60); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } } public class Colors extends JFrame { public Colors() { initUI(); } private void initUI() { setTitle("Colors"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); add(new Surface()); setSize(360, 300); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Colors col = new Colors(); col.setVisible(true); } }); } } In the example we draw nine colored rectangles. g2d.setColor(new Color(125, 167, 116)); A new color is created with the Color class. The parameters of the constructor are the red, green and blue parts of the new color. The setColor() method sets the graphics context's current color to the specified color. All subsequent graphics operations using this graphics context use this specified color. g2d.fillRect(10, 15, 90, 60); To fill the rectangle with a color, we use the fillRect() method. Figure: Colors Gradients In computer graphics, gradient is a smooth blending of shades from light to dark or from one color to another. In 2D drawing programs and paint programs, gradients are used to create colorful backgrounds and special effects as well as to simulate lights and shadows. (answers.com) Gradients.java package com.zetcode; import import import import import import import java.awt.Color; java.awt.GradientPaint; java.awt.Graphics; java.awt.Graphics2D; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities; class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; GradientPaint gp1 = new GradientPaint(5, 5, Color.red, 20, 20, Color.black, true); g2d.setPaint(gp1); g2d.fillRect(20, 20, 300, 40); GradientPaint gp2 = new GradientPaint(5, 25, Color.yellow, 20, 2, Color.black, true); g2d.setPaint(gp2); g2d.fillRect(20, 80, 300, 40); GradientPaint gp3 = new GradientPaint(5, 25, Color.green, 2, 2, Color.black, true); g2d.setPaint(gp3); g2d.fillRect(20, 140, 300, 40); GradientPaint gp4 = new GradientPaint(25, 25, Color.blue, 15, 25, Color.black, true); g2d.setPaint(gp4); g2d.fillRect(20, 200, 300, 40); GradientPaint gp5 = new GradientPaint(0, 0, Color.orange, 0, 20, Color.black, true); g2d.setPaint(gp5); g2d.fillRect(20, 260, 300, 40); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } } public class Gradients extends JFrame { public Gradients() { initUI(); } private void initUI() { setTitle("Gradients"); add(new Surface()); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(350, 350); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Gradients gr = new Gradients(); gr.setVisible(true); } }); } } Our code example presents five rectangles with gradients. GradientPaint gp4 = new GradientPaint(25, 25, Color.blue, 15, 25, Color.black, true); To work with gradients, we use the GradientPaint class. By manipulating the color values and the starting and ending points, we can get different results. g2d.setPaint(gp5); The gradient is activated calling the setPaint() method. Figure: Gradients Textures A texture is a bitmap image applied to a shape. To work with textures in Java 2D, we use the TexturePaint class. Textures.java package com.zetcode; import import import import import import import import import import import java.awt.Graphics; java.awt.Graphics2D; java.awt.Rectangle; java.awt.TexturePaint; java.awt.image.BufferedImage; java.io.File; java.io.IOException; java.util.logging.Level; java.util.logging.Logger; javax.imageio.ImageIO; javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.SwingUtilities; class Surface extends JPanel { private private private private private private BufferedImage slate; BufferedImage java; BufferedImage pane; TexturePaint slatetp; TexturePaint javatp; TexturePaint panetp; public Surface() { loadImages(); } private void loadImages() { try { slate = ImageIO.read(new File("slate.png")); java = ImageIO.read(new File("java.png")); pane = ImageIO.read(new File("pane.png")); } catch (IOException ex) { Logger.getLogger(Surface.class.getName()).log(Level.SEVERE, null, ex); } } private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; slatetp = new TexturePaint(slate, new Rectangle(0, 0, 90, 60)); javatp = new TexturePaint(java, new Rectangle(0, 0, 90, 60)); panetp = new TexturePaint(pane, new Rectangle(0, 0, 90, 60)); g2d.setPaint(slatetp); g2d.fillRect(10, 15, 90, 60); g2d.setPaint(javatp); g2d.fillRect(130, 15, 90, 60); g2d.setPaint(panetp); g2d.fillRect(250, 15, 90, 60); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } } public class Textures extends JFrame { public Textures() { initUI(); } private void initUI() { setTitle("Textures"); add(new Surface()); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(360, 120); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Textures tx = new Textures(); tx.setVisible(true); } }); } } In the code example, we fill three rectangles with three different textures. slate = ImageIO.read(new File("slate.png")); Using the ImageIO class, we read the image into the buffered image. slatetp = new TexturePaint(slate, new Rectangle(0, 0, 90, 60)); We create a TexturePaint class out of the buffered image. g2d.setPaint(slatetp); g2d.fillRect(10, 15, 90, 60); We fill a rectangle with a texture. Figure: Textures In this part of the Java 2D tutorial, we have covered some basic and more advanced shapes of the Java 2D library. Transparency explained Transparency is the quality of being able to see through a material. The easiest way to understand transparency is to imagine a piece of glass or water. Technically, the rays of light can go through the glass and this way we can see objects behind the glass. In computer graphics, we can achieve transparency effects using alpha compositing. Alpha compositing is the process of combining an image with a background to create the appearance of partial transparency. The composition process uses an alpha channel. Alpha channel is an 8-bit layer in a graphics file format that is used for expressing translucency (transparency). The extra eight bits per pixel serves as a mask and represents 256 levels of translucency. (answers.com, wikipedia.org) The AlphaComposite class is used to work with transparency in Java 2D. It implements the basic alpha compositing rules for combining source and destination pixels to achieve blending and transparency effects with graphics and images. To create an AlphaComposite, you provide two values. The rule designator and the alpha value. The rule specifies how we combine source and destination pixels. Most often it is AlphaComposite.SRC_OVER. The alpha value can range from 0.0f (completely transparent) to 1.0f (completely opaque). Transparent rectangles The first example will draw ten rectangles with different levels of transparency. TransparentRectangles.java package com.zetcode; import import import import import import import java.awt.AlphaComposite; java.awt.Color; java.awt.Graphics; java.awt.Graphics2D; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities; class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.setColor(Color.BLUE); for (int i = 1; i <= 10; i++) { g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, i * 0.1f)); g2d.fillRect(50 * i, 20, 40, 40); } } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } } public class TransparentRectangles extends JFrame { public TransparentRectangles() { initUI(); } private void initUI() { setTitle("Transparent rectangles"); add(new Surface()); setSize(590, 120); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { TransparentRectangles tr = new TransparentRectangles(); tr.setVisible(true); } }); } } In our example we draw 10 blue rectangles with various levels of transparency applied. g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, i * 0.1f)); This is the key line of the example. We use the forementioned AlphaComposite.SRC_OVER rule. The alpha value dynamically changes in the for loop. Figure: Transparent rectangles Fade out demo In the next example, we will fade out an image. The image will gradually get more transparent until it is completely invisible. FadeOut.java package com.zetcode; import import import import import import import import import import import import java.awt.AlphaComposite; java.awt.Dimension; java.awt.Graphics; java.awt.Graphics2D; java.awt.Image; java.awt.event.ActionEvent; java.awt.event.ActionListener; javax.swing.ImageIcon; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities; javax.swing.Timer; class Surface extends JPanel implements ActionListener { private Image img; private Timer timer; private float alpha = 1f; public Surface() { loadImage(); setSurfaceSize(); initTimer(); } private void loadImage() { img = new ImageIcon("mushrooms.jpg").getImage(); } private void setSurfaceSize() { int h = img.getHeight(this); int w = img.getWidth(this); setPreferredSize(new Dimension(w, h)); } private void initTimer() { timer = new Timer(20, this); timer.start(); } private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha)); g2d.drawImage(img, 0, 0, null); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } @Override public void actionPerformed(ActionEvent e) { alpha += -0.01f; if (alpha <= 0) { alpha = 0; timer.stop(); } repaint(); } } public class FadeOut extends JFrame { public FadeOut() { initUI(); } private void initUI() { setTitle("Fade out"); add(new Surface()); pack(); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { FadeOut fo = new FadeOut(); fo.setVisible(true); } }); } } With the AlphaComposite we gradually fade out the image on the panel. private void setSurfaceSize() { int h = img.getHeight(this); int w = img.getWidth(this); setPreferredSize(new Dimension(w, h)); } The setSurfaceSize() method finds out the size of the image and sets a preferred size for the panel. The preferred size with the combination of the pack() method will display the whole image on the window. private void initTimer() { timer = new Timer(20, this); timer.start(); } The initTimer() method starts the timer. The timer fires action events after a specified delay. In reaction to an action event, we will change the alpha value and repaint the panel. g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha)); g2d.drawImage(castle, 0, 0, null); These two methods draw an image with increasing levels of transparency on the panel. alpha += -0.01f; In the actionPerformed() method we gradually decrease the alpha value. Note that the alpha value must not be negative. repaint(); The method repaints the component. It calls the paint() method of this component (JPanel) as soon as possible. Waiting In this example, we use transparency effect to create a waiting demo. We will draw 8 lines that will gradually fade out creating an illusion, that a line is moving. Such effects are often used to inform users, that a lengthy task is going on behind the scenes. An example is streaming video over the Internet. Waiting.java package com.zetcode; import import import import import import import import import import import java.awt.AlphaComposite; java.awt.BasicStroke; java.awt.Graphics; java.awt.Graphics2D; java.awt.RenderingHints; java.awt.event.ActionEvent; java.awt.event.ActionListener; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities; javax.swing.Timer; class Surface extends JPanel implements ActionListener { private Timer timer; private int count; private final double[][] trs = { {0.0, 0.15, 0.30, 0.5, 0.65, 0.80, 0.9, 1.0}, {1.0, 0.0, 0.15, 0.30, 0.5, 0.65, 0.8, 0.9}, {0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65, 0.8}, {0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65}, {0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5}, {0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3}, {0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15}, {0.15, 0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0,} }; public Surface() { initTimer(); } private void initTimer() { timer = new Timer(80, this); timer.setInitialDelay(190); timer.start(); } private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); int width = getWidth(); int height = getHeight(); g2d.setStroke(new BasicStroke(3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); g2d.translate(width / 2, height / 2); for (int i = 0; i < 8; i++) { g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float) trs[count % 8][i])); g2d.rotate(Math.PI / 4f); g2d.drawLine(0, -10, 0, -40); } } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } @Override public void actionPerformed(ActionEvent e) { repaint(); count++; } } public class Waiting extends JFrame { public Waiting() { initUI(); } private void initUI() { setTitle("Waiting"); add(new Surface()); setSize(300, 200); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Waiting wt = new Waiting(); wt.setVisible(true); } }); } } We draw eight lines with eight different alpha values. private final double[][] trs = { ... }; This is a two dimensional array of transparency values used in this demo. There are 8 rows, each for one state. Each of the 8 lines will continuously use these values. g2d.setStroke(new BasicStroke(3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); We make the lines a bit thicker, so that they are better visible. We draw the lines with rounded caps. g2d.rotate(Math.PI/4f); g2d.drawLine(0, -10, 0, -40); This code will draw each of the eight lines. The rotate() method is used to rotate the lines alongside a circle. Figure: Waiting In this part of the Java 2D tutorial, we have talked about transparency Operations There are several compositing operations. We will show some of them in the next code example. The AlphaComposite class implements basic alpha compositing rules for combining source and destination colors to achieve blending and transparency effects with graphics and images. Say we want to draw two objects on a panel. The first object drawn is called a destination. The second one a source. The AlphaComposite class determines how these two are going to be blended together. If we have a AlphaComposite.SRC_OVER rule, the pixels of the source object will be drawn, where the two objects overlap. Compositing.java package com.zetcode; import import import import import import import import java.awt.AlphaComposite; java.awt.Color; java.awt.Graphics; java.awt.Graphics2D; java.awt.image.BufferedImage; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities; class Surface extends JPanel { private int rules[] = { AlphaComposite.DST, AlphaComposite.DST_ATOP, AlphaComposite.DST_OUT, AlphaComposite.SRC, AlphaComposite.SRC_ATOP, AlphaComposite.SRC_OUT }; private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; for (int x = 20, y = 20, i = 0; i < rules.length; x += 60, i++) { AlphaComposite ac = AlphaComposite.getInstance(rules[i], 0.8f); BufferedImage buffImg = new BufferedImage(60, 60, BufferedImage.TYPE_INT_ARGB); Graphics2D gbi = buffImg.createGraphics(); gbi.setPaint(Color.blue); gbi.fillRect(0, 0, 40, 40); gbi.setComposite(ac); gbi.setPaint(Color.green); gbi.fillRect(5, 5, 40, 40); g2d.drawImage(buffImg, x, y, null); } } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } } public class Composition extends JFrame { public Composition() { setTitle("Composition"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); add(new Surface()); setSize(400, 120); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Composition cm = new Composition(); cm.setVisible(true); } }); } } We draw two rectangles and combine them with six different compositing operations. private int rules[] = { AlphaComposite.DST, AlphaComposite.DST_ATOP, AlphaComposite.DST_OUT, AlphaComposite.SRC, AlphaComposite.SRC_ATOP, AlphaComposite.SRC_OUT }; Here we have six different compositing rules. Only two of these have practical significance. If not sure what compositing rule to use, pick up the SRC_OVER rule. AlphaComposite ac = AlphaComposite.getInstance(rules[i]); Here we get the AlphaComposite class. BufferedImage buffImg = new BufferedImage(60, 60, BufferedImage.TYPE_INT_ARGB); Graphics2D gbi = buffImg.createGraphics(); We use a buffer image to perform the compositing operations. gbi.setComposite(ac); Sets the Composite for the Graphics2D context. g2d.drawImage(buffImg, x, y, null); We draw the image on the panel. Figure: Composition Sun and Cloud In the next example we show the Sun coming from behind a cloud. We will use composition technique in this animation. SunAndCloud.java package com.zetcode; import import import import import import import import import import import import java.awt.AlphaComposite; java.awt.Graphics; java.awt.Graphics2D; java.awt.Image; java.awt.event.ActionEvent; java.awt.event.ActionListener; java.awt.image.BufferedImage; javax.swing.ImageIcon; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities; javax.swing.Timer; class Surface extends JPanel implements ActionListener { private private private private Image Image Timer float sun; cloud; timer; alpha; public Surface() { loadImages(); initTimer(); } private void loadImages() { sun = new ImageIcon("sun.png").getImage(); cloud = new ImageIcon("cloud.png").getImage(); } private void initTimer() { timer = new Timer(800, this); timer.start(); alpha = 1f; } private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; BufferedImage buffImg = new BufferedImage(220, 140, BufferedImage.TYPE_INT_ARGB); Graphics2D gbi = buffImg.createGraphics(); AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha); gbi.drawImage(sun, 40, 30, null); gbi.setComposite(ac); gbi.drawImage(cloud, 0, 0, null); g2d.drawImage(buffImg, 20, 20, null); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } @Override public void actionPerformed(ActionEvent e) { alpha -= 0.1; if (alpha <= 0) { alpha = 0; timer.stop(); System.out.println("Timer stopped."); } repaint(); } } public class SunAndCloud extends JFrame { public SunAndCloud() { initUI(); } private void initUI() { setTitle("Sun and Cloud"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); add(new Surface()); setSize(300, 210); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { SunAndCloud sac = new SunAndCloud(); sac.setVisible(true); } }); } } The Sun comes from behind the cloud. The cloud finally disappears. private void loadImages() { sun = new ImageIcon("sun.png").getImage(); cloud = new ImageIcon("cloud.png").getImage(); } We load two images from the disk. private void initTimer() { timer = new Timer(800, this); timer.start(); alpha = 1f; } Inside the initTimer() method the timer is activated. AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha); We use the AlphaComposite.SRC_OVER rule. Here the source blends with destination and overwrites empty pixels. gbi.drawImage(sun, 40, 30, null); gbi.setComposite(ac); gbi.drawImage(cloud, 0, 0, null); g2d.drawImage(buffImg, 20, 20, null); The images are rendered into a BufferedImage and are later copied to the screen. The setComposite() specifies how new pixels are to be combined with the existing pixels on the graphics device during the rendering process. Figure: Sun & Cloud In this part of the Java 2D tutorial, we have talked about image composition. Translation The following example describes a simple translation. Translation.java package com.zetcode; import import import import import import java.awt.Color; java.awt.Graphics; java.awt.Graphics2D; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities; class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.setColor(new Color(150, 150, 150)); g2d.fillRect(20, 20, 80, 50); g2d.translate(150, 50); g2d.fillRect(20, 20, 80, 50); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } } public class Translation extends JFrame { public Translation() { initUI(); } private void initUI() { setTitle("Translation"); add(new Surface()); setSize(300, 200); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Translation trl = new Translation(); trl.setVisible(true); } }); } } The example draws a rectangle. Then we do a translation and draw the same rectangle again. g2d.translate(150, 50); This line moves the origin of the Graphics2D context to a new point. Figure: Translation Rotation The next example demonstrates a rotation. Rotation.java package com.zetcode; import import import import import import java.awt.Color; java.awt.Graphics; java.awt.Graphics2D; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities; class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.setColor(new Color(150, 150, 150)); g2d.fillRect(20, 20, 80, 50); g2d.translate(180, -50); g2d.rotate(Math.PI/4); g2d.fillRect(80, 80, 80, 50); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } } public class Rotation extends JFrame { public Rotation() { initUI(); } private void initUI() { setTitle("Rotation"); add(new Surface()); setSize(300, 200); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Rotation rt = new Rotation(); rt.setVisible(true); } }); } } The example draws a rectangle, performs a translation and a rotation and draws the same rectangle again. g2d.rotate(Math.PI/4); The rotate() method performs rotation. Note that the rotation parameter is in radians. Figure: Rotation Scaling The next example demonstrates scaling of an object. Scaling is done with the scale() method. In this method, we provide two parameters. They are the x scale and y scale factor, by which coordinates are scaled along the x or y axis respectively. Scaling.java package com.zetcode; import import import import import import import java.awt.Color; java.awt.Graphics; java.awt.Graphics2D; java.awt.geom.AffineTransform; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities; class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.setColor(new Color(150, 150, 150)); g2d.fillRect(20, 20, 80, 50); AffineTransform tx1 = new AffineTransform(); tx1.translate(110, 22); tx1.scale(0.5, 0.5); g2d.setTransform(tx1); g2d.fillRect(0, 0, 80, 50); AffineTransform tx2 = new AffineTransform(); tx2.translate(170, 20); tx2.scale(1.5, 1.5); g2d.setTransform(tx2); g2d.fillRect(0, 0, 80, 50); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } } public class Scaling extends JFrame { public Scaling() { initUI(); } private void initUI() { setTitle("Scaling"); add(new Surface()); setSize(330, 160); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Scaling sc = new Scaling(); sc.setVisible(true); } }); } } We have a rectangle. First we scale it down and then we scale it up a bit. AffineTransform tx2 = new AffineTransform(); tx2.translate(170, 20); tx2.scale(1.5, 1.5); Another scaling would be added to the first one. So we need to create and apply a new affine transform. Figure: Scaling Shearing In the following example we perform shearing. We use the share() method. Scale.java package com.zetcode; import import import import import import import import java.awt.Color; java.awt.Graphics; java.awt.Graphics2D; java.awt.Rectangle; java.awt.geom.AffineTransform; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities; class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; AffineTransform tx1 = new AffineTransform(); tx1.translate(50, 90); g2d.setTransform(tx1); g2d.setColor(Color.green); g2d.drawRect(0, 0, 160, 50); AffineTransform tx2 = new AffineTransform(); tx2.translate(50, 90); tx2.shear(0, 1); g2d.setTransform(tx2); g2d.setColor(Color.blue); g2d.draw(new Rectangle(0, 0, 80, 50)); AffineTransform tx3 = new AffineTransform(); tx3.translate(130, 10); tx3.shear(0, 1); g2d.setTransform(tx3); g2d.setColor(Color.red); g2d.drawRect(0, 0, 80, 50); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } } public class Shearing extends JFrame { public Shearing() { initUI(); } private void initUI() { setTitle("Shearing"); add(new Surface()); setSize(330, 270); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Shearing sh = new Shearing(); sh.setVisible(true); } }); } } In this example, we draw three rectangles in three different colors. They form a structure. Two of them are sheared. tx2.shear(0, 1); The two parameters are multipliers by which coordinates are shifted in the direction of the x and y axis. Figure: Shearing Donut In the following example we create an complex shape by rotating an ellipse. Donut.java package com.zetcode; import import import import import import import import import import import java.awt.BasicStroke; java.awt.Color; java.awt.Dimension; java.awt.Graphics; java.awt.Graphics2D; java.awt.RenderingHints; java.awt.geom.AffineTransform; java.awt.geom.Ellipse2D; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities; class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2 = (Graphics2D) g; RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g2.setRenderingHints(rh); Dimension size = getSize(); double w = size.getWidth(); double h = size.getHeight(); Ellipse2D e = new Ellipse2D.Double(0, 0, 80, 130); g2.setStroke(new BasicStroke(1)); g2.setColor(Color.gray); for (double deg = 0; deg < 360; deg += 5) { AffineTransform at = AffineTransform.getTranslateInstance(w / 2, h / 2); at.rotate(Math.toRadians(deg)); g2.draw(at.createTransformedShape(e)); } } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } } public class Donut extends JFrame { public Donut() { initUI(); } private void initUI() { setTitle("Donut"); add(new Surface()); setSize(370, 320); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Donut dn = new Donut(); dn.setVisible(true); } }); } } In this example, we create a donut shape. Ellipse2D e = new Ellipse2D.Double(0, 0, 80, 130); g2.setStroke(new BasicStroke(1)); g2.setColor(Color.gray); In the beginning there was an ellipse. for (double deg = 0; deg < 360; deg += 5) { AffineTransform at = AffineTransform.getTranslateInstance(w / 2, h / 2); at.rotate(Math.toRadians(deg)); g2.draw(at.createTransformedShape(e)); } After several rotations, there is a donut. In this part of the Java 2D tutorial, we have talked about transformations Tetris The Tetris game is one of the most popular computer games ever created. The original game was designed and programmed by a Russian programmer Alexey Pajitnov in 1985. Since then, Tetris is available on almost every computer platform in lots of variations. Even my mobile phone has a modified version of the Tetris game. Tetris is called a falling block puzzle game. In this game, we have seven different shapes called tetrominoes. S-shape, Z-shape, T-shape, L-shape, Line-shape, MirroredL-shape and a Square-shape. Each of these shapes is formed with four squares. The shapes are falling down the board. The object of the Tetris game is to move and rotate the shapes, so that they fit as much as possible. If we manage to form a row, the row is destroyed and we score. We play the Tetris game until we top out. Figure: Tetrominoes The development We do not have images for our Tetris game, we draw the tetrominoes using Swing drawing API. Behind every computer game, there is a mathematical model. So it is in Tetris. Some ideas behind the game. We use a Timer class to create a game cycle The tetrominoes are drawn The shapes move on a square by square basis (not pixel by pixel) Mathematically a board is a simple list of numbers I have simplified the game a bit, so that it is easier to understand. The game starts immediately, after it is launched. We can pause the game by pressing the p key. The space key will drop the Tetris piece immediately to the bottom. The d key will drop the piece one line down. (It can be used to speed up the falling a bit.) The game goes at constant speed, no acceleration is implemented. The score is the number of lines, that we have removed. Tetris.java package com.zetcode; import java.awt.BorderLayout; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.SwingUtilities; public class Tetris extends JFrame { private JLabel statusbar; public Tetris() { initUI(); } private void initUI() { statusbar = new JLabel(" 0"); add(statusbar, BorderLayout.SOUTH); Board board = new Board(this); add(board); board.start(); setSize(200, 400); setTitle("Tetris"); setDefaultCloseOperation(EXIT_ON_CLOSE); setLocationRelativeTo(null); } public JLabel getStatusBar() { return statusbar; } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Tetris game = new Tetris(); game.setVisible(true); } }); } } In the Tetris.java file, we set up the game. We create a board on which we play the game. We create a statusbar. board.start(); The start() method starts the Tetris game. Immediately after the window appears on the screen. Shape.java package com.zetcode; import java.util.Random; public class Shape { protected enum Tetrominoes { NoShape, ZShape, SShape, LineShape, TShape, SquareShape, LShape, MirroredLShape }; private Tetrominoes pieceShape; private int coords[][]; private int[][][] coordsTable; public Shape() { coords = new int[4][2]; setShape(Tetrominoes.NoShape); } public void setShape(Tetrominoes shape) { coordsTable = new int[][][] { { { 0, 0 }, { 0, 0 }, { 0, 0 }, { { 0, -1 }, { 0, 0 }, { -1, 0 }, { { 0, -1 }, { 0, 0 }, { 1, 0 }, { { 0, -1 }, { 0, 0 }, { 0, 1 }, { { -1, 0 }, { 0, 0 }, { 1, 0 }, { { 0, 0 }, { 1, 0 }, { 0, 1 }, { { -1, -1 }, { 0, -1 }, { 0, 0 }, { { 1, -1 }, { 0, -1 }, { 0, 0 }, }; { { { { { { { { 0, 0 } }, -1, 1 } }, 1, 1 } }, 0, 2 } }, 0, 1 } }, 1, 1 } }, 0, 1 } }, 0, 1 } } for (int i = 0; i < 4 ; i++) { for (int j = 0; j < 2; ++j) { coords[i][j] = coordsTable[shape.ordinal()][i][j]; } } pieceShape = shape; } private void setX(int index, int private void setY(int index, int public int x(int index) { return public int y(int index) { return public Tetrominoes getShape() { x) { coords[index][0] = x; } y) { coords[index][1] = y; } coords[index][0]; } coords[index][1]; } return pieceShape; } public void setRandomShape() { Random r = new Random(); int x = Math.abs(r.nextInt()) % 7 + 1; Tetrominoes[] values = Tetrominoes.values(); setShape(values[x]); } public int minX() { int m = coords[0][0]; for (int i=0; i < 4; i++) { m = Math.min(m, coords[i][0]); } return m; } public int minY() { int m = coords[0][1]; for (int i=0; i < 4; i++) { m = Math.min(m, coords[i][1]); } return m; } public Shape rotateLeft() { if (pieceShape == Tetrominoes.SquareShape) return this; Shape result = new Shape(); result.pieceShape = pieceShape; for (int i = 0; i < 4; ++i) { result.setX(i, y(i)); result.setY(i, -x(i)); } return result; } public Shape rotateRight() { if (pieceShape == Tetrominoes.SquareShape) return this; Shape result = new Shape(); result.pieceShape = pieceShape; for (int i = 0; i < 4; ++i) { result.setX(i, -y(i)); result.setY(i, x(i)); } return result; } } The Shape class provides information about a Tetris piece. protected enum Tetrominoes { NoShape, ZShape, SShape, LineShape, TShape, SquareShape, LShape, MirroredLShape }; The Tetrominoes enum holds all seven Tetris shapes. Plus the empty shape called here NoShape. public Shape() { coords = new int[4][2]; setShape(Tetrominoes.NoShape); } This is the constructor of the Shape class. The coords array holds the actual coordinates of a Tetris piece. coordsTable = new int[][][] { { { 0, 0 }, { 0, 0 }, { 0, 0 }, { { 0, -1 }, { 0, 0 }, { -1, 0 }, { { 0, -1 }, { 0, 0 }, { 1, 0 }, { { 0, -1 }, { 0, 0 }, { 0, 1 }, { { -1, 0 }, { 0, 0 }, { 1, 0 }, { { 0, 0 }, { 1, 0 }, { 0, 1 }, { { -1, -1 }, { 0, -1 }, { 0, 0 }, { { 1, -1 }, { 0, -1 }, { 0, 0 }, }; { { { { { { { { 0, 0 } }, -1, 1 } }, 1, 1 } }, 0, 2 } }, 0, 1 } }, 1, 1 } }, 0, 1 } }, 0, 1 } } The coordsTable array holds all possible coordinate values of our Tetris pieces. This is a template from which all pieces take their coordinate values. for (int i = 0; i < 4 ; i++) { for (int j = 0; j < 2; ++j) { coords[i][j] = coordsTable[shape.ordinal()][i][j]; } } Here we put one row of the coordinate values from the coordsTable to a coords array of a Tetris piece. Note the use of the ordinal() method. In C++, an enum type is esencially an integer. Unlike in C++, Java enums are full classes. And the ordinal() method returns the current position of the enum type in the enum object. The following image will help understand the coordinate values a bit more. The coords array saves the coordinates of the Tetris piece. For example, numbers { 0, -1 }, { 0, 0 }, { -1, 0 }, { -1, -1 } , represent a rotated S-shape. The following diagram illustrates the shape. Figure: Coordinates public Shape rotateLeft() { if (pieceShape == Tetrominoes.SquareShape) return this; Shape result = new Shape(); result.pieceShape = pieceShape; for (int i = 0; i < 4; ++i) { result.setX(i, y(i)); result.setY(i, -x(i)); } return result; } This code rotates the piece to the left. The square does not have to be rotated. That's why we simply return the reference to the current object. Looking at the previous image will help to understand the rotation. Board.java package com.zetcode; import import import import import import import import import import java.awt.Color; java.awt.Dimension; java.awt.Graphics; java.awt.event.ActionEvent; java.awt.event.ActionListener; java.awt.event.KeyAdapter; java.awt.event.KeyEvent; javax.swing.JLabel; javax.swing.JPanel; javax.swing.Timer; import com.zetcode.Shape.Tetrominoes; public class Board extends JPanel implements ActionListener { private final int BoardWidth = 10; private final int BoardHeight = 22; private private private private private private private private private private Timer timer; boolean isFallingFinished = false; boolean isStarted = false; boolean isPaused = false; int numLinesRemoved = 0; int curX = 0; int curY = 0; JLabel statusbar; Shape curPiece; Tetrominoes[] board; public Board(Tetris parent) { initBoard(parent); } private void initBoard(Tetris parent) { setFocusable(true); curPiece = new Shape(); timer = new Timer(400, this); timer.start(); statusbar = parent.getStatusBar(); board = new Tetrominoes[BoardWidth * BoardHeight]; addKeyListener(new TAdapter()); clearBoard(); } @Override public void actionPerformed(ActionEvent e) { if (isFallingFinished) { isFallingFinished = false; newPiece(); } else { oneLineDown(); } } private int squareWidth() { return (int) getSize().getWidth() / BoardWidth; } private int squareHeight() { return (int) getSize().getHeight() / BoardHeight; } private Tetrominoes shapeAt(int x, int y) { return board[(y * BoardWidth) + x]; } public void start() { if (isPaused) return; isStarted = true; isFallingFinished = false; numLinesRemoved = 0; clearBoard(); newPiece(); timer.start(); } private void pause() { if (!isStarted) return; isPaused = !isPaused; if (isPaused) { timer.stop(); statusbar.setText("paused"); } else { timer.start(); statusbar.setText(String.valueOf(numLinesRemoved)); } repaint(); } private void doDrawing(Graphics g) { Dimension size = getSize(); int boardTop = (int) size.getHeight() - BoardHeight * squareHeight(); for (int i = 0; i < BoardHeight; ++i) { for (int j = 0; j < BoardWidth; ++j) { Tetrominoes shape = shapeAt(j, BoardHeight - i - 1); if (shape != Tetrominoes.NoShape) drawSquare(g, 0 + j * squareWidth(), boardTop + i * squareHeight(), shape); } } if (curPiece.getShape() != Tetrominoes.NoShape) { for (int i = 0; i < 4; ++i) { int x = curX + curPiece.x(i); int y = curY - curPiece.y(i); drawSquare(g, 0 + x * squareWidth(), boardTop + (BoardHeight - y - 1) * squareHeight(), curPiece.getShape()); } } } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } private void dropDown() { int newY = curY; while (newY > 0) { if (!tryMove(curPiece, curX, newY - 1)) break; --newY; } pieceDropped(); } private void oneLineDown() { if (!tryMove(curPiece, curX, curY - 1)) pieceDropped(); } private void clearBoard() { for (int i = 0; i < BoardHeight * BoardWidth; ++i) board[i] = Tetrominoes.NoShape; } private void pieceDropped() { for (int i = 0; i < 4; ++i) { int x = curX + curPiece.x(i); int y = curY - curPiece.y(i); board[(y * BoardWidth) + x] = curPiece.getShape(); } removeFullLines(); if (!isFallingFinished) newPiece(); } private void newPiece() { curPiece.setRandomShape(); curX = BoardWidth / 2 + 1; curY = BoardHeight - 1 + curPiece.minY(); if (!tryMove(curPiece, curX, curY)) { curPiece.setShape(Tetrominoes.NoShape); timer.stop(); isStarted = false; statusbar.setText("game over"); } } private boolean tryMove(Shape newPiece, int newX, int newY) { for (int i = 0; i < 4; ++i) { int x = newX + newPiece.x(i); int y = newY - newPiece.y(i); if (x < 0 || x >= BoardWidth || y < 0 || y >= BoardHeight) return false; if (shapeAt(x, y) != Tetrominoes.NoShape) return false; } curPiece = newPiece; curX = newX; curY = newY; repaint(); return true; } private void removeFullLines() { int numFullLines = 0; for (int i = BoardHeight - 1; i >= 0; --i) { boolean lineIsFull = true; for (int j = 0; j < BoardWidth; ++j) { if (shapeAt(j, i) == Tetrominoes.NoShape) { lineIsFull = false; break; } } if (lineIsFull) { ++numFullLines; for (int k = i; k < BoardHeight - 1; ++k) { for (int j = 0; j < BoardWidth; ++j) board[(k * BoardWidth) + j] = shapeAt(j, k + 1); } } } if (numFullLines > 0) { numLinesRemoved += numFullLines; statusbar.setText(String.valueOf(numLinesRemoved)); isFallingFinished = true; curPiece.setShape(Tetrominoes.NoShape); repaint(); } } private void drawSquare(Graphics g, int x, int y, Tetrominoes shape) { Color colors[] = { new Color(0, 0, 0), new Color(204, 102, 102), new Color(102, 204, 102), new Color(102, 102, 204), new Color(204, 204, 102), new Color(204, 102, 204), new Color(102, 204, 204), new Color(218, 170, 0) }; Color color = colors[shape.ordinal()]; g.setColor(color); g.fillRect(x + 1, y + 1, squareWidth() - 2, squareHeight() 2); g.setColor(color.brighter()); g.drawLine(x, y + squareHeight() - 1, x, y); g.drawLine(x, y, x + squareWidth() - 1, y); g.setColor(color.darker()); g.drawLine(x + 1, y + squareHeight() - 1, x + squareWidth() - 1, y + squareHeight() 1); g.drawLine(x + squareWidth() - 1, y + squareHeight() - 1, x + squareWidth() - 1, y + 1); } class TAdapter extends KeyAdapter { @Override public void keyPressed(KeyEvent e) { if (!isStarted || curPiece.getShape() == Tetrominoes.NoShape) { return; } int keycode = e.getKeyCode(); if (keycode == 'p' || keycode == 'P') { pause(); return; } if (isPaused) return; switch (keycode) { case KeyEvent.VK_LEFT: tryMove(curPiece, curX - 1, curY); break; case KeyEvent.VK_RIGHT: tryMove(curPiece, curX + 1, curY); break; case KeyEvent.VK_DOWN: tryMove(curPiece.rotateRight(), curX, curY); break; case KeyEvent.VK_UP: tryMove(curPiece.rotateLeft(), curX, curY); break; case KeyEvent.VK_SPACE: dropDown(); break; case 'd': oneLineDown(); break; case 'D': oneLineDown(); break; } } } } Finally, we have the Board.java file. This is where the game logic is located. ... private private private private private private ... boolean isFallingFinished = false; boolean isStarted = false; boolean isPaused = false; int numLinesRemoved = 0; int curX = 0; int curY = 0; We initialize some important variables. The isFallingFinished variable determines, if the Tetris shape has finished falling and we then need to create a new shape. The numLinesRemoved counts the number of lines, we have removed so far. The curX and curY variables determine the actual position of the falling Tetris shape. setFocusable(true); We must explicitly call the setFocusable() method. From now, the board has the keyboard input. timer = new Timer(400, this); timer.start(); Timer object fires one or more action events after a specified delay. In our case, the timer calls the actionPerformed() method each 400ms. @Override public void actionPerformed(ActionEvent e) { if (isFallingFinished) { isFallingFinished = false; newPiece(); } else { oneLineDown(); } } The actionPerformed() method checks if the falling has finished. If so, a new piece is created. If not, the falling Tetris piece goes one line down. Inside the doDrawing() method, we draw all objects on the board. The painting has two steps. for (int i = 0; i < BoardHeight; ++i) { for (int j = 0; j < BoardWidth; ++j) { Tetrominoes shape = shapeAt(j, BoardHeight - i - 1); if (shape != Tetrominoes.NoShape) drawSquare(g, 0 + j * squareWidth(), boardTop + i * squareHeight(), shape); } } In the first step we paint all the shapes, or remains of the shapes, that have been dropped to the bottom of the board. All the squares are remembered in the board array. We access it using the shapeAt() method. if (curPiece.getShape() != Tetrominoes.NoShape) { for (int i = 0; i < 4; ++i) { int x = curX + curPiece.x(i); int y = curY - curPiece.y(i); drawSquare(g, 0 + x * squareWidth(), boardTop + (BoardHeight - y - 1) * squareHeight(), curPiece.getShape()); } } In the second step, we paint the actual falling piece. private void dropDown() { int newY = curY; while (newY > 0) { if (!tryMove(curPiece, curX, newY - 1)) break; --newY; } pieceDropped(); } If we press the space key, the piece is dropped to the bottom. We simply try to drop the piece one line down until it reaches the bottom or the top of another fallen Tetris piece. private void clearBoard() { for (int i = 0; i < BoardHeight * BoardWidth; ++i) board[i] = Tetrominoes.NoShape; } The clearBoard() method fills the board with empty NoShapes. This is later used at collision detection. private void pieceDropped() { for (int i = 0; i < 4; ++i) { int x = curX + curPiece.x(i); int y = curY - curPiece.y(i); board[(y * BoardWidth) + x] = curPiece.getShape(); } removeFullLines(); if (!isFallingFinished) newPiece(); } The pieceDropped() method puts the falling piece into the board array. Once again, the board holds all the squares of the pieces and remains of the pieces that has finished falling. When the piece has finished falling, it is time to check, if we can remove some lines off the board. This is the job of the removeFullLines() method. Then we create a new piece. More precisely, we try to create a new piece. private void newPiece() { curPiece.setRandomShape(); curX = BoardWidth / 2 + 1; curY = BoardHeight - 1 + curPiece.minY(); if (!tryMove(curPiece, curX, curY)) { curPiece.setShape(Tetrominoes.NoShape); timer.stop(); isStarted = false; statusbar.setText("game over"); } } The newPiece() method creates a new Tetris piece. The piece gets a new random shape. Then we compute the initial curX and curY values. If we cannot move to the initial positions, the game is over. We top out. The timer is stopped. We put game over string on the statusbar. private boolean tryMove(Shape newPiece, int newX, int newY) { for (int i = 0; i < 4; ++i) { int x = newX + newPiece.x(i); int y = newY - newPiece.y(i); if (x < 0 || x >= BoardWidth || y < 0 || y >= BoardHeight) return false; if (shapeAt(x, y) != Tetrominoes.NoShape) return false; } curPiece = newPiece; curX = newX; curY = newY; repaint(); return true; } The tryMove() method tries to move the Tetris piece. The method returns false, if it has reached the board boundaries or it is adjacent to the already fallen Tetris pieces. int numFullLines = 0; for (int i = BoardHeight - 1; i >= 0; --i) { boolean lineIsFull = true; for (int j = 0; j < BoardWidth; ++j) { if (shapeAt(j, i) == Tetrominoes.NoShape) { lineIsFull = false; break; } } if (lineIsFull) { ++numFullLines; for (int k = i; k < BoardHeight - 1; ++k) { for (int j = 0; j < BoardWidth; ++j) board[(k * BoardWidth) + j] = shapeAt(j, k + 1); } } } Inside the removeFullLines() method, we check if there is any full row among all rows in the board. If there is at least one full line, it is removed. After finding a full line we increase the counter. We move all the lines above the full row one line down. This way we destroy the full line. Notice, that in our Tetris game, we use the so called naive gravity. This means, that the squares may be left floating above empty gaps. Every Tetris piece has four squares. Each of the squares is drawn with the drawSquare() method. Tetris pieces have different colors. g.setColor(color.brighter()); g.drawLine(x, y + squareHeight() - 1, x, y); g.drawLine(x, y, x + squareWidth() - 1, y); The left and top sides of a square are drawn with a brighter color. Similarly, the bottom and right sides are drawn with darker colors. This is to simulate a 3D edge. We control the game with a keyboard. The control mechanism is implemented with a KeyAdapter. This is an inner class that overrides the keyPressed() method. case KeyEvent.VK_LEFT: tryMove(curPiece, curX - 1, curY); break; If we press the left arrow key, we try to move the falling piece one square to the left. Figure: Tetris This was the Tetris game Linear gradients Linear gradients are blendings of colours or shades of colours along a line. They are created with the cairo_pattern_create_linear() function. #include <cairo.h> #include <gtk/gtk.h> void draw_gradient1(cairo_t *); void draw_gradient2(cairo_t *); void draw_gradient3(cairo_t *); static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data) { draw_gradient1(cr); draw_gradient2(cr); draw_gradient3(cr); return FALSE; } void draw_gradient1(cairo_t *cr) { cairo_pattern_t *pat1; pat1 = cairo_pattern_create_linear(0.0, 0.0, 350.0, 350.0); gdouble j; gint count = 1; for ( j = 0.1; j < 1; j += 0.1 ) { if (( count % 2 )) { cairo_pattern_add_color_stop_rgb(pat1, j, 0, 0, 0); } else { cairo_pattern_add_color_stop_rgb(pat1, j, 1, 0, 0); } count++; } cairo_rectangle(cr, 20, 20, 300, 100); cairo_set_source(cr, pat1); cairo_fill(cr); cairo_pattern_destroy(pat1); } void draw_gradient2(cairo_t *cr) { cairo_pattern_t *pat2; pat2 = cairo_pattern_create_linear(0.0, 0.0, 350.0, 0.0); gdouble i; gint count = 1; for ( i = 0.05; i < 0.95; i += 0.025 ) { if (( count % 2 )) { cairo_pattern_add_color_stop_rgb(pat2, i, 0, 0, 0); } else { cairo_pattern_add_color_stop_rgb(pat2, i, 0, 0, 1); } count++; } cairo_rectangle(cr, 20, 140, 300, 100); cairo_set_source(cr, pat2); cairo_fill(cr); cairo_pattern_destroy(pat2); } void draw_gradient3(cairo_t *cr) { cairo_pattern_t *pat3; pat3 = cairo_pattern_create_linear(20.0, 260.0, 20.0, 360.0); cairo_pattern_add_color_stop_rgb(pat3, 0.1, 0, 0, 0); cairo_pattern_add_color_stop_rgb(pat3, 0.5, 1, 1, 0); cairo_pattern_add_color_stop_rgb(pat3, 0.9, 0, 0, 0); cairo_rectangle(cr, 20, 260, 300, 100); cairo_set_source(cr, pat3); cairo_fill(cr); cairo_pattern_destroy(pat3); } int main(int argc, char *argv[]) { GtkWidget *window; GtkWidget *darea; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); darea = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER (window), darea); g_signal_connect(G_OBJECT(darea), "draw", G_CALLBACK(on_draw_event), NULL); g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_window_set_default_size(GTK_WINDOW(window), 340, 390); gtk_window_set_title(GTK_WINDOW(window), "Linear gradients"); gtk_widget_set_app_paintable(window, TRUE); gtk_widget_show_all(window); gtk_main(); return 0; } The example draws three rectangles filled with linear gradients. pat3 = cairo_pattern_create_linear(20.0, 260.0, 20.0, 360.0); Here we create a linear gradient pattern. The parameters specify the line, along which we draw the gradient. In our case it is a vertical line. cairo_pattern_add_color_stop_rgb(pat3, 0.1, 0, 0, 0); cairo_pattern_add_color_stop_rgb(pat3, 0.5, 1, 1, 0); cairo_pattern_add_color_stop_rgb(pat3, 0.9, 0, 0, 0); We define colour stops to produce our gradient pattern. In this case, the gradient is a blending of black and yellow colours. By adding two black and one yellow stops, we create a horizontal gradient pattern. What these stops actually mean? In our case, we begin with black colour, which will stop at 1/10 of the size. Then we begin to gradually paint in yellow, which will culminate at the centre of the shape. The yellow colour stops at 9/10 of the size, where we begin painting in black again, until the end. Figure: Linear gradients Radial gradients Radial gradients are blendings of colours or shades of colours between two circles. The cairo_pattern_create_radial() function s is used to create radial gradients in Cairo. #include <cairo.h> #include <math.h> #include <gtk/gtk.h> void draw_gradient1(cairo_t *); void draw_gradient2(cairo_t *); static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data) { draw_gradient1(cr); draw_gradient2(cr); return FALSE; } void draw_gradient1(cairo_t *cr) { cairo_pattern_t *r1; cairo_set_source_rgba(cr, 0, 0, 0, 1); cairo_set_line_width(cr, 12); cairo_translate(cr, 60, 60); r1 = cairo_pattern_create_radial(30, 30, 10, 30, 30, 90); cairo_pattern_add_color_stop_rgba(r1, 0, 1, 1, 1, 1); cairo_pattern_add_color_stop_rgba(r1, 1, 0.6, 0.6, 0.6, 1); cairo_set_source(cr, r1); cairo_arc(cr, 0, 0, 40, 0, M_PI * 2); cairo_fill(cr); cairo_pattern_destroy(r1); } void draw_gradient2(cairo_t *cr) { cairo_pattern_t *r2; cairo_translate(cr, 120, 0); r2 = cairo_pattern_create_radial(0, 0, 10, 0, 0, 40); cairo_pattern_add_color_stop_rgb(r2, 0, 1, 1, 0); cairo_pattern_add_color_stop_rgb(r2, 0.8, 0, 0, 0); cairo_set_source(cr, r2); cairo_arc(cr, 0, 0, 40, 0, M_PI * 2); cairo_fill(cr); } int main(int argc, char *argv[]) { GtkWidget *window; GtkWidget *darea; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); darea = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER (window), darea); g_signal_connect(G_OBJECT(darea), "draw", G_CALLBACK(on_draw_event), NULL); g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_window_set_default_size(GTK_WINDOW(window), 300, 200); gtk_window_set_title(GTK_WINDOW(window), "Radial gradients"); gtk_widget_set_app_paintable(window, TRUE); gtk_widget_show_all(window); gtk_main(); return 0; } In the example, we draw two radial gradients. r1 = cairo_pattern_create_radial(30, 30, 10, 30, 30, 90); cairo_pattern_add_color_stop_rgba(r1, 0, 1, 1, 1, 1); cairo_pattern_add_color_stop_rgba(r1, 1, 0.6, 0.6, 0.6, 1); cairo_set_source(cr, r1); cairo_arc(cr, 0, 0, 40, 0, M_PI * 2); cairo_fill(cr); We draw a circle and fill its inside with a radial gradient. The radial gradient is defined by two circles. The cairo_pattern_add_color_stop_rgba() function defines the colours. We can experiment with the position of the circles or the length of their radius. In the first gradient example, we have created an object which resembles a 3D shape. r2 = cairo_pattern_create_radial(0, 0, 10, 0, 0, 40); cairo_pattern_add_color_stop_rgb(r2, 0, 1, 1, 0); cairo_pattern_add_color_stop_rgb(r2, 0.8, 0, 0, 0); cairo_set_source(cr, r2); cairo_arc(cr, 0, 0, 40, 0, M_PI * 2); cairo_fill(cr); In this example, the circles that define the radial gradient and the custom drawn circle have a common center point. Figure: Radial gradients In this chapter of the Cairo graphics tutorial we have covered gradients GAMES Puzzle We have an image of a Sid character from the Ice Age movie. It is cut into 12 pieces. The goal is to form the picture. Puzzle.java import import import import import import import import java.awt.BorderLayout; java.awt.Dimension; java.awt.GridLayout; java.awt.Image; java.awt.event.ActionEvent; java.awt.event.ActionListener; java.awt.image.CropImageFilter; java.awt.image.FilteredImageSource; import import import import import import javax.swing.Box; javax.swing.ImageIcon; javax.swing.JButton; javax.swing.JFrame; javax.swing.JLabel; javax.swing.JPanel; public class Puzzle extends JFrame implements ActionListener { private JPanel centerPanel; private JButton button; private JLabel label; private Image source; private Image image; int[][] pos; int width, height; public Puzzle() { pos = new int[][] { {0, {3, {6, {9, 1, 2}, 4, 5}, 7, 8}, 10, 11} }; centerPanel = new JPanel(); centerPanel.setLayout(new GridLayout(4, 4, 0, 0)); ImageIcon sid = new ImageIcon(Puzzle.class.getResource("icesid.jpg")); source = sid.getImage(); width = sid.getIconWidth(); height = sid.getIconHeight(); add(Box.createRigidArea(new Dimension(0, 5)), BorderLayout.NORTH); add(centerPanel, BorderLayout.CENTER); for ( int i = 0; i < 4; i++) { for ( int j = 0; j < 3; j++) { if ( j == 2 && i == 3) { label = new JLabel(""); centerPanel.add(label); } else { button = new JButton(); button.addActionListener(this); centerPanel.add(button); image = createImage(new FilteredImageSource(source.getSource(), new CropImageFilter(j*width/3, i*height/4, (width/3)+1, height/4))); button.setIcon(new ImageIcon(image)); } } } setSize(325, 275); setTitle("Puzzle"); setResizable(false); setLocationRelativeTo(null); setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); setVisible(true); } public static void main(String[] args) { new Puzzle(); } public void actionPerformed(ActionEvent e) { JButton button = (JButton) e.getSource(); Dimension size = button.getSize(); int int int int int int int labelX = label.getX(); labelY = label.getY(); buttonX = button.getX(); buttonY = button.getY(); buttonPosX = buttonX / size.width; buttonPosY = buttonY / size.height; buttonIndex = pos[buttonPosY][buttonPosX]; if (labelX == buttonX && (labelY - buttonY) == size.height ) { int labelIndex = buttonIndex + 3; centerPanel.remove(buttonIndex); centerPanel.add(label, buttonIndex); centerPanel.add(button,labelIndex); centerPanel.validate(); } if (labelX == buttonX && (labelY - buttonY) == -size.height ) { int labelIndex = buttonIndex - 3; centerPanel.remove(labelIndex); centerPanel.add(button,labelIndex); centerPanel.add(label, buttonIndex); centerPanel.validate(); } if (labelY == buttonY && (labelX - buttonX) == size.width ) { int labelIndex = buttonIndex + 1; centerPanel.remove(buttonIndex); centerPanel.add(label, buttonIndex); centerPanel.add(button,labelIndex); centerPanel.validate(); } if (labelY == buttonY && (labelX - buttonX) == -size.width ) { int labelIndex = buttonIndex - 1; centerPanel.remove(buttonIndex); centerPanel.add(label, labelIndex); centerPanel.add(button,labelIndex); centerPanel.validate(); } } } The goal of this little game is to form the original picture. We move the buttons by clicking on them. Only buttons adjacent to the label can be moved. pos = new int[][] { {0, 1, 2}, {3, 4, 5}, {6, 7, 8}, {9, 10, 11} }; These are the positions of the image parts. ImageIcon sid = new ImageIcon(Puzzle.class.getResource("icesid.jpg")); source = sid.getImage(); We use the ImageIcon class to load the image. for ( int i = 0; i < 4; i++) { for ( int j = 0; j < 3; j++) { if ( j == 2 && i == 3) { label = new JLabel(""); centerPanel.add(label); } else { button = new JButton(); button.addActionListener(this); centerPanel.add(button); image = createImage(new FilteredImageSource(source.getSource(), new CropImageFilter(j*width/3, i*height/4, (width/3)+1, height/4))); button.setIcon(new ImageIcon(image)); } } } The code creates 11 buttons and one label. We crop the image into pieces and place them on the buttons. int int int int labelX = label.getX(); labelY = label.getY(); buttonX = button.getX(); buttonY = button.getY(); We get the x, y coordinates of the button that we hit and an empty label. The x, y coordinates are important in the logic of the program. int buttonPosX = buttonX / size.width; int buttonPosY = buttonY / size.height; int buttonIndex = pos[buttonPosY][buttonPosX]; Here we get the index of the button in the two dimensional array of the button positions. if (labelX == buttonX && (labelY - buttonY) == size.height ) { int labelIndex = buttonIndex + 3; centerPanel.remove(buttonIndex); centerPanel.add(label, buttonIndex); centerPanel.add(button,labelIndex); centerPanel.validate(); } In this case, we check if we clicked on the button, that is right above the empty label. If it is above the label, they share the x coordinate. If the button is right above the label, the equation (labelY - buttonY) == size.height is true. Figure: Puzzle Snake is an older classic video game. It was first created in late 70s. Later it was brought to PCs. In this game the player controls a snake. The objective is to eat as many apples as possible. Each time the snake eats an apple, its body grows. The snake must avoid the walls and its own body. This game is sometimes called Nibbles. Development The size of each of the joints of a snake is 10px. The snake is controlled with the cursor keys. Initially the snake has three joints. The game is started by pressing one of the cursor keys. If the game is finished, we display Game Over message in the middle of the Board. Board.java package snake2; import import import import import import import import import import java.awt.Color; java.awt.Font; java.awt.FontMetrics; java.awt.Graphics; java.awt.Image; java.awt.Toolkit; java.awt.event.ActionEvent; java.awt.event.ActionListener; java.awt.event.KeyAdapter; java.awt.event.KeyEvent; import javax.swing.ImageIcon; import javax.swing.JPanel; import javax.swing.Timer; public class Board extends JPanel implements ActionListener { private private private private private private final final final final final final int int int int int int WIDTH = 300; HEIGHT = 300; DOT_SIZE = 10; ALL_DOTS = 900; RAND_POS = 29; DELAY = 140; private int x[] = new int[ALL_DOTS]; private int y[] = new int[ALL_DOTS]; private int dots; private int apple_x; private int apple_y; private private private private private boolean boolean boolean boolean boolean left = false; right = true; up = false; down = false; inGame = true; private Timer timer; private Image ball; private Image apple; private Image head; public Board() { addKeyListener(new TAdapter()); setBackground(Color.black); ImageIcon iid = new ImageIcon(this.getClass().getResource("dot.png")); ball = iid.getImage(); ImageIcon iia = new ImageIcon(this.getClass().getResource("apple.png")); apple = iia.getImage(); ImageIcon iih = new ImageIcon(this.getClass().getResource("head.png")); head = iih.getImage(); setFocusable(true); initGame(); } public void initGame() { dots = 3; for (int z = 0; z < dots; z++) { x[z] = 50 - z*10; y[z] = 50; } locateApple(); timer = new Timer(DELAY, this); timer.start(); } public void paint(Graphics g) { super.paint(g); if (inGame) { g.drawImage(apple, apple_x, apple_y, this); for (int z = 0; z < dots; z++) { if (z == 0) g.drawImage(head, x[z], y[z], this); else g.drawImage(ball, x[z], y[z], this); } Toolkit.getDefaultToolkit().sync(); g.dispose(); } else { gameOver(g); } } public void gameOver(Graphics g) { String msg = "Game Over"; Font small = new Font("Helvetica", Font.BOLD, 14); FontMetrics metr = this.getFontMetrics(small); g.setColor(Color.white); g.setFont(small); g.drawString(msg, (WIDTH - metr.stringWidth(msg)) / 2, HEIGHT / 2); } public void checkApple() { if ((x[0] == apple_x) && (y[0] == apple_y)) { dots++; locateApple(); } } public void move() { for (int z = dots; z > 0; z--) { x[z] = x[(z - 1)]; y[z] = y[(z - 1)]; } if (left) { x[0] -= DOT_SIZE; } if (right) { x[0] += DOT_SIZE; } if (up) { y[0] -= DOT_SIZE; } if (down) { y[0] += DOT_SIZE; } } public void checkCollision() { for (int z = dots; z > 0; z--) { if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) { inGame = false; } } if (y[0] > HEIGHT) { inGame = false; } if (y[0] < 0) { inGame = false; } if (x[0] > WIDTH) { inGame = false; } if (x[0] < 0) { inGame = false; } } public void locateApple() { int r = (int) (Math.random() * RAND_POS); apple_x = ((r * DOT_SIZE)); r = (int) (Math.random() * RAND_POS); apple_y = ((r * DOT_SIZE)); } public void actionPerformed(ActionEvent e) { if (inGame) { checkApple(); checkCollision(); move(); } repaint(); } private class TAdapter extends KeyAdapter { public void keyPressed(KeyEvent e) { int key = e.getKeyCode(); if ((key left up = down } == KeyEvent.VK_LEFT) && (!right)) { = true; false; = false; if ((key == KeyEvent.VK_RIGHT) && (!left)) { right = true; up = false; down = false; } if ((key == KeyEvent.VK_UP) && (!down)) { up = true; right = false; left = false; } if ((key == KeyEvent.VK_DOWN) && (!up)) { down = true; right = false; left = false; } } } } First we will define the constants used in our game. private private private private private private final final final final final final int int int int int int WIDTH = 300; HEIGHT = 300; DOT_SIZE = 10; ALL_DOTS = 900; RAND_POS = 29; DELAY = 140; The WIDTH and HEIGHT constants determine the size of the Board. The DOT_SIZE is the size of the apple and the dot of the snake. The ALL_DOTS constant defines the maximum number of possible dots on the Board. (900 = 300*300/10*10) The RAND_POS constant is used to calculate a random position of an apple. The DELAY constant determines the speed of the game. private int x[] = new int[ALL_DOTS]; private int y[] = new int[ALL_DOTS]; These two arrays store x, y coordinates of all joints of a snake. In the move() method we have the key algorithm of the game. To understand it, look at how the snake is moving. You control the head of the snake. You can change its direction with the cursor keys. The rest of the joints move one position up the chain. The second joint moves where the first was, the third joint where the second was etc. for (int z = dots; z > 0; z--) { x[z] = x[(z - 1)]; y[z] = y[(z - 1)]; } This code moves the joints up the chain. if (left) { x[0] -= DOT_SIZE; } Move the head to the left. In the checkCollision() method, we determine if the snake has hit itself or one of the walls. for (int z = dots; z > 0; z--) { if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) { inGame = false; } } Finish the game, if the snake hits one of its joints with the head. if (y[0] > HEIGHT) { inGame = false; } Finish the game, if the snake hits the bottom of the Board. Snake.java package snake2; import javax.swing.JFrame; public class Snake extends JFrame { public Snake() { add(new Board()); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(320, 340); setLocationRelativeTo(null); setTitle("Snake"); setResizable(false); setVisible(true); } public static void main(String[] args) { new Snake(); } } This is the main class. Figure: Snake Minesweeper Minesweeper is a popular board game shipped with many operating systems by default. The goal of the game is to sweep all mines from a mine field. If the player clicks on the cell which contains a mine, the mine detonates and the game is over. Further a cell can contain a number or it can be blank. The number indicates how many mines are adjacent to this particular cell. We set a mark on a cell by right clicking on it. This way we indicate, that we believe, there is a mine. The following Minesweeper clone is a modified and simplified version of an Java applet which I found at javaside.com. Board.java package mines; import import import import java.awt.Graphics; java.awt.Image; java.awt.event.MouseAdapter; java.awt.event.MouseEvent; import java.util.Random; import import import import javax.swing.BorderFactory; javax.swing.ImageIcon; javax.swing.JLabel; javax.swing.JPanel; public class Board extends JPanel { private final int NUM_IMAGES = 13; private final int CELL_SIZE = 15; private final private final private final private final private final private final MARK_FOR_CELL; int int int int int int COVER_FOR_CELL = 10; MARK_FOR_CELL = 10; EMPTY_CELL = 0; MINE_CELL = 9; COVERED_MINE_CELL = MINE_CELL + COVER_FOR_CELL; MARKED_MINE_CELL = COVERED_MINE_CELL + int int int int DRAW_MINE = 9; DRAW_COVER = 10; DRAW_MARK = 11; DRAW_WRONG_MARK = 12; private private private private final final final final private private private private private private private private private int[] field; boolean inGame; int mines_left; Image[] img; int mines = 40; int rows = 16; int cols = 16; int all_cells; JLabel statusbar; public Board(JLabel statusbar) { this.statusbar = statusbar; img = new Image[NUM_IMAGES]; for (int i = 0; i < NUM_IMAGES; i++) { img[i] = (new ImageIcon(this.getClass().getResource((i) + ".png"))).getImage(); } setDoubleBuffered(true); addMouseListener(new MinesAdapter()); newGame(); } public void newGame() { Random random; int current_col; int i = 0; int position = 0; int cell = 0; random = new Random(); inGame = true; mines_left = mines; all_cells = rows * cols; field = new int[all_cells]; for (i = 0; i < all_cells; i++) field[i] = COVER_FOR_CELL; statusbar.setText(Integer.toString(mines_left)); i = 0; while (i < mines) { position = (int) (all_cells * random.nextDouble()); if ((position < all_cells) && (field[position] != COVERED_MINE_CELL)) { current_col = position % cols; field[position] = COVERED_MINE_CELL; i++; if (current_col > 0) { cell = position - 1 - cols; if (cell >= 0) if (field[cell] != COVERED_MINE_CELL) field[cell] += 1; cell = position - 1; if (cell >= 0) if (field[cell] != COVERED_MINE_CELL) field[cell] += 1; cell = position + cols - 1; if (cell < all_cells) if (field[cell] != COVERED_MINE_CELL) field[cell] += 1; } cell = position - cols; if (cell >= 0) if (field[cell] != COVERED_MINE_CELL) field[cell] += 1; cell = position + cols; if (cell < all_cells) if (field[cell] != COVERED_MINE_CELL) field[cell] += 1; if (current_col < (cols - 1)) { cell = position - cols + 1; if (cell >= 0) if (field[cell] != COVERED_MINE_CELL) field[cell] += 1; cell = position + cols + 1; if (cell < all_cells) if (field[cell] != COVERED_MINE_CELL) field[cell] += 1; cell = position + 1; if (cell < all_cells) if (field[cell] != COVERED_MINE_CELL) field[cell] += 1; } } } } public void find_empty_cells(int j) { int current_col = j % cols; int cell; if (current_col > 0) { cell = j - cols - 1; if (cell >= 0) if (field[cell] > MINE_CELL) { field[cell] -= COVER_FOR_CELL; if (field[cell] == EMPTY_CELL) find_empty_cells(cell); } cell = j - 1; if (cell >= 0) if (field[cell] > MINE_CELL) { field[cell] -= COVER_FOR_CELL; if (field[cell] == EMPTY_CELL) find_empty_cells(cell); } cell = j + cols - 1; if (cell < all_cells) if (field[cell] > MINE_CELL) { field[cell] -= COVER_FOR_CELL; if (field[cell] == EMPTY_CELL) find_empty_cells(cell); } } cell = j - cols; if (cell >= 0) if (field[cell] > MINE_CELL) { field[cell] -= COVER_FOR_CELL; if (field[cell] == EMPTY_CELL) find_empty_cells(cell); } cell = j + cols; if (cell < all_cells) if (field[cell] > MINE_CELL) { field[cell] -= COVER_FOR_CELL; if (field[cell] == EMPTY_CELL) find_empty_cells(cell); } if (current_col < (cols - 1)) { cell = j - cols + 1; if (cell >= 0) if (field[cell] > MINE_CELL) { field[cell] -= COVER_FOR_CELL; if (field[cell] == EMPTY_CELL) find_empty_cells(cell); } cell = j + cols + 1; if (cell < all_cells) if (field[cell] > MINE_CELL) { field[cell] -= COVER_FOR_CELL; if (field[cell] == EMPTY_CELL) find_empty_cells(cell); } cell = j + 1; if (cell < all_cells) if (field[cell] > MINE_CELL) { field[cell] -= COVER_FOR_CELL; if (field[cell] == EMPTY_CELL) find_empty_cells(cell); } } } public void paint(Graphics g) { int cell = 0; int uncover = 0; for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { cell = field[(i * cols) + j]; if (inGame && cell == MINE_CELL) inGame = false; if (!inGame) { if (cell == COVERED_MINE_CELL) { cell = DRAW_MINE; } else if (cell == MARKED_MINE_CELL) { cell = DRAW_MARK; } else if (cell > COVERED_MINE_CELL) { cell = DRAW_WRONG_MARK; } else if (cell > MINE_CELL) { cell = DRAW_COVER; } } else { if (cell > COVERED_MINE_CELL) cell = DRAW_MARK; else if (cell > MINE_CELL) { cell = DRAW_COVER; uncover++; } } g.drawImage(img[cell], (j * CELL_SIZE), (i * CELL_SIZE), this); } } if (uncover == 0 && inGame) { inGame = false; statusbar.setText("Game won"); } else if (!inGame) statusbar.setText("Game lost"); } class MinesAdapter extends MouseAdapter { public void mousePressed(MouseEvent e) { int x = e.getX(); int y = e.getY(); int cCol = x / CELL_SIZE; int cRow = y / CELL_SIZE; boolean rep = false; if (!inGame) { newGame(); repaint(); } if ((x < cols * CELL_SIZE) && (y < rows * CELL_SIZE)) { if (e.getButton() == MouseEvent.BUTTON3) { if (field[(cRow * cols) + cCol] > MINE_CELL) { rep = true; if (field[(cRow * cols) + cCol] <= COVERED_MINE_CELL) { if (mines_left > 0) { field[(cRow * cols) + cCol] += MARK_FOR_CELL; mines_left--; statusbar.setText(Integer.toString(mines_left)); } else statusbar.setText("No marks left"); } else { field[(cRow * cols) + cCol] -= MARK_FOR_CELL; mines_left++; statusbar.setText(Integer.toString(mines_left)); } } } else { if (field[(cRow * cols) + cCol] > COVERED_MINE_CELL) { return; } if ((field[(cRow * cols) + cCol] > MINE_CELL) && (field[(cRow * cols) + cCol] < MARKED_MINE_CELL)) { field[(cRow * cols) + cCol] -= COVER_FOR_CELL; rep = true; if (field[(cRow * cols) + cCol] == MINE_CELL) inGame = false; if (field[(cRow * cols) + cCol] == EMPTY_CELL) find_empty_cells((cRow * cols) + cCol); } } if (rep) repaint(); } } } } First we will define the constants used in our game. private final int NUM_IMAGES = 13; private final int CELL_SIZE = 15; There are 13 images used in this game. A cell can be surrounded by maximum of 8 mines, so we need numbers 1..8. We need images for an empty cell, a mine, a covered cell, a marked cell and finally for a wrongly marked cell. The size of each of the images is 15x15 px. private final int COVER_FOR_CELL = 10; private final int MARK_FOR_CELL = 10; private final int EMPTY_CELL = 0; ... A mine field is an array of numbers. For example 0 denotes an empty cell. Number 10 is used for a cell cover as well as for a mark. Using constants improves readability of the code. private int[] field; The field is an array of numbers. Each cell in the field has a specific number. E.g. a mine cell has number 9. A cell with number 2, meaning it is adjacent to two mines, has number two. The numbers are added. For example, a covered mine has number 19, 9 for the mine and 10 for the cell cover etc. private int mines = 40; private int rows = 16; private int cols = 16; The minefield in our game has 40 hidden mines. There are 16 rows and 16 columns in this field. So there are 256 cells together in the minefield. for (int i = 0; i < NUM_IMAGES; i++) { img[i] = (new ImageIcon(this.getClass().getResource((i) + ".png"))).getImage(); } Here we load our images into the image array. The images are named 0.png, 1.png ... 12.png. The newGame() initiates the Minesweeper game. all_cells = rows * cols; field = new int[all_cells]; for (i = 0; i < all_cells; i++) field[i] = COVER_FOR_CELL; These lines set up the mine field. Every cell is covered by default. while (i < mines) { position = (int) (all_cells * random.nextDouble()); if ((position < all_cells) && (field[position] != COVERED_MINE_CELL)) { current_col = position % cols; field[position] = COVERED_MINE_CELL; ... In the while cycle we randomly position all mines in the field. cell = position - cols; if (cell >= 0) if (field[cell] != COVERED_MINE_CELL) field[cell] += 1; Each of the cells can be surrounded up to 8 cells. (This does not apply to the border cells.) We raise the number for adjacent cells for each of the randomly placed mine. In our example, we add 1 to the top neighbor of the cell in question. In the find_empty_cells() method, we find empty cells. If the player clicks on a mine cell, the game is over. If he clicks on a cell adjacent to a mine, he uncovers a number indicating how many mines the cell is adjacent to. Clicking on an empty cell leads to uncovering many other empty cells plus cells with a number that form a border around a space of empty borders. We use a recursive algorithm to find empty cells. cell = j - 1; if (cell >= 0) if (field[cell] > MINE_CELL) { field[cell] -= COVER_FOR_CELL; if (field[cell] == EMPTY_CELL) find_empty_cells(cell); } In this code, we check the cell that is left to an empty cell in question. If it is not empty, uncover it. If it is empty, repeat the whole process by recursively calling the find_empty_cells() method. The paint() method turns numbers into images. if (!inGame) { if (cell == COVERED_MINE_CELL) { cell = DRAW_MINE; } else if (cell == MARKED_MINE_CELL) { cell = DRAW_MARK; } else if (cell > COVERED_MINE_CELL) { cell = DRAW_WRONG_MARK; } else if (cell > MINE_CELL) { cell = DRAW_COVER; } } If the game is over and we lost, we show all uncovered mines if any and show all wrongly marked cells if any. g.drawImage(img[cell], (j * CELL_SIZE), (i * CELL_SIZE), this); This code line draws every cell. In the mousePressed() method we react to mouse clicks. The Minesweeper game is controlled solely by mouse. We react to left and right mouse clicks. field[(cRow * cols) + cCol] += MARK_FOR_CELL; mines_left--; If we right click on an unmarked cell, we add MARK_FOR_CELL to the number representing the cell. This leads to drawing a covered cell with a mark in the paint() method. if (field[(cRow * cols) + cCol] > COVERED_MINE_CELL) { return; } Nothing happens, if we click on the covered & marked cell. It must by first uncovered by another right click and only then it is possible to left click on it. field[(cRow * cols) + cCol] -= COVER_FOR_CELL; Left click removes a cover from the cell. if (field[(cRow * cols) + cCol] == MINE_CELL) inGame = false; if (field[(cRow * cols) + cCol] == EMPTY_CELL) find_empty_cells((cRow * cols) + cCol); In case we left clicked on a mine, the game is over. If we left clicked on an empty cell, we call the find_empty_cells() method, which recursively finds all adjacent empty cells. Mines.java package mines; import java.awt.BorderLayout; import javax.swing.JFrame; import javax.swing.JLabel; public class Mines extends JFrame { private final int WIDTH = 250; private final int HEIGHT = 290; private JLabel statusbar; public Mines() { setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(WIDTH, HEIGHT); setLocationRelativeTo(null); setTitle("Minesweeper"); statusbar = new JLabel(""); add(statusbar, BorderLayout.SOUTH); add(new Board(statusbar)); setResizable(false); setVisible(true); } public static void main(String[] args) { new Mines(); } } This is the main class. Figure: Minesweepe Development The following code example is a remake of a Pacman game by Brian Postma available at javaboutique. I have modified and simplified the code. So that it is easier to understand. The goal of the game is to collect all the points in the maze and avoid the ghosts. The pacman is animated in two ways. His position in the maze and his body. We animate his body with four images, depending on the direction. The animation is used to create the illusion of pacman opening and closing his mouth. The maze consists of 15 x 15 squares. The structure of the maze is based on a simple array of integers. Pacman has three lives. We also count the score. The game consists of two files. Board.java package pacman; import import import import import import import import java.awt.BasicStroke; java.awt.Color; java.awt.Dimension; java.awt.Event; java.awt.Font; java.awt.FontMetrics; java.awt.Graphics; java.awt.Graphics2D; import import import import import import java.awt.Image; java.awt.Toolkit; java.awt.event.ActionEvent; java.awt.event.ActionListener; java.awt.event.KeyAdapter; java.awt.event.KeyEvent; import javax.swing.ImageIcon; import javax.swing.JPanel; import javax.swing.Timer; public class Board extends JPanel implements ActionListener { Dimension d; Font smallfont = new Font("Helvetica", Font.BOLD, 14); FontMetrics fmsmall, fmlarge; Image ii; Color dotcolor = new Color(192, 192, 0); Color mazecolor; boolean ingame = false; boolean dying = false; final final final final final final final int int int int int int int blocksize = 24; nrofblocks = 15; scrsize = nrofblocks * blocksize; pacanimdelay = 2; pacmananimcount = 4; maxghosts = 12; pacmanspeed = 6; int pacanimcount = pacanimdelay; int pacanimdir = 1; int pacmananimpos = 0; int nrofghosts = 6; int pacsleft, score; int deathcounter; int[] dx, dy; int[] ghostx, ghosty, ghostdx, ghostdy, ghostspeed; Image Image Image Image ghost; pacman1, pacman2up, pacman2left, pacman2right, pacman2down; pacman3up, pacman3down, pacman3left, pacman3right; pacman4up, pacman4down, pacman4left, pacman4right; int pacmanx, pacmany, pacmandx, pacmandy; int reqdx, reqdy, viewdx, viewdy; final { 19, 21, 21, 21, 17, 17, 25, 1, 1, 1, short leveldata[] = 26, 26, 26, 18, 18, 0, 0, 0, 17, 16, 0, 0, 0, 17, 16, 0, 0, 0, 17, 16, 18, 18, 18, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 24, 24, 17, 16, 20, 0, 0, 17, 16, 16, 18, 18, 17, 16, 16, 16, 16, 18, 16, 16, 16, 20, 20, 28, 0, 22, 20, 18, 16, 16, 24, 0, 0, 0, 0, 0, 0, 18, 16, 16, 16, 17, 17, 25, 0, 19, 17, 18, 16, 16, 16, 16, 16, 24, 0, 18, 16, 18, 16, 16, 16, 16, 16, 24, 0, 18, 16, 18, 16, 16, 16, 16, 16, 16, 17, 16, 16, 18, 16, 16, 16, 16, 16, 20, 20, 20, 20, 18, 16, 16, 16, 16, 24, 0, 0, 0, 0, 22, 20, 20, 20, 20, 20, 21, 21, 21, 21, 1, 1, 1, 1, 9, 17, 17, 17, 25, 8, 16, 16, 16, 24, 8, 16, 16, 16, 24, 8, 16, 16, 16, 24, 8, 16, 16, 16, 24, 8, 20, 16, 16, 24, 8, 0, 18, 16, 24, 8, 17, 16, 16, 24, 8, 16, 16, 16, 24, 8, 16, 16, 16, 16, 25, 16, 16, 16, 16, 24, 20, 20, 20, 16, 24, final int validspeeds[] = { 1, 2, 3, 4, 6, 8 }; final int maxspeed = 6; int currentspeed = 3; short[] screendata; Timer timer; public Board() { GetImages(); addKeyListener(new TAdapter()); screendata = new short[nrofblocks * nrofblocks]; mazecolor = new Color(5, 100, 5); setFocusable(true); d = new Dimension(400, 400); setBackground(Color.black); setDoubleBuffered(true); ghostx = new int[maxghosts]; ghostdx = new int[maxghosts]; ghosty = new int[maxghosts]; ghostdy = new int[maxghosts]; ghostspeed = new int[maxghosts]; dx = new int[4]; dy = new int[4]; timer = new Timer(40, this); timer.start(); } public void addNotify() { super.addNotify(); GameInit(); } public void DoAnim() { pacanimcount--; if (pacanimcount <= 0) { pacanimcount = pacanimdelay; pacmananimpos = pacmananimpos + pacanimdir; if (pacmananimpos == (pacmananimcount - 1) || pacmananimpos == 0) pacanimdir = -pacanimdir; } } public void PlayGame(Graphics2D g2d) { if (dying) { Death(); 0, 0, 0, 18, 24, 21, 21, 21, 20, 28 }; } else { MovePacMan(); DrawPacMan(g2d); moveGhosts(g2d); CheckMaze(); } } public void ShowIntroScreen(Graphics2D g2d) { g2d.setColor(new Color(0, 32, 48)); g2d.fillRect(50, scrsize / 2 - 30, scrsize - 100, 50); g2d.setColor(Color.white); g2d.drawRect(50, scrsize / 2 - 30, scrsize - 100, 50); String s = "Press s to start."; Font small = new Font("Helvetica", Font.BOLD, 14); FontMetrics metr = this.getFontMetrics(small); g2d.setColor(Color.white); g2d.setFont(small); g2d.drawString(s, (scrsize - metr.stringWidth(s)) / 2, scrsize / 2); } public void DrawScore(Graphics2D g) { int i; String s; g.setFont(smallfont); g.setColor(new Color(96, 128, 255)); s = "Score: " + score; g.drawString(s, scrsize / 2 + 96, scrsize + 16); for (i = 0; i < pacsleft; i++) { g.drawImage(pacman3left, i * 28 + 8, scrsize + 1, this); } } public void CheckMaze() { short i = 0; boolean finished = true; while (i < nrofblocks * nrofblocks && finished) { if ((screendata[i] & 48) != 0) finished = false; i++; } if (finished) { score += 50; if (nrofghosts < maxghosts) nrofghosts++; if (currentspeed < maxspeed) currentspeed++; LevelInit(); } } public void Death() { pacsleft--; if (pacsleft == 0) ingame = false; LevelContinue(); } public void moveGhosts(Graphics2D g2d) { short i; int pos; int count; for (i = 0; i < nrofghosts; i++) { if (ghostx[i] % blocksize == 0 && ghosty[i] % blocksize == 0) { pos = ghostx[i] / blocksize + nrofblocks * (int)(ghosty[i] / blocksize); count = 0; if ((screendata[pos] dx[count] = -1; dy[count] = 0; count++; } if ((screendata[pos] dx[count] = 0; dy[count] = -1; count++; } if ((screendata[pos] dx[count] = 1; dy[count] = 0; count++; } if ((screendata[pos] dx[count] = 0; dy[count] = 1; count++; } & 1) == 0 && ghostdx[i] != 1) { & 2) == 0 && ghostdy[i] != 1) { & 4) == 0 && ghostdx[i] != -1) { & 8) == 0 && ghostdy[i] != -1) { if (count == 0) { if ((screendata[pos] & 15) == 15) { ghostdx[i] = 0; ghostdy[i] = 0; } else { ghostdx[i] = -ghostdx[i]; ghostdy[i] = -ghostdy[i]; } } else { count = (int)(Math.random() * count); if (count > 3) count = 3; ghostdx[i] = dx[count]; ghostdy[i] = dy[count]; } } ghostx[i] = ghostx[i] + (ghostdx[i] * ghostspeed[i]); ghosty[i] = ghosty[i] + (ghostdy[i] * ghostspeed[i]); DrawGhost(g2d, ghostx[i] + 1, ghosty[i] + 1); if (pacmanx > (ghostx[i] - 12) && pacmanx < (ghostx[i] + 12) && pacmany > (ghosty[i] - 12) && pacmany < (ghosty[i] + 12) && ingame) { dying = true; deathcounter = 64; } } } public void DrawGhost(Graphics2D g2d, int x, int y) { g2d.drawImage(ghost, x, y, this); } public void MovePacMan() { int pos; short ch; if (reqdx == -pacmandx && reqdy == -pacmandy) { pacmandx = reqdx; pacmandy = reqdy; viewdx = pacmandx; viewdy = pacmandy; } if (pacmanx % blocksize == 0 && pacmany % blocksize == 0) { pos = pacmanx / blocksize + nrofblocks * (int)(pacmany / blocksize); ch = screendata[pos]; if ((ch & 16) != 0) { screendata[pos] = (short)(ch & 15); score++; } if (reqdx != 0 || reqdy != 0) { if (!((reqdx == -1 && reqdy == 0 && (ch & 1) != 0) || (reqdx == 1 && reqdy == 0 && (ch & 4) != 0) || (reqdx == 0 && reqdy == -1 && (ch & 2) != 0) || (reqdx == 0 && reqdy == 1 && (ch & 8) != 0))) { pacmandx = reqdx; pacmandy = reqdy; viewdx = pacmandx; viewdy = pacmandy; } } // Check for standstill if ((pacmandx == -1 && pacmandy == 0 && (ch & 1) != 0) || (pacmandx == 1 && pacmandy == 0 && (ch & 4) != 0) || (pacmandx == 0 && pacmandy == -1 && (ch & 2) != 0) || (pacmandx == 0 && pacmandy == 1 && (ch & 8) != 0)) { pacmandx = 0; pacmandy = 0; } } pacmanx = pacmanx + pacmanspeed * pacmandx; pacmany = pacmany + pacmanspeed * pacmandy; } public void DrawPacMan(Graphics2D g2d) { if (viewdx == -1) DrawPacManLeft(g2d); else if (viewdx == 1) DrawPacManRight(g2d); else if (viewdy == -1) DrawPacManUp(g2d); else DrawPacManDown(g2d); } public void DrawPacManUp(Graphics2D g2d) switch (pacmananimpos) { case 1: g2d.drawImage(pacman2up, pacmanx break; case 2: g2d.drawImage(pacman3up, pacmanx break; case 3: g2d.drawImage(pacman4up, pacmanx break; default: g2d.drawImage(pacman1, pacmanx + break; } } { + 1, pacmany + 1, this); + 1, pacmany + 1, this); + 1, pacmany + 1, this); 1, pacmany + 1, this); public void DrawPacManDown(Graphics2D g2d) { switch (pacmananimpos) { case 1: g2d.drawImage(pacman2down, pacmanx + 1, pacmany + 1, this); break; case 2: g2d.drawImage(pacman3down, pacmanx + 1, pacmany + 1, this); break; case 3: g2d.drawImage(pacman4down, pacmanx + 1, pacmany + 1, this); break; default: g2d.drawImage(pacman1, pacmanx + 1, pacmany + 1, this); break; } } public void DrawPacManLeft(Graphics2D g2d) { switch (pacmananimpos) { case 1: g2d.drawImage(pacman2left, pacmanx + 1, pacmany + 1, this); break; case 2: g2d.drawImage(pacman3left, pacmanx + 1, pacmany + 1, this); break; case 3: g2d.drawImage(pacman4left, pacmanx + 1, pacmany + 1, this); break; default: g2d.drawImage(pacman1, pacmanx + 1, pacmany + 1, this); break; } } public void DrawPacManRight(Graphics2D g2d) switch (pacmananimpos) { case 1: g2d.drawImage(pacman2right, pacmanx this); break; case 2: g2d.drawImage(pacman3right, pacmanx this); break; case 3: g2d.drawImage(pacman4right, pacmanx this); break; default: g2d.drawImage(pacman1, pacmanx + 1, break; } } { + 1, pacmany + 1, + 1, pacmany + 1, + 1, pacmany + 1, pacmany + 1, this); public void DrawMaze(Graphics2D g2d) { short i = 0; int x, y; for (y = 0; y < scrsize; y += blocksize) { for (x = 0; x < scrsize; x += blocksize) { g2d.setColor(mazecolor); g2d.setStroke(new BasicStroke(2)); if ((screendata[i] & 1) != 0) // draws left { g2d.drawLine(x, y, x, y + blocksize - 1); } if ((screendata[i] & 2) != 0) // draws top { g2d.drawLine(x, y, x + blocksize - 1, y); } if ((screendata[i] & 4) != 0) // draws right { g2d.drawLine(x + blocksize - 1, y, x + blocksize 1, y + blocksize - 1); } if ((screendata[i] & 8) != 0) // draws bottom { g2d.drawLine(x, y + blocksize - 1, x + blocksize 1, y + blocksize - 1); } if ((screendata[i] & 16) != 0) // draws point { g2d.setColor(dotcolor); g2d.fillRect(x + 11, y + 11, 2, 2); } i++; } } } public void GameInit() { pacsleft = 3; score = 0; LevelInit(); nrofghosts = 6; currentspeed = 3; } public void LevelInit() { int i; for (i = 0; i < nrofblocks * nrofblocks; i++) screendata[i] = leveldata[i]; LevelContinue(); } public void LevelContinue() { short i; int dx = 1; int random; for (i = 0; i < nrofghosts; i++) { ghosty[i] = 4 * blocksize; ghostx[i] = 4 * blocksize; ghostdy[i] = 0; ghostdx[i] = dx; dx = -dx; random = (int)(Math.random() * (currentspeed + 1)); if (random > currentspeed) random = currentspeed; ghostspeed[i] = validspeeds[random]; } pacmanx = 7 * blocksize; pacmany = 11 * blocksize; pacmandx = 0; pacmandy = 0; reqdx = 0; reqdy = 0; viewdx = -1; viewdy = 0; dying = false; } public void GetImages() { ghost = new ImageIcon(Board.class.getResource("../pacpix/ghost.png")).getImage(); pacman1 = new ImageIcon(Board.class.getResource("../pacpix/pacman.png")).getImage(); pacman2up = new ImageIcon(Board.class.getResource("../pacpix/up1.png")).getImage(); pacman3up = new ImageIcon(Board.class.getResource("../pacpix/up2.png")).getImage(); pacman4up = new ImageIcon(Board.class.getResource("../pacpix/up3.png")).getImage(); pacman2down = new ImageIcon(Board.class.getResource("../pacpix/down1.png")).getImage(); pacman3down = new ImageIcon(Board.class.getResource("../pacpix/down2.png")).getImage(); pacman4down = new ImageIcon(Board.class.getResource("../pacpix/down3.png")).getImage(); pacman2left = new ImageIcon(Board.class.getResource("../pacpix/left1.png")).getImage(); pacman3left = new ImageIcon(Board.class.getResource("../pacpix/left2.png")).getImage(); pacman4left = new ImageIcon(Board.class.getResource("../pacpix/left3.png")).getImage(); pacman2right = new ImageIcon(Board.class.getResource("../pacpix/right1.png")).getImage(); pacman3right = new ImageIcon(Board.class.getResource("../pacpix/right2.png")).getImage(); pacman4right = new ImageIcon(Board.class.getResource("../pacpix/right3.png")).getImage(); } public void paint(Graphics g) { super.paint(g); Graphics2D g2d = (Graphics2D) g; g2d.setColor(Color.black); g2d.fillRect(0, 0, d.width, d.height); DrawMaze(g2d); DrawScore(g2d); DoAnim(); if (ingame) PlayGame(g2d); else ShowIntroScreen(g2d); g.drawImage(ii, 5, 5, this); Toolkit.getDefaultToolkit().sync(); g.dispose(); } class TAdapter extends KeyAdapter { public void keyPressed(KeyEvent e) { int key = e.getKeyCode(); if (ingame) { if (key == KeyEvent.VK_LEFT) { reqdx=-1; reqdy=0; } else if (key == KeyEvent.VK_RIGHT) { reqdx=1; reqdy=0; } else if (key == KeyEvent.VK_UP) { reqdx=0; reqdy=-1; } else if (key == KeyEvent.VK_DOWN) { reqdx=0; reqdy=1; } else if (key == KeyEvent.VK_ESCAPE && timer.isRunning()) { ingame=false; } else if (key == KeyEvent.VK_PAUSE) { if (timer.isRunning()) timer.stop(); else timer.start(); } } else { if (key == 's' || key == 'S') { ingame=true; GameInit(); } } } public void keyReleased(KeyEvent e) { int key = e.getKeyCode(); if (key == Event.LEFT || key == Event.RIGHT || key == Event.UP || key == Event.DOWN) { reqdx=0; reqdy=0; } } } public void actionPerformed(ActionEvent e) { repaint(); } } The Commons.java file has some common constants. final short leveldata[] = { 19, 26, 26, 26, 18, ... }; These numbers make up the maze. They provide information out of which we create the corners and the points. For example number 19 in the upper left corner means, that the square will have top and left borders and a point. (16 + 2 + 1) public void doAnim() { pacanimcount--; if (pacanimcount <= 0) { pacanimcount = pacanimdelay; pacmananimpos = pacmananimpos + pacanimdir; if (pacmananimpos == (pacmananimcount - 1) || pacmananimpos == 0) pacanimdir = -pacanimdir; } } The doAnim() counts the pacmananimpos variable, which determines what pacman image is drawn. There are four pacman images. There is also a pacanimdelay variable, which makes the animation a bit slower. Otherwise the pacman would open his mouth too fast. boolean finished = true; while (i < nrofblocks * nrofblocks && finished) { if ((screendata[i] & 16) != 0) finished = false; i++; } This code is part of the checkMaze() method. It checks, if there are any points left for the Pacman to eat. Number 16 stands for a point. If all points are consumed, we move to the next level. Next we will examine the moveGhosts() method. The ghosts move one square and then decide, if they change the direction. if (ghostx[i] % blocksize == 0 && ghosty[i] % blocksize == 0) { Continue only if you have finished moving one square. pos = ghostx[i] / blocksize + nrofblocks * (int)(ghosty[i] / blocksize); This line determines, where the ghost is situated. In which position/square. There are 225 theoretical positions. (A ghost cannot move over walls. ) if ((screendata[pos] & 1) == 0 && ghostdx[i] != 1) { dx[count] = -1; dy[count] = 0; count++; } If there is no obstacle on the left and the ghost is not already moving to the right, the ghost will move to the left. What does this code really mean? If the ghost enters a tunnel, he will continue in the same direction until he is out of the tunnel. Moving of ghosts is partly random. We do not apply this randomness inside long tunnels. The ghost might get stuck there. if (pacmanx > (ghostx[i] - 12) && pacmanx < (ghostx[i] + 12) && pacmany > (ghosty[i] - 12) && pacmany < (ghosty[i] + 12) && ingame) { dying = true; deathcounter = 64; } If there is a collision between ghosts and a pacman, the pacman dies. Next we are going to examine the movePacman() method. The reqdx and reqdy variables are determined in the TAdapter inner class. These variables are controlled with cursor keys. if ((ch & 16) != 0) { screendata[pos] = (short)(ch & 15); score++; } If the pacman moves to a position, where there is a point, we remove it from the maze and increase the score value. if ((pacmandx == -1 && pacmandy == 0 && (ch & 1) != 0) || (pacmandx == 1 && pacmandy == 0 && (ch & 4) != 0) || (pacmandx == 0 && pacmandy == -1 && (ch & 2) != 0) || (pacmandx == 0 && pacmandy == 1 && (ch & 8) != 0)) { pacmandx = 0; pacmandy = 0; } If the pacman cannot move further it his current direction, there is a standstill. public void drawPacMan(Graphics2D g2d) { if (viewdx == -1) drawPacManLeft(g2d); else if (viewdx == 1) drawPacManRight(g2d); else if (viewdy == -1) drawPacManUp(g2d); else drawPacManDown(g2d); } There are four possible directions for a pacman. There are four images for all directions. The images are used to animate pacman opening a closing his mouth. The drawMaze() method draws the maze out of the numbers in the screendata array. Number 1 is a left border, 2 is a top border, 4 is a right border, 8 is a bottom border and 16 is a point. We simply go through all 225 squares int the maze. For example we have 9 in the screendata array. We have the first bit (1) and the fourth bit (8) set. So we draw a bottom and a left border on this particular square. if ((screendata[i] & 1) != 0) // draws left { g2d.drawLine(x, y, x, y + blocksize - 1); } Draw a left border if the first bit of a number is set. PacMan.java package packman; import javax.swing.JFrame; import pacman.Board; public class PacMan extends JFrame { public PacMan() { add(new Board()); setTitle("Pacman"); setDefaultCloseOperation(EXIT_ON_CLOSE); setSize(380, 420); setLocationRelativeTo(null); setVisible(true); } public static void main(String[] args) { new PacMan(); } } This is a PacMan file with a main method. Figure: Pacman