Documente Academic
Documente Profesional
Documente Cultură
Curs 8 Java
Curs 8 Java
Curs 8
FOLOSIREA IMAGINILOR , ANIMATIEI SI SUNETULUI
Animatia in Java se realizeaza prin folosirea AWT - mai precis a unor componente ale
sale . Putem crea animatie si folosind instructiunile de desenare invatate in cursurile
anterioare dar la acest moment vom trece la folosirea unor imagini deja create - sub
forma de fisiere grafice cu diverse extensii .
Avem de facut practic doi pasi conceptuali pentru a realiza o animatie :
-
desenarea
comandarea sistemului de ferestre pentru a afisa desenul realizat anterior
Runnable este o interfata ; ea reprezinta sistemul prin care o clasa poate mosteni
metode pe care altfel nu le-ar fi mostenit de la superclasele sale . Aceste metode pot fi
astfel disponibile oricarei metode care are nevoie de ele . Runnable contine o metode
run() de care avem nevoie pentru a porni un fir de executie si de aceea trebuie
implementata aceasta interfata in cazul nostru .
Thread este o clasa din pachetul java.lang - asa incat nu avem nevoie de o instructiune
import pentru a o utiliza . De obicei obiectul de tip Thread se creaza in metoda start()
si va avea o valoare null pana la crearea efectiva a obiectului - creare care se face tot
in metoda start() . Pentru a rula un fir de executie creat avem nevoie de metoda sa
start() :
obiect_thread.start();
Apelarea acestei metode duce la apelarea metodei run() - mostenita prin interfata
Runnable .
Metoda run() este cea mai importanta a appletului devenit fir de executie . Ea este
folosita pentru a controla secventele animatiei prin stabilirea tuturor operatiilor legate
de desenare si de modificarile intre secventele de animatie .
Dupa definirea in metoda run() a comportamentului necesar firului de executie trebuie
sa definim si metoda stop() - pentru oprirea appletului .
Acest lucru - oprirea - il putem face prin atribuirea valorii null obiectului Thread ; de
fapt acest lucru nu duce la oprirea automata a executie firului dar problema se rezolva
prin metoda run() care va fi creata astfel incat sa permita rularea doar daca obiectul
Thread nu este null .
Pentru a clarifica problematica firelor de executie vom prezenta un applet care creaza
un ceas - o animatie simpla - cu actualizari constante .
In cadrul appletului vom folosi un ciclu while care ar putea fi periculos in conditii
normale : acest ciclu ar monopoliza resursele si practic nu am vedea nimic pe ecran
deoarece Java ar astepta la infinit oprirea ciclului .
Appletul va defini un anumit font pentru afisare si un obiect de tip Date care pastreaza
data curenta . Metodele start() si stop() au rolul de a porni si respectiv opri firul de
executie .
Metoda run() este cea mai importanta - cea care realizeaza de fapt toata animatia .
Aici vom avea si buclucasul ciclu while de mai sus ; primul lucru facut in ciclu este
apelarea repaint() pentru redesenarea ferestrei . Tot aici vom intalni si o noua metoda :
sleep() . Aceasta metoda a clasei Thread determina o pauza in executie . Daca nu s-ar
folosi aceasta metoda appletul ar rula la viteza maxima , lucru care poate sa nu fie
conform cu dorinta programatorului . Instructiunile try si catch vor apare aici pentru
tratarea erorilor si pentru moment le putem ignora .
Metoda paint() a appletului creaza o noua instanta a clasei Date - pentru folosirea
acestei clase trebuie sa o importam , ea fiind inclusa in java.util . De asemenea apare
si metoda toString() a clasei Date , necesara pentru afisarea ca sir a datei si orei . La
fiecare apelare a metodei paint() vom avea astfel un nou obiect Date care va tine ora si
data curente .
Sa vedem in continuare codul sursa al appletului descris anterior :
import java.awt.*;
import java.util.*;
public class CeasDigital extends java.applet.Applet implements Runnable {
Font fontul=new Font ("TimesRoman",Font.BOLD,24);
Date data;
Thread executabil;
public void start() {
if (executabil==null) {
executabil=new Thread(this);
executabil.start();
}
}
public void stop() {
if (executabil!=null) {
executabil=null;
}
}
public void run() {
Thread firExecutie=Thread.currentThread();
while (executabil==firExecutie) {
repaint();
try{
Thread.sleep(1000);
} catch (InterruptedException e) {}
}
}
public void paint(Graphics ecran) {
data=new Date();
ecran.setFont(fontul);
ecran.drawString(""+data.toString(),10,50);
}
}
EFECTUL DE FLICKERING AL ANIMATIE
Acest efect - cu denumirea in limba engleza , traducerea fiind palpaire - este cauzat de
modul de reimprospatare a fiecarui caddru de animatie . Dupa cum am mai spus :
apelul metodei repaint() duce automat la un apel al metodei repaint() .De fapt mai
exista inca o metoda intermadiara pe care Java o foloseste pentru a redesena ecranul
aplicatiei ; metoda update() - care sterge ecranul prin umplerea cu culoarea de fundal a
ferestrei appletului si abia apoi se apeleaza paint() .
Din cauza umplerii ecranului efectuata de metoda update() apare si acest efect de
flickering .
In practica exista doua moduri de a evita acest eveniment suparator :
-
Vom prezenta mai intai prima metoda , suprascrierea lui update() - aceasta fiind cea
mai simpla ; in multe cazuri insa ea nu este aplicabila la programe mai complexe si va
trebui utilizata cea de a doua tehnica de mai sus .
SUPRASCRIEREA METODEI UPDATE()
Implicit aceasta metoda are urmatorul cod sursa :
public void update(Graphics g) {
g.setColor(getBackground());
g.fillRect(0,0,size().width,size().height);
g.setColor(getForeground());
paint(g);
}
Putem alege o versiune de rezolvare a problemei palpairii prin anularea totala a
stergerii ecranului . Aceasta solutie nu poate fi aplicata pe scara larga dar in cazurile in
care se poate aplica este cea mai simpla solutie .
Sa luam ca exemplu codul sursa de mai jos :
import java.awt.*;
public class AlternareCulori extends java.applet.Applet implements Runnable {
Font f=new Font("TimesRoman",Font.BOLD,50);
Color culori[]=new Color[50];
Thread executabil;
public void start() {
if (executabil==null) {
executabil=new Thread(this);
executabil.start();
}
}
public void stop() {
executabil=null;
}
Acum vom opera modificarea metodei update() pentru a reduce acest efect suparator .
Vom inlatura din metoda partea responsabila cu stergerea ecranului , obtinand o
metoda update() cu urmatorul cod sursa :
public void update(Graphics ecran) {
paint(ecran);
}
Cea de a doua metoda de evitare a flickerului este dubla memorare . Aceasta consta in
procesul de a desena un cadru intreg de animatie intr-o zona invizibila inainte de a-l
copia in zona vizibila de pe ecran . Zona invizibila in care lucram se numeste buffer
( sau memorie tampon ) .
Prin aceasta tehnica se creaza practic inca o suprafata de desenare , se fac toate
operatiunile de desenare pe ea dupa care se deseneaza dintr-o data intreaga suprafata
in fereastra principala a appletului - toate acestea in loc sa se deseneze direct in
fereastra appletului , pe rand fiecare element .
Tehnica dublei memorari este destulde performanta , ea reusind sa elimine practic
flickerul dar aduce dezavantajul unei folosiri intensive a memoriei sistemului de
calcul . Pentru a crea un applet bazat pe aceasta tehnica trebuie sa avem o imagine pe
care o desenam in buffer si un context grafic pentru acea imagine . Acestea vor simula
efectul desenarii pe suprafata appletului : contextul grafic ( de fapt o instanta a clasei
Graphics ) care ofera metodele de desen si obiectul Image , care memoreaza ceea ce
se va desena .
Ca sa reusim implementarea corect a tehnicii de mai sus trebuie sa parcurgem patru
etape .
Mai intai imaginea invizibila si contextul grafic trebuie stocate in variabile de instanta
care vor putea apoi fi trimise metodei paint() . Acest lucru se face sintactic la modul
urmator :
Image imagineInvizibila;
Graphics invizibil;
In al doilea rand pe parcursul initializarii appletului vom crea obiecte Image si
Graphics pe care le vom atribui acestor variabile . Metoda createImage() returneaza o
instanta a clasei Image , pe care apoi o putem transmite metodei getGraphics() pentru
a obtine un nou context pentru imaginea respectiva :
imagineInvizibila = createImage(size().width , size().height);
invizibil=imagineInvizibila.getGraphics();
In acest moment , ori de cate ori va trebui sa desenam pe ecran - cu metoda paint() vom desena in contextul grafic invizibil ; ca exemplu , pentru a desena o imagine
numita img la pozitia 100,100 folosim linia de cod :
invizibil.drawImage(img,100,100,this);
In ceea ce priveste cuvantul cheie this folosit aici nu va faceti probleme pentru ceea ce
reprezinta - va fi prezentat mai detaliat in paginile urmatoare .
try {
Thread.sleep(100);
} catch (InterruptedException e) { }
}
}
public void update(Graphics ecran) {
paint(ecran);
}
public void paint(Graphics ecran) {
invizibil.setColor(Color.black);
invizibil.fillRect(0,0,100,100);
invizibil.setColor(Color.white);
invizibil.fillRect(100,0,100,100);
invizibil.setColor(Color.red);
invizibil.fillOval(pozX,5,90,90);
ecran.drawImage(imgInvizibila,0,0,this);
}
public void destroy() {
invizibil.dispose();
}
}
Sa comentam putin programul de mai sus :
-
variabila pozX este folosita pentru a muta cercul dintr-un loc in altul , ea
pastrand coordonatele unde se afla piesa la un moment dat . Valoarea variabilei
se modifica continuu in metoda run() .
primul pas pentru dubla memorare consta in crearea unui obiect Image care sa
pastreze cadrul invizibil pana cand acesta este complet si a unui obiect
Graphics care sa permita desenarea in aceasta zona invizibila . Acest lucru il
fac liniile :
Image imgInvizibila;
Graphics invizibil;
-
metoda paint() este modificata pentru a scrie zona tampon invizibila in locul
obiectului principal Graphics ; practic , doar ultima linie a metodei paint()
afiseaza in fereastra appletului . Aceasta instructiune afiseaza cadrul de
animatie complet la coordonatele 0,0 . Deoarece obiectul imgInvizibil a fost
creat de aceeasi dimensiune ca zona de ecran el va acoperi complet fereastra
appletului .
Obiectul URL este creat prin transmiterea adresei paginii web ca argument pentru
metoda constructor a clasei URL , ca in exemplul de mai jos :
URL u=new URL (http://www.site.com/imagini/imagine1.gif);
Dupa ce am creat obiectul URL il putem folosi pentru a crea un obiect Image care
reprezinta propriu-zis fisierul grafic .
Pentru a incarca o imagine noua intr-un obiect Image clasa Applet contine o metoda
numita getImage() , care poate fi folosita in doua moduri :
-
Ultima metoda este putin mai complicata dar ofera o mai mare flexibilitate .
Clasa Applet poseda doua metode care pot fi folosite pentru a crea o adresa URL de
baza fara a folosi in program o adresa fixa explicita ( lucru care ar face ca la orice
modificare a adresei necesitate de applet sa fie necesara si o recompilare a appletului )
:
-
Daca fisierul grafic trebuie afisat la o alta scara decat originalul trebuie sa folosim
sase argumente pentru metoda drawImage() :
-
Scalarea imaginii are efect doar pentru afisarea in applet , obiectul propriu-zis nefiind
alterat de aceste apeluri de metoda .
Pentru a afla dimensiunile unei imagini afisate avem la dipsozitie metodele
getHeight() si getWidth() care returneaza inaltimea si respectiv latimea imaginii
afisate .
Ultiimul argument al metodei drawImage este cuvantul cheie this - element folosit in
general intr-un obiect pentru a face o referinta la el insusi .
Folosirea sa in acest context este necesara pentru a identifica un applet care poate
urmari incarcarea imaginii de pe web . Incarcarea imaginii este urmarita prin
intermediul unei interfete ImageObserver . Clasele care implementeaza aceasta
interfata - printre care si Applet - pot observa gradul de incarcare al unei imagini .
Acest lucru poate fi folositor de exemplu pentru un program care sa afiseze altceva in
timpul incarcarii unor imagini ( procese care uneori pot dura destul de mult ) .
In continuare vom prezenta un exemplu de afisare a unor imagini la scara originala si
cu dimensiuni marite :
import java.awt.*;
public class Imagine extends java.applet.Applet {
Image poza;
public void init() {
poza=getImage(getCodeBase(),"poza1.gif");
}
public void paint(Graphics ecran) {
int latime=poza.getWidth(this);
int inaltime=poza.getHeight(this);
int xPoz=10;
setBackground(Color.pink);
ecran.drawImage(poza,10,10,latime,inaltime,this);
xPoz+=latime+5;
ecran.drawImage(poza,xPoz,10,latime*4,inaltime*4,this);
}
}
Instructiunea de mai jos incarca si reda un fisier audio numit sunet.wav , aflat in
acelasi director cu appletul :
play(getCodeBase(),"sunet.wav");
Metoda play() incarca si reda sunetul cat mai repede posibil . In cazul in care fisierul
de sunet nu este disponibil la adresa servita metodei play() nu vom obtine nici un
mesaj de eroare - pur si simplu nu se va auzi nici un sunet !
Avem posibilitatea de a reda continuu un sunet , de a-l porni si a-l opri la dorinta .
Acesta lucru se poate face incarcand fisierul audio intr-un obiect AudioClip , folosind
metoda getAudioClip a acestuia . Clasa AudioClip este inclusa in pachetul java.awt .
Metoda getAudioClip() primeste unul sau doua argumente . Primul argument ( care
poate fi si unic ) este un obiect URL care identifica fisierul de sunet iar al doilea poate
fi o referinta la cale .
In exemplul de mai jos putem vedea cum se incarca un fisier audio - "sunet.wav" ,
aflat intr subdirectorul "audio" al appletului - intr-un obiect AudioClip :
AudioClip clip=getAudioClip(getCodeBase(),"audio/sunet.wav");
Metoda getAudioClip() poate fi apelata numai in cadrul unui applet . Pentru
incarcarea unui fisier audio intr-o aplicatie independenta Java trebuie sa folosim
metoda newAudioClip() a clasei Applet :
AudioClip clip=newAudioClip("audio/sunet.wav");
Odata creat obiectul AudioClip putem apela si metodele play() , stop() sau loop() ale
acestuia . Play() reda sunetul , stop() opreste redarea sunetului iar loop() provoaca
redarea continuu a fisierului audio .
Spre deosebire de apelarea simpla play() pentru un anumit fisier de sunet ( caz in care
inexistenta fisierului audio nu provoaca nici o eroare ) folosirea metodelor
getAudioClip() sau newAudioClip() poate duce la erori in cazul in care fisierul de
sunet indicat de argumente nu exista ; acest lucru se datoreaza faptului ca obiectul
AudioClip creat de noi va avea valoarea null , iar redarea unui obiect null produce o
eroare .
In cazul in care vrem sa redam mai multe sunete simultan nu exista nici o problema vom folosi mai multe fire de executie .
Trebuie mentionata si o problema - in cazul in care utilizam o redare continuua a unui
sunet in appletul nostru oprirea firului de executie al appletului nu va duce si la
oprirea automata a sunetului . In practica daca un utilizator va trece in alta pagina web
sunetul va continua sa se auda ! Rezolvarea acestei probleme se face prin utilizarea
metodei stop() pentru sunetul redat in acelasi timp cu oprirea firului de executie al
appletului .
In continuare avem un exemplu care reda continuu un sunet - sunet1.wav - si o data la
fiecare 5 secunde reda si fisierul sunet2.wav :
import java.awt.*;
import java.applet.*;
public class CicluAudio extends java.applet.Applet implements Runnable {
AudioClip sunetFundal;
AudioClip bip;
Thread executabil;
public void start() {
if (executabil==null) {
executabil=new Thread(this);
executabil.start();
}
}
public void stop() {
if (executabil!=null) {
if (sunetFundal!=null)
sunetFundal.stop();
executabil=null;
}
}
public void init() {
sunetFundal=getAudioClip(getCodeBase(),"sunet1.wav");
bip=getAudioClip(getCodeBase(),"sunet2.wav");
}
public void run() {
if (sunetFundal!=null) sunetFundal.loop();
Thread firExecutie=Thread.currentThread();
while (executabil==firExecutie) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {}
if (bip!=null) bip.play();
}
}
public void paint(Graphics ecran) {
ecran.drawString("Se aude sunet !",50,50);
}
}