Download games

Document related concepts
no text concepts found
Transcript
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