Ș.l. dr. ing. Adina Cocu [607178]
Universitatea “Dunărea de Jos” din Galați
Facultatea de Automatică, Calculatoare, Inginerie Electrică si Electronică
Specializarea: Calculatoare și Tehnologia Informației
LUCRARE DE LICENȚĂ
Îndrumător științific:
Ș.l. dr. ing. Adina Cocu
Absolvent: [anonimizat]
2016
Universitatea “Dunărea de Jos” din Galați
Facultatea de Automatică, Calculatoare, Inginerie Electrică si Electronică
Specializarea: Calculatoare și Tehnologia Informației
APLICAȚIE ANDROID
MEMORY CARDS
Îndrumător științific:
Ș.l. dr. ing. Adina Cocu
Absolvent: [anonimizat]
2016
REZUMAT
Reținerea unor lec ții sau formule poate fii obositoare și nepl ăcută. Aceast ă
activitate poate fi uneori o povară , astfel dac ă va fi pl ăcut ă, distractiv ă si interactiv ă va fi
mult mai apreciat ă.
Pentru o mai bună o rganizare a datelor, formulelor sau desenelor este indicat să ai
un caiet de notițe , un dosar cu fi șe sau plan șe reprezentative . Acest lucru limiteaz ă
procesul de învățare la c âteva locuri: acas ă sau într-o sal ă de lectur ă.
Tehnica fi șelor de memorare a informa țiilor este folo sită de majoritatea
persoanelor, fiind mai simplist ă. Pentru a l ărgii aria de aplicabili tate a acestui stil de
invătare, am creat o aplica ție asem ănătoare acestei metode , dar mult mai facil ă .
Aplica ția Memory Cards ofer ă oportunitatea utilizatorului s ă rețină lucruri mai
repede într-un mod pl ăcut. Fiind o aplica ție portabil ă, aceasta poate fi accesată oricând și
oriunde pentru re împrosp ătarea cuno ștințelor . Aplica ția ofer ă posibilitatea utilizatorului
să-și creeze singur cardurile de memorie, s ă le modifice sau s ă le gestioneze în pachete.
Aplica ția are un mod specific de învățare, cardurile ap ărand in func ție de c ât de
bine cunoa ște persoana respectiv ă textul de pe ele. Dac ă cardurile vor coincide cu ceea ce
spune utilizatorul, acestea vor ap ărea la sf ârsitul pachetului, iar cele care au nevoie de
mai mult ă repetare vor ap ărea la începutul lui.
CUPRINS
1. Introducere ………………………….. ………………………….. ………………………….. ………………………….. …. 1
1.1 Descrierea proiectului ………………………….. ………………………….. ………………………….. ………….. 1
1.2 Scopul proiectului ………………………….. ………………………….. ………………………….. ………………… 1
1.3 Structura proiectului ………………………….. ………………………….. ………………………….. ……………. 1
1.4 Mediul de dezvoltare ………………………….. ………………………….. ………………………….. …………… 2
2. Sistemul de operare Android ………………………….. ………………………….. ………………………….. ……… 3
2.1 Despre Android ………………………….. ………………………….. ………………………….. …………………… 3
2.2 Arhitetura sistemului Android ………………………….. ………………………….. ………………………….. .. 4
2.3 Componentele aplicației Memory Cards ………………………….. ………………………….. ……………… 6
2.3.1 Activity ………………………….. ………………………….. ………………………….. …………………………. 6
2.3.2 Intent ………………………….. ………………………….. ………………………….. …………………………. 10
2.3.3 Service ………………………….. ………………………….. ………………………….. ……………………….. 10
2.3.4 Structura de clase ………………………….. ………………………….. ………………………….. ………… 11
3. Structura bazei de date ………………………….. ………………………….. ………………………….. ……………. 12
3.1 Tehnologie folosită ………………………….. ………………………….. ………………………….. …………….. 12
3.2 Structura ………………………….. ………………………….. ………………………….. ………………………….. . 12
3.3 Crearea bazei de date ………………………….. ………………………….. ………………………….. ………… 14
3.3.1 Tabelul pachetelor de carduri ………………………….. ………………………….. ……………………. 14
3.3.2 Tabelul cardurilor ………………………….. ………………………….. ………………………….. ………… 16
4. Dezvoltarea aplicației ………………………….. ………………………….. ………………………….. ………………. 19
5. Prezentarea aplicației ………………………….. ………………………….. ………………………….. ………………. 33
5.1 Meniul principal ………………………….. ………………………….. ………………………….. ………………… 33
5.2 Verificarea cunostințelor ………………………….. ………………………….. ………………………….. …….. 34
3.3 Gestionarea pachetelor de carduri ………………………….. ………………………….. …………………… 37
3.4 Ajutor ………………………….. ………………………….. ………………………….. ………………………….. …… 41
6. Testarea aplicației ………………………….. ………………………….. ………………………….. …………………… 42
Cazul 1: ………………………….. ………………………….. ………………………….. ………………………….. ….. 42
Cazul 2: ………………………….. ………………………….. ………………………….. ………………………….. ….. 43
Concluzii ………………………….. ………………………….. ………………………….. ………………………….. ……….. 44
Bibliografie ………………………….. ………………………….. ………………………….. ………………………….. ……. 45
1
1. Introducere
1.1 Descrierea proiectului
Memorarea textelor e ste o activitate destul de grea , nepl ăcută si obositoare
pentru unele persoane, dar usoar ă pentru altele. Acestea recurg la tot felul de tehnici
pentru a retine mai usor. Pentru a veni in ajutorul lor, voi realiza o aplica ție cu ajutorul
căreia memorarea si învățarea vor deveni o activitate distractiv ă si mult mai usoar ă.
Aplica ția va f ii creat ă pentru dispozitivele mobile, la indem âna oricui, acest lucru f ăcând
aceast ă activitate posibila oriunde si oric ând.
1.2 Scopul proiectului
Scopul proiectului este acela de a ajuta persoanele s ă rețină lucruri c ât mai rapid .
Foarte multe dintre aceste a folosind deja una dintre cele mai populare tehnici de
învățare: folosirea fi șelor de învățare.
Cum func ționeaz ă aceast ă tehnic ă? Se ia câteva foi de h ârtie : pe o parte a
acestora se scrie întrebarea , iar r ăspunsul pe cealalt ă parte. Folosirea acestor fișe de
învățare permite doritorilor de studiu să combine cele două metode de maximă eficiență:
testarea și repetarea. Atunci când persoana care învață știe răspu nsul la întrebarea de pe
o fișă , aceasta va ap ărea la sf ârșitul pac hetului .
1.3 Structura proiectului
Proiectul este structurat in 3 p ărți cu func ționalt ăți diferite. Cele 3 arii de
dezvoltare a aplica ției sunt:
• Interfa ța aplica ției – interfa ța aplica ției este una simpla si usor de urm ărit, fără
multe meniuri ascunse, v iu colorat ă si interactiv ă.
• Stocarea datelor – pentru stocarea datelor s-a folosit o baz ă de date, SQLite.
Aceast a stocheaz ă cărțile si pachetele create de utilizator.
• Realizarea aplica ției ca un joc – acest lucru va captiva utiliz atorul , il va determina
să isi depășeasca scorul precedent și sa isi duc ă scopul l a bun sfârșit.
2
1.4 Mediul de dezvoltare
Pentru realizarea programului am folosit ca IDE, Android Studio. Acesta este un
mediu de dezoltare nou ap ărut pentru crearea aplica țiilor mobile, înlocuind Eclipse ADT
devenind primul IDE pentru dezvoltarea aplica țiilor Android.
3
2. Sistemul de operare Android
2.1 Despre Android
La început, telefoanele mobile a u avut sisteme de operare propr i, sisteme care
aveau nevoie de unelte de dezvoltare specifice. Telefoanele deseori d ădeau prioritate
aplica țiilor native in defavoarea aplica țiilor scrise de diver și dezvoltatori.
În android aplica țiile native si cele create de dezvoltatori sunt scrise folosind
aceleasi API -uri și sunt executate în acelasi timp. Aceste API -uri ofer ă acces la hardware -ul
telefonului cat și la elementele stocate in memoria lui. Android are API-uri puternice,
excelent documentate, o comunitate de dezvoltatori in continu ă expansiune și fără
costuri in ceea ce prive ște dezvoltarea și distribu ția aplica țiilor.
După cum dispozitivele mobile continu ă să creasc ă in popularitate, Android ofer ă
o op ortunitate interesant ă pentru realizarea de aplicatii inovative pentru telefoane,
indiferent de experien ța dezvo ltatorului.
Android se situeaz ă pe primul loc în sisteme de operare mobile create special
pentru hardware. Windows Mobile și Apple iPhone asigur ă un mediu de dezvoltare mai
bogat și mai simplu pentru aplica țiile mobile , dar spre deosebire de Andr oid, sunt
construite pe sisteme de operare proprietare care în unele cazuri proritizeaz ă aplica țiile
native în detriment ul celor create de utilizatori, restric ționeaz ă transferul de date între
aplica ți terțe si aplica țiile native ale telefonului și restric ționeaz ă sau controleaz ă
distribu ția aplica țiilor c ătre platforme.
Android ofer ă noi posibilit ăți pentru aplica țiile mobile deoarece ofer ă un mediu de
dezvoltare deschis , construit peste un kernel Linux open source. Accesul la hardware ne
este disponibil pentru toate aplica țiile prin intermediul unor libr ării de API -uri, iar
interac țiunea dintre aplica ții, deși este contolat ă cu grij ă, este suportat ă în întregime.
În Android, toate aplica țiile au un statut egal. Aplica țiile ter țe si cele native sunt
scrise folosind accelea și API-uri si sunt executate în ac elași mediu de execu ție. Utilizatorii
pot scoate și înlocui orice aplica ție nativ ă cu o alternativ ă oferit ă de către dezvoltatori,
chiar tastatura si ecranul de start put ând fi înlocuite.
4
În ultimul timp, tot mai multe companii au optat ca sistem de oper are pentru
device -urile proprii sistemul de operare Android. Acesta, la început a fost doar pentru
telefoanele mobile, iar mai apoi a fost folosit la televizoare, automobile si ceasuri.
2.2 Arhitetura sist emului Android
Android este alc ătuit din straturi care comunic ă între ele. Exista 5 straturi
principale: Linux Kernel, Biblioteci, Motorul Android, Cadrul pentru aplica ții si Aplica țiile.
Cele 5 straturi sunt str âns legate între ele, fiecare strat folosind serviciile aduse de stratul
de sub acesta.
Fig.2.1 Arhitectura sistemului Android [2]
5
Kernel Linux – este primul strat al sistemului de operare, acesta este rezervat sistemului
și nu poate fi utilizat de programatori sau utilizator.
Bibliotecile – reprezint ă al doilea strat, acestea sunt scrise in C sau C++ și sunt compilate
pentru un anumit hardware. Aici sunt bibliotecile pentru gestiun ea bazelor de date
(SQLite), redarea filmelor, afi șarea pozelor.
Motorul Android – conține ma șina virtuala Dalvik si bibliotecile java. Aplicațiile Android
rulează pe mașina virtuală Dalvik, programele sunt scrise în Java și
compilate în bytecode. Fișierele .class sunt transfo rmate în fișiere
executabile Dalvik “.dex”. Toate acestea, pe ntru că aplicațiile ruleaza
pe un sistem cu memorie destul de limitat și cu o putere de procesare
mai mica.
Cadrul de aplicatii – este partea cu care lucreaz ă direct programatorul. Aceasta este
preinstalat ă in Android, însă servi ciile se pot extinde și se pot crea
propriile componente.
Cele mai importante componente:
-Activity Manager: controleaz a ciclu l de via ța al aplica țiilor și stiva de navigare a
utilizatorului .
-Content Providers: con ține datele ce sunt împ arțite între apli cații (ex: contactele
din telefon) .
-Location Manager: acces la GPS .
-Notification Manager: alerte trimise utilizatorului c ă ceva s -a întamplat în
background .
Aplica țiile – reprezinta totalitatea de aplica ții ce le folose ște utilizatorul .
6
2.3 Componentele aplica ției Memory Cards
2.3.1 Activity
„Reprezint ă o interfa ță cu utilizatorul, o fereastr ă. O aplica ție Android poate avea
una sau mai multe activita ți. De exemplu o aplica ție de tip agend ă poate avea o activitate
pentru a gestiona contactele, o activitate pentru a gestiona întalniri si una pentru a edita
o intrare în agend ă.
Fiecare Activitate are propriul s ău ciclu de viață, independent de ciclul de via ță al
procesului asociat aplica ției.
Fiecare activitate are propria stare si datele acesteia pot fi salvate sau restaurate.
Activit ățile pot fi pornite de aplica ții diferite (dacă este permis). Are un ciclu de via ță
comp lex deoarece aplica țiile pot avea activit ăți multiple și doar una este în prim -plan.
Utiliz ând managerul de activit ăți, sistemul Android gestioneaz ă o stiv ă de activit ăți care se
găsesc în diferite st ări (porni te, în execu ție, întrerupt ă, oprit ă, distrus ă);
În SDK, activitatea este implementat ă folosind o subclasa a clasei Activity care extinde
clasa Context.
Ciclul de via ță al unei activit ăți
Descrie starea în care o activitate poate fi la un moment dat:
Running -Activitatea a fost creat ă (onCreate()),por nită (onStart()) și este afisat ă pe ecranul
dispozitivului . În cazul în care activitatea a mai fost utilizat ă si aplica ția a salvat starea
acesteia (onSaveInstanceState()), activitatea este reluata din acel punct
(onRestoreInstanceState() si on Resume()). În aceasta stare utilizatorul interactioneaz ă cu
activitatea prin intermediul interfetei dispozitivului (tastatur ă, touchscreen, display).
Paused -Activitatea pierde prim -planul (onPause()), deoarece o alt ă activitate este
executat ă (de exemplu o fereastr ă de dialog ). De asemenea, în cazul în care aparatul
intră în modul sleep, activitatea este oprit ă temporar. Activitatea își poate relua executia
(onResume()) și este plasat ă înapoi în prim -plan.
7
Stopped -Activitatea nu mai este în uz și pentru c ă este oprit ă (onStop()) nu este
vizibil ă. Pentru a fi reactivat ă (ea deja exist ând), activitatea trebuie s ă fie repornit ă
(onRestart() si onStart()) si reluat ă (onResume()).
Destroyed -Activitatea este distrus ă (onDestroy()) și memoria s -a eliberat, deoare ce nu
mai este necesar ă, sistemul are nevoie de memorie suplimentar ă pentru rutinele proprii
sau pentru alte activit ăți. Deoarece managementul memoriei este un aspect important
pentru sistemul de operare Linuxal dispozitivului mobil, procesul care g ăzduies te o
activitate întrerupt ă, oprit ă sau distrus ă poate fi terminat pentru eliberarea memorie
pentru noi activit ăți, doar procesele ce gestioneaz ă activit ăți sunt protejate.
Activitatea are mai multe st ări între care exist ă tranzi ții clare. În ciuda faptului că lucrurile
pot ar ăta complicat , în realitate ele sunt mult mai simple dac ă ne concentr ăm pe
urmatoarele elemente:
• o singur ă activitate poate fi în prim -plan la un moment dat.
• doar sistemul gestioneaz ă stările și tranzi țiile unei activit ăti și nu program atorul
(nu în mod direct, deoarece atunci c ând se lanseaz ă o activitate noua se modific ă
implicit starea activit ății curente) .
• sistemul va anun ța atunci cand activitatea își schimb ă starea prin intermediul
handler -elor(metode de forma onXXX()) pentru evanimentele de tip tranzitie.
Programatorul poate ad ăuga propriul cod pentru supradefinirea acestor metode .
8
Fig.2 .2 Ciclul de viată al unei activități [3]
onCreate(Bundle) – când activitatea este creat ă folosind argumentul metodei de tip
Bundle exist ă posibilitatea de a fi restabilită starea activit ății care a fost salvat ă într-o
sesiune anterioar ă. După ce activitatea a fost creat ă, va fi pornit ă (onStart()).
@Override
protected void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState) ;
setContentView(R.layout. memory_card );
La crearea clasei, se apeleaz ă metoda onCreate. Aceasta restabileste starea anterioar ă
apoi seteaz ă layout -ul corespunz ător clasei.
9
onStart() – este apelat ă în cazul în care activitatea urmeaz ă să fie afisat ă. Din acest punct ,
activitatea poate veni în prim plan (onResume()) sau poate r ămane ascuns ă în fundal
(onStop()).
onRestoreInstanceState(Bundle ) – este apelet ă în cazul în care activitatea este initializat ă
cu datele dintr -o stare anterioar ă ce a fost salvat ă. În mod implicit, sistemul restaureaz ă
starea interfe ței cu utilizatorul (starea controalelor vizuale, pozi ția cursorului, etc).
onResume() – apelat ă cand activitatea este vizibil ă iar utilizatorul poate interac ționa cu
aceasta. Din aceast ă stare, activitatea poate fi plasat ă in fundal, devenind intrerup tă
(onPause).
onRestart () – este apelat ă în cazul în care activitatea revine în prim -plan dintr -o stare
oprit ă (stopped), dupa aceast ă activitatea este pornit ă (onStart()) din nou.
onPaused() – este apelat ă atunci c ând sistemul aduce în prim -plan o alta activitate.
Activitatea curent ă este mutat ă în fundal și mai tarziu poate fi oprit ă (onStop() sau
repornit ă și afisat ă (onResume ()). Acest a este un moment bun pentru a salva datele
aplica ției într-un mediu de stocare persistent (fisiere, baze de date) deoarece dupa
aceast ă faza de activitate poate fi terminat ă și distrus ă fară a se anun ța acest lucru.
onSaveInstanceState(Bundle) – este apelat ă pentru a salva starea curent ă a activit ății. În
mod implicit , sistemul salveaz ă starea interfe ței cu utilizator ul.
@Override
protected void onSaveInstanceState (Bundle outState) {
super.onSaveInstanceState(outS tate);
outState.putSerializable(CardsTable. KEY_DECKID , mDeckId);
outState.putSerializable(CardsTable. KEY_ROWID , mRowId);
}
Aceast ă metod ă este apelat ă înainte ca activitatea s ă fie distrus ă, asta pentru a se putea
întoarce la ea in viitor și să restituie starea.
10
onStop() – este apelat ă în cazul în care activitatea nu mai este utilizat ă și nu mai este
vizibil ă deoarece o alt ă activitate in teractioneaz ă cu utilizatorul . Din acest punct,
activitatea poate fi repornit ă (onRestart()) sau distrus ă (onDestroy()).
onDestroy() – este apelat ă în cazul în care activitatea este distr usă , iar memoria sa
eliberat ă. Acest lucru se p oate întampla în cazul în care sistemul necesit ă mai multa
memorie sau dac ă programatorul termin ă explicit activitatea apel ând metoda finish() din
clasa Activity. Deoarece tranzi țiile dintre straturi sunt descrise prin apeluri c ătre diferite
metode de tipul onXXX(), ciclul de via ță al unei activit ăți poate fi descris de succesiunea
posibil ă a acestor apeluri. ”[6]
2.3.2 Intent
Intent -urile sunt un mecanism universal în Android, ele fiind folosite pentru a
porni Activity -uri, servicii și pentru a emite broadcast -uri. Este un mesaj asincron utilizat
pentru a activa activit ăți sau servicii și este gestionat ă de o instan ță a clasei Intent.
2.3.3 Service
• Un task care se executa in fundal, far ă interac țiunea direct ă cu utilizatorul;
• Gestionat ă de o instan ță a clasei Service;
• Content provider (Furnizor sau manager de continut);
• Un API folosit pentru a gestiona datele private ale aplica ției;
• Un sistem de management de date ce descrie o alternativ ă la sistemul de fisiere,
baze de date SQLite sau orice alta solu ție de stocare persistent ă;
• Implementat ă de o subclasa a clasei ContentProvider, o solu ție pentru a partaja și
controla (pe baza de permisiuni) transferul de date între aplica ții (de exemplu ,
sistemul Android ofera un furnizor de con ținut pentru datele de contact).
11
2.3.4 Structura de clase
Aplica ția es te alcatuit ă din 15 clase. Acestea sunt repartizate în pa chete în felul
următor:
Fig.2.3 Structura claselor
Pachetul Decks contine toate clasele folosite pentru modificarea pachetelor de
carduri și a cardurilor.
Pachetul MemoryCards con ține clasa principal ă, prima care apare la deschiderea
aplica ției.
Pachetul Play con ține clasele pentru verificarea cunostin țelor.
Pachetul Tables con ține clasele pentru cele doua tabele din baza de date .
Ultimul pachet, Util con ține clasele ajutatoare, cele care nu au layout -uri. Acestea
sunt pentru crearea bazei de da te, desenarea statisticilor, sortarea interog ărilor și efectul
de rota ție a cardurilor.
12
3. Structura bazei de date
3.1 Tehnologie folosit ă
Pentru stocarea datelor s-a folosit o baz ă de date local ă SQLite. Dacă bazele de
date normale sunt proiectate pentru a gestiona un volum foarte mare de informație,
SQLite are o altă abordare, fiind o bază de date open -source, folosită cel mai des pentru a
gestiona informația la nivel local.
La fel cum indică și numele, SQLite folosește Structured Query Language pentru a
facilita accesul la datele stocate și a fost proiectată în special pentru a fi rapidă, cu
dimensiuni reduse și ușor de folosit.
Pentru a proteja datele, Android restricționează accesul la aceste baze de date,
făcându -le direct accesibile doar aplicației care le -a creat. Astfel, putem avea oricâte baze
de date cu oricâte tabele fiecare, ele fiind accesibile claselor din aplicație direct prin
intermediul numelor lor.
3.2 Structura
Aplica ția con ține dou ă tabele, una pentru stocarea pachetelor de carduri ( Tab.3.1)
și alta pentru carduri ( Tab.3.2).
DECKS
_id integer primary key
autoincrement
name text not null
card_number text not null
Tab.3.1 Tabelul pachetelor de carduri
Tabelul pentru pache tul de carturi este alc ătuit din 3 coloane:
_id – aceasta este cheia primar ă, este de tip integer și se incrementeaz ă singur ă.
name – este pentru numele pachetelor de carduri.
card_number – conține num ărul de carduri pe c are le con ține pachetul respectiv .
13
CARDS
_id integer primary key
autoincrement
front text not null
back text not null
rating integer
deck_id integer
Tab. 3.2 Tabelul cardurilor
Tabelul cardurilor contine 5 coloane:
_id – cheia primar ă a cardurilor.
Front – textul pe care îl contine cardul pe partea din fat ă, reprezintă întrebarea de pe
aceste a.
back – textul de pe pa rtea din spate a cardului, text pe care utilizatorul trebuie sa il
cunoasc ă ca să primeasc ă 1 punct in joc.
rating – fiecare card are la cre area lui ratingul 0. În fuctie de utilizator, acesta se
incrementeaz ă cu 1 daca utilizatorul stie r ăspunsul de pe card, sau se
decrementeaz ă cu 1 daca acesta nu stie r ăspunsul. În func ție de acest scor,
cardurile cunoscute vor ap ărea la sf ârsitul pachetului iar cele cunoscute vor
apărea la începutul lui.
deck_id – reprezint ă id-ul pachetului din care face parte cardul respectiv.
14
3.3 Crearea bazei de date
3.3.1 Tabelul pachetelor de carduri
Pentru creare a tabelului de pachete s-a folosit sintaxa:
public static void create(SQLiteDatabase db) {
db.execSQL(
"create table " + NAME +" ("
+ KEY_DECKID +" integer primary key autoincrement, "
+ KEY_NUMBER +" text not null, "
+ KEY_NAME +" text not null);"
);
}
NAME – reprezint ă denumirea tabelului
KEY_DECKID – cheia primar ă;
KEY_NUMBER – num ărul de carduri pe care le contine pachetul ;
KEY_NAME – denumirea pachetului. ;
Metod ă pentru crearea unui pachet de carduri în baza de date. Aceasta este
apelat ă după ce se in troduce numele pachetului în interfa ță. Primeste ca argument
numel e pachetului da t de utilizator.
public long createDeck (String name) {
ContentValues initialValues = new ContentValues() ;
initialValues.put( KEY_NAME , name);
initialValues.put( KEY_NUMBER , "0 cards" );
return mDb.insert( NAME, null, initialValues) ;
}
Metod a pentru ștergerea unui pachet va primi ca argument un num ăr ce va
corespunde id -ului pachetului ce se doreste șters. Acest a va fi șters atat din interfa ța
grafic ă cât și din baza de date.
public boolean deleteDeck (long deckId) {
return mDb.delete( NAME, KEY_DECKID + "=" + deckId , null) > 0;
}
15
La ad ăugarea de noi carduri sau la ștergere, num ărul acestora se va schimba.
Pentru a modifica num ărul cardurilor afisat în dreptul fiec ărui pachet de carduri s-a folosit
urmatoarea metod ă. Aceasta primeste ca argumente id -ul pachetului în care s -a efectuat
schimbarea și num ărul cardurilor r ămase în acesta.
public boolean updateNumber (long deckId, String numar) {
ContentValues args = new ContentValues() ;
args.put( KEY_NUMBER , numar);
return mDb.update( NAME, args, KEY_DECKID + "=" + deckId , null) > 0;
}
Pentru redenumirea unui pachet s -a folosit metoda urmatoare cu doi parametri:
id-ul pachetului dorit și numele cu care se doreste schimbat.
public boolean updateDeck (long deckId, String name) {
ContentValues args = new ContentValues() ;
args.put( KEY_NAME , name);
return mDb.update( NAME, args, KEY_DECKID + "=" + deckId , null) > 0;
}
Afișarea tabelelor din baza de date s -a realizat cu o metod ă prin care returneaz ă
toate pachetele g ăsite în tabelul NAME. Acestea vor fi afi șate în ordine cresc ătoare dup ă
denumirea acestora.
public Cursor fetchAllDecks () {
String[] fields = new String[] {
KEY_DECKID ,
KEY_NUMBER ,
KEY_NAME
};
return mDb.query(
NAME, // table
fields, // columns
null, // selection
null, // selection Args
null, // group by
null, // having
KEY_NAME + " asc" // order by
);
}
16
Pentru afi șarea unui tabel din baza de date s -a folosit o me todă care prime ște ca
parametru id-ul pachetu lui și îl returneaz ă pe acesta.
public Cursor fetchDeck (long deckId) throws SQLException {
String[] fields = new String[] {
KEY_DECKID ,
KEY_NUMBER ,
KEY_NAME
};
Cursor mCursor = mDb.query(
true, // distinct
NAME, // table
fields, // columns
KEY_DECKID + "=" + deckId , // selection
null, // selection args
null, // group by
null, // having
null, // order by
null // limit
);
if (mCursor != null) {
mCursor.moveToFirst() ;
}
return mCursor;
}
3.3.2 Tabelul cardurilor
Pentru crearea tabelului de carduri s -a folosit:
public static void create(SQLiteDatabase db) {
db.execSQL(
"create table " +CardsTable. NAME +" ("
+ CardsTable. KEY_ROWID +" integer primary key
autoincrement, "
+ CardsTable. KEY_FRONT +" text not null, "
+ CardsTable. KEY_BACK +" text not null, "
+ CardsTable. KEY_DECKID +" integer, "
+ CardsTable. KEY_RATING +" integer);"
);
}
NAME – reprezint ă nume le tabelului ;
KEY_ROWID – id-ul cardurilor și cheia primar ă;
KEY_FRONT – textul de pe fa ța cardului ;
KEY_BACK – textul de pe spatele cardului ;
KEY_DECKID – num ărul corespunz ător pachtului din care face parte cardul respectiv ;
KEY_RATING – nivelul de cuno ștință a unui card ;
17
Metoda pentru creare a cardului va primi ca parametru dou ă string -uri pentru
textul de pe fetele cardurilor, și un num ăr care va fi id -ul pachetului din care face parte.
public long createCard (String front , String back , long deckId) {
ContentValues initialValues = new ContentValues() ;
initialValues.put(CardsTable. KEY_FRONT , front);
initialValues.put(CardsTable. KEY_BACK , back);
initialValues.put(CardsTable. KEY_DECKID , deckId);
return mDb.insert(CardsT able.NAME, null, initialValues) ;
}
Ștergerea unui card se realizaez ă cu urm ătoarea metoda:
public boolean deleteCard (long rowId) {
return mDb.delete(CardsTable. NAME, CardsTable. KEY_ROWID + "=" +
rowId, null) > 0;
}
Modificarea unui card se realizeaz ă cu urm ătoarea metod ă. Aceasta primeste ca
parametri id -ul cardului modificat, textele de pe fa ță si spatele cardului și id-ul pachetului
din care face parte.
public boolean updateCard (long rowId, String front , String back , long
deckId) {
ContentValues args = new ContentValues() ;
args.put(CardsTable. KEY_FRONT , front);
args.put(CardsTable. KEY_BACK , back);
args.put(CardsTable. KEY_DECKID , deckId);
return mDb.update(CardsTable. NAME, args, CardsTable. KEY_ROWID + "=" +
rowId, null) > 0;
}
Modificarea nive lului de cunoa ștere a unui card se schimb ă dând ca par ametru
noul rating al cardului.
public boolean updateCard (long rowId, int rating) {
ContentValues args = new ContentValues() ;
args.put(CardsTable. KEY_RATING , rating);
return mDb.update(CardsTable. NAME, args, CardsTable. KEY_ROWID + "=" +
rowId, null) > 0;
}
18
Aducerea tutror cardurilor dintr -un anumit pachet se realizeaz ă cu metoda
fetchAllCards. Aceasta returneaz ă o interogare cu toate coloanele tabelului NAME av ând
id-ul primit ca parametru.
public Cursor fetchAllCards (long deckId, String sort) {
String[] fields = new String[] {
CardsTable. KEY_ROWID ,
CardsTable. KEY_FRONT ,
CardsTable. KEY_BACK ,
CardsTable. KEY_DECKID ,
CardsTable. KEY_RATING
};
return mDb.query(
CardsTable. NAME, // table
fields, // columns
CardsTable. KEY_DECKID + "=" +deckId, // selection
null, // selection Arguments
null, // group by
null, // having
sort // order by
);
}
Pentru aducerea din baza de date a unui card, se folosescte metoda urmatoare cu
parametru id-ul cardului.
public Cursor fetchCard (long rowId) throws SQLException {
String[] fields = new String[] {
CardsTable. KEY_ROWID ,
CardsTable. KEY_FRONT ,
CardsTable. KEY_BACK ,
CardsTable. KEY_DECKID ,
CardsTable. KEY_RATING
};
Cursor mCursor = mDb.query(
true, // distinct
CardsTable. NAME, // table
fields, // columns
CardsTable. KEY_ROWID + "=" + rowId, // selection
null, // selection args
null, // group by
null, // having
null, // order by
null // limit
);
if (mCursor != null) {
mCursor.moveToFirst() ;
}
return mCursor;
}
19
4. Dezvoltarea aplica ției
Pentru a putea fi folosite tabelele create anterior, este nevoie de o clasa care
crea ză baza de date.
public class MemoryCardsDbAdapter {
private static final String DATABASE_NAME = "memorycards" ;
private static final int DATABASE_VERSION = 1;
private DatabaseHelper mDbHelper ;
private SQLiteDatabase mDb;
private final Context mCtx;
public CardsTable mCards;
public DecksTable mDecks;
private static class DatabaseHelper extends SQLiteOpenHelper {… }
public MemoryCardsDbAdapter(Context ctx) {
this.mCtx = ctx;}
public MemoryCardsDbAdapter open() throws SQLException {
mDbHelper = new DatabaseHelper( mCtx);
mDb = mDbHelper .getWritableDatabase() ;
mCards = new CardsTable( mDb);
mDecks = new DecksTable( mDb);
return this;
}
}
Clasa MemoryCardsDbAdapter creaz ă baza de date cu ajutorul clasei
DatabaseHelper. Cu ajutorul metodei open() baza de date se deschide.
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) {
super(context , DATABASE_NAME , null, DATABASE_VERSION );
}
@Override
public void onCreate (SQLiteDatabase db) {
CardsTable. create(db);
DecksTable. create(db);
}
@Override
public void onUpgrade (SQLiteDatabase db ,int oldVersion ,int
newVersion) {
if( oldVersion<newVersion ) {
CardsTable. upgrade(db);
DecksTable. upgrade(db);
}
}
}
20
Aplica ția începe cu o activitate ce cuprinde cele 3 butoane de început. Fiecare
dintre ele are un ascult ător de evenimente care face tranzi ția activit ăților c ând sunt
apăsate.
@Override
protected void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState) ;
setContentView(R.layout. memory_card );
mPlay=(ImageButton) findViewById(R.id. imageButtonPlay );
mDecks=(ImageButton) findViewById(R.id. imageButtonDecks );
mHelp=(ImageButton) findViewById(R.id. imageButtonHelp );
mPlay.setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(View v) {
Animation Transition1 = AnimationUtils. loadAnimation (MemoryCard. this,
android.R.anim. fade_in);
mPlay.startAnimation(Transition1) ;
Transition1.setAnimationListener( new Animation. AnimationListener(){
@Override
public void onAnimationStart (Animation a nimation) {
startActivity( new
Intent(getApplicationContext() ,DeckListPlay. class));
}
@Override
public void onAnimationRepeat (Animation animation) {}
@Override
public void onAnimationEnd (Animation animation) {}
});
}
});
În ascult ătorul butonului a fost creat o anima ție care porne ște odat ă cu evenimentul.
Aceast ă anima ție se vede la tranzi ția dintre activit ăți.
21
Deck ListPlay este clasa ce con ține lista de pachete. Aceasta este creat ă
suprascriind metoda onCreate().
@Override
protected void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState) ;
setContentView(R.layout. deck_list_play );
registerForContextMenu(getListView()) ;
mDb = new MemoryCardsDbAdapter( this);
mDb.open();
mSelectedDeckId = -1;
fillData() ;
}
În metoda se seteaza layout -ul creat anterior, se creaz ă o variabil ă pentru
inițializarea clasei MemorCardsDbAdapter și se deschide baza de date cu ajutorul
acesteia. În continuare se ini țializeaz ă variabila care ne arat ă ce pachet a fost selectat cu
-1 pentru a nu fi selectat niciunu l și se apeleaz ă metoda fillData () care încarc ă lista cu ce
găseste în tabelul de pachete.
private void fillData () {
Cursor decksCursor = mDb.mDecks.fetchAllDecks() ;
startManagingCursor(decksCursor) ;
String[] from = new
String[]{DecksTable. KEY_NAME ,DecksTable. KEY_NUMBER };
int[] to = new int[]{R.id. text1,R.id.text2};
SimpleCursorAdapter notes = new SimpleCursorAdapter(
this,
R.layout. decks_list_row ,
decksCursor ,
from,
to
);
setListAdapter(notes) ;
}
Metoda fillData() creaz ă un cursor pe care îl initializeaz ă cu rez ultatele aduse din
tabelul de pachete. Aceasta afiseaz ă denumirea și num ărul de card uri p entru fiecare
pachet în parte.
protected void onListItemClick (ListView l , View v, int position , long id)
{
super.onListItemClick(l , v, position , id);
mSelectedDeckId = id;
Intent intent = new Intent(this,Play.class);
intent.putExtra(CardsTable. KEY_DECKID , mSelectedDeckId );
startActivity(intent) ;
}
22
Metoda onListItemClick se apeleaz ă când se apas ă pe un pachet de carduri. În acel
moment variabila ce salveaz ă id-ul pachetului selectat se initializeaz ă și se deschide clasa
Play. Aici are loc verificarea cu pachetul selectat din list ă.
Ajung ând în clasa Play, la crearea activit ății se efectueaz ă succesiv mai multe opera țiuni,
cum ar fii:
-inițializarea variab ilei cu id -ul pach etului curent;
mDeckId = (savedInstanceState == null) ? null :
(Long) savedInstanceState.getSerializable(CardsTable. KEY_DECKID );
if (mDeckId == null) {
Bundle extras = getIntent().getExtras() ;
if( extras != null && extras.containsKey(CardsTable. KEY_DECKID ) ) {
mDeckId = extras.getLong(CardsTable. KEY_DECKID );
}
}
-deschiderea bazei de date;
mDb = new MemoryCardsDbAdapter( this);
mDb.open();
-inițializarea cursorului cu cardurile aduse din tabelul de carduri. Cursorul caut ă în baza de
date toate cardurile ce au id -ul pachetului e gal cu id -ul pachetului cure nt;
QuerySorter sort = new QuerySorter().asc(CardsTable. KEY_RATING ).rand() ;
mCursor = mDb.mCards.fetchAllCards( mDeckId, sort.toString()) ;
startManagingCursor( mCursor);
if( mCursor.getCount()< 1 ) {
mCursor = null;
findViewById(R.id. frontBackSwitcher ).setVisibility(View. GONE);
findViewById(android.R.id. empty).setVisibility(View. VISIBLE);
return;
}
-apelarea metodelor de afi șare a textelor pe carduri și cea pentru anima țiile acestora dar
și inițializarea v ariabilelor de succes sau e șec;
invalidateFields( INVALIDATE_ALL_FIELDS );
mSuccessCount = 0;
mFailCount = 0;
createSlideAnimations() ;
23
După ce au fost c ăutate toate cardurile în baza de date și au fost sortate, acestea
trebuie s ă fie afi șate. Acest lucru se realizeaz ă prin metoda:
private void invalidateFields (int which) {
if (mCursor != null) {
if( which== INVALIDATE_ALL_FIELDS || which== INVALIDATE_FRONT_FIELDS
) {
mFrontTitle .setText( mCursor.getString(
mCursor.getColumnIndexOrThrow(Cards Table.KEY_FRONT )));
}
if( which== INVALIDATE_ALL_FIELDS || which== INVALIDATE_BACK_FIELDS )
{
mBackTitle .setText( mCursor.getString(
mCursor.getColumnIndexOrThrow(CardsTable. KEY_BACK )));
}
mRating = mCursor.getInt( mCursor.getColumnIndexOrThrow(
CardsTable. KEY_RATING ));
mRowId = mCursor.getLong( mCursor.getColumnIndexOrThrow(
CardsTable. KEY_ROWID ));
}
}
– INVALIDATE_ALL_FIELDS: modific ă toate c âmpurile de pe card.
– INVALIDATE_ FRONT_FIELDS: modific ă doar fa ța cardului.
– INVALIDATE_BACK_FIELDS: modific ă spatele cardului.
După ce apare primul card și s-a citit întrebarea prin ap ăsarea cardului acesta se
roteste pentru a dezv ălui con ținutul. Acest efect se face cu ajutorul unei clase noi
Rotate3dAnimation care con ține metode extinse din Animation.
private void createRotate3dAnimations (){
mSwitcher = (ViewSwitcher) findViewById(R.id. frontBackSwitcher );
float centerX = mSwitcher .getWidth() / 2.0f;
float centerY = mSwitcher .getHeight() / 2.0f;
mRotationAnimIn = new Rotate3dAnimation( 180, 360, centerX, centerY,
310.0f, true);
mRotationAnimIn .setDuration( 500);
mRotationAnimIn .setInterpolator( new AccelerateInterpolator()) ;
mRotationAnimOut = new Rotate3dAnimation( 0, 180, centerX, centerY,
310.0f, false);
mRotationAnimOut .setDuration( 500);
mRotationAnimOut .setInterpolator( new AccelerateInterpolator()) ;
}
24
Pe lânga anima ția de rotire a cardurilor, în aceast ă activitate mai exist ă și o
anima ție de tranzi ție între carduri. Acestea deruleaz ă de la dreapta spre st ânga dupa ce
este ap ăsat unul dintre cele 2 butoane de pe card.
private void createSlideAnimations (){
mSlideAni mIn = AnimationUtils. loadAnimation (this,
android.R.anim. slide_in_left );
mSlideAnimIn .setAnimationListener( new AnimationListener(){
public void onAnimationStart (Animation animation) {
invalidateFields( INVALIDATE_FRONT_FIELDS );
}
public void onAnimationRepeat (Animation animation) {
}
public void onAnimationEnd (Animation animation) {
mSwitcher .setInAnimation( mRotationAnimIn );
}
});
mSlideAnimOut = AnimationUtils. loadAnima tion(this,
android.R.anim. slide_out_right );
mSlideAnimOut .setAnimationListener( new AnimationListener(){
public void onAnimationStart (Animation animation) {
}
public void onAnimationRepeat (Animation animation) {
}
public void onAnimationEnd (Animation animation) {
invalidateFields( INVALIDATE_BACK_FIELDS );
mSwitcher .setOutAnimation( mRotationAnimOut );
}
});
}
După apăsarea butonului de succes apare un nou card, dar nu înain te de a
modifica rating -ul cardului. Daca butonul ap ăsat este cel verde, atunci valoarea rating -ului
va cre ște cu 1, iar dac ă butonul este cel ro șu, valoarea va scade cu 1.
public void onSuccess (View v) {
++mSuccessCount ;
saveState( 1);
onMoveNext() ;
}
public void onFail(View v) {
++mFailCount ;
saveState( -1);
onMoveNext() ;
}
25
Dacă ratingul este între minimul si maximul posibil (acestea fiind -10 si respectiv
10), atunci el se va modifica.
private void saveState (int rating) {
rating = mRating+rating;
if( rating>= MIN_RATING && rating<= MAX_RATING ) {
mDb.mCards.updateCard( mRowId, rating);
}
}
După modificarea rating -ului se trece la urm ătorul card, dac ă acesta exista. Dac ă
nu, se trece la urm ătoarea activitate, cea de rezultat. Aici va ap ărea o diagram ă cu
num ărul de r ăspunsuri corecte si g reșite.
public void onMoveNext () {
if( mCursor.moveToNext() ) {
mSwitcher .setInAnimation( mSlideAnimIn );
mSwitcher .setOutAnimation( mSlideAnimOut );
mSwitcher .showNext() ;
}
else {
Intent intent = new Intent(this,Results. class);
intent.putExtra(Results. SUCCESSES , mSuccessCount );
intent.putExtra(Results. FAILS, mFailCount );
intent.putExtra(DecksTable. KEY_DECKID , mDeckId);
startActivity(intent) ;
finish() ;
}
}
Pentru diagram ă s-a creat o noua clas ă: PieChartView, clasa ce cuprinde mai multe
metode printre care cea de ini țializare a celor dou ă valori și cea de desenare a acesteia.
PieChartView pie = (PieChartView) findViewById(R.id. pieChart );
pie.setValues( mSuccesses , mFails);
TextView txt = (TextView)findViewById(R.id. txtSuccesses );
txt.setText( String. format( getString(R.string. lbl_successes ),
mSuccesses ) );
txt = (TextView)findV iewById(R.id. txtFails );
txt.setText( String. format( getString(R.string. lbl_fails ), mFails) );
26
Metoda setValues atribuie celor doua variab ile num ărul de r ăspunsuri corecte si
num ărul de r ăspunsuri gresite.
Metoda onDraw este rescris ă, ea deseneaz ă diagrama în functie de valorile prel uate prin
metoda precedent ă.
public void setValues ( int successes , int fails) {
mSuccesses = successes ;
mFails = fails;
invalidate() ;
}
@Override
protected void onDraw(Canvas canvas) {
float total = mSuccesses +mFails;
float angle1 = ( mSuccesses *360/total);
canvas.drawArc( mFrame, 0, angle1, true, mFillPaintSuccess );
canvas.drawArc( mFrame, 0, angle1, true, mStrokePaint );
float angle2 = ( mFails*360/total);
canvas.drawArc( mFrame, angle1, angle2, true, mFillPaintFail );
canvas.drawArc( mFrame, angle1, angle2, true, mStrokePaint );
}
Mai exact metoda deseneaz ă doua arcuri de cerc în functie de valoarea variabilelor de
succes sau esec. Dac ă num ărul succesului este mai mare decat cel al esecului, diagrama
va con ține mai mult verde decat rosu.
Metoda onSizeChanged modific ă diagrama în func ție de dimensiunea ecranului.
Acest lucru face ca diagrama s ă își păstreze forma indiferent de rezolu ția pe care este
afisat ă.
@Override
protected void onSizeChanged (int w, int h, int oldw, int oldh) {
mFrame.set(STROKE_WIDTH , STROKE_WIDTH , w-STROKE_WIDTH , h-
STROKE_WIDTH );
}
27
Modificarea unui card sau a unui pachet se face doar in meniul Decks. În prima
fereastr ă avem posibilitatea de a ad ăuga noi pachete sau de a le modifica pe cele
existente.
@Override
public void onCreateContextMenu (ContextMenu menu , View v,
ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu , v, menuInfo) ;
AdapterVi ew.AdapterContextMenuInfo info =
(AdapterView.AdapterContextMenuInfo)menuInfo ;
mSelectedDeckId = info.id; //getSelectedItemId(); ??
menu.setHeaderTitle( R.string. lbl_manage_deck );
menu.add(ContextMenu. NONE, CMNU_EDIT_DECK , ContextMenu. NONE,
R.string. menu_edit_deck );
menu.add(ContextMenu. NONE, CMNU_DELETE_DECK ,ContextMenu. NONE,
R.string. menu_delete_deck );
menu.add(ContextMenu. NONE, CMNU_CREATE_DECK , ContextMenu. NONE,
R.string. menu_create_deck );
}
Aceast ă metod ă este invocat ă când vrem să modific ăm un pachet și ținem ap ăsat pe
acesta. Acest lucru va duce la deschiderea unui meniu cu cele 3 optiuni:
-prima op țiune va modifica numele pachetului ;
-a doua op țiune va șterge pachetul ;
-a treia op țiune va crea un pachet nou ;
În urma alegerii unei op țiuni, trebuie s ă se efectueze aceasta. Pentru acest fapt s -a
folosit urmatoarea metod ă care în functie de alegerea f ăcuta va apela alt ă metod ă.
public boolean onContextItemSelected (MenuItem item) {
int id = item.getItemId() ;
switch( id ) {
case CMNU_EDIT_DECK :
editDeck() ;
return true;
case CMNU_DELETE_DECK :
showDialog( DIALOG_REMOVE_ALL_CARDS );
return true;
case CMNU_CREATE_DECK :
createDeck( null);
return true;
}
return false;
}
28
Metoda de schimbare a denumirii pachetelor va deschide activitatea destinata
acesteia, aceea cu un textfield și 2 butoane de salvat sau renun țat la opera țiune .
private void editDeck () {
if( mSelectedDeckId <0 ) {
alert("Select an item first!" );
return;
}
Intent intent = new Intent(this, DeckEdit. class);
intent.putExtra(DecksTable. KEY_DECKID , mSelectedDeckId );
startActivityForResult(intent ,ACTIVITY_EDIT_DECK );
fillData() ;
}
Metoda de ștergere este apelat ă după apăsarea butonului de ok din fereastra de
alert ă.
public void deleteDeck (boolean delAllCards){
if( mSelectedDeckId <0 ) {
alert("Select an item first!" );
return;
}
mDb.mDecks.deleteDeck( mSelectedDeckId );
fillData() ;
alert("Deck deleted" );
}
Metoda de creare a pachetelor va deschide acea și fereastr ă ca și cea de
modificare, doar ca aceasta nu este destinat ă modificarii.
public void createDeck (View v) {
mSelectedDeckId = -1;
Intent intent = new Intent(this, DeckEdit. class);
startActivityForResult(intent ,ACTIVITY_CREATE_DECK );
}
Metoda alert va ap ărea în majoritatea ac țiunilor, ea afiseaz ă pe ecran cand o
activi tate a fost dus ă la bun sf ârsit.
private void alert(String text) {
Context context = getApplicationContext() ;
int duration = Toast. LENGTH_SHORT ;
Toast toast = Toast. makeText (context , text, duration) ;
toast.setGravity(Gravity. CENTER, 0, 0);
toast.show() ;
}
29
Dacă se dore ște ștergerea unui pachet ce con ține carduri, va ap ărea o fereastr ă de
aten ționare ca în acel pachet exist ă carduri ce vor disparea în urma opera țiunii.
private AlertDialog createDeleteAllCardsDialog () {
AlertDialog.Builder dlg = new AlertDialog.Builder( this);
dlg.setTitle(R.string. lbl_remove_deck );
dlg.setMessage(R.string. lbl_delete_allcards_question );
dlg.setPositiveButton(R.string. lbl_accept , new
DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog , int whichButton) {
deleteDeck( true);
}
});
dlg.setNegativeButton(R.string. lbl_cancel , new
DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog , int whichButton) {
deleteDeck( false);
}
});
return dlg.create() ;
}
Apăsând pe un pachet se va deschide o fereastr ă ce con ține toate cardurile din
pachetul respectiv. Cardurile sunt c ăutate în baza de date dup ă id-ul pachetului și vor f i
afișate în lista în ordine cresc ătoare dup ă textul scris pe prima fa ță a cardului.
private void fillData () {
QuerySorter sort = new QuerySorter().asc(CardsTable. KEY_FRONT );
Cursor notesCursor =
mDb.mCards.fetchAllCards( mDeckId,sort.toString()) ;
startManagingCursor(notesCursor) ;
nr=mDb.mCards.fetchAllCards( mDeckId,sort.toString()).getCount() ;
mDb.mDecks.updateNumber( mDeckId, nr + " cards" );
String[] from = new String[]{CardsTable. KEY_FRONT ,
CardsTable. KEY_BACK };
int[] to = new int[]{R.id. text1, R.id.text2};
SimpleCursorAdapter notes = new SimpleCursorAdapter(
this,
R.layout. card_list_row ,
notesCursor ,
from,
to
);
setListAdapter(notes) ;
}
30
Ca și in lista cu pachete, ținând ap ăsat pe un card va ap ărea un dialog cu 2 op țiuni:
ștergere și creare de nou card.
@Override
public void onCreateContextMenu (ContextMenu menu , View v,
ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu , v, menuInfo) ;
AdapterView.AdapterContextMenuInfo info =
(AdapterView.AdapterContextMenuInfo)menuInfo ;
mSelectedItemId = info.id;
menu.setHeaderTitle(R.string. lbl_card_options );
menu.add(ContextMenu. NONE, CMNU_CREATE_CARD , ContextMenu. NONE,
"Create a new c ard");
menu.add(ContextMenu. NONE, CMNU_DELETE_CARD , ContextMenu. NONE,
"Delete card" );
}
public boolean onContextItemSelected (MenuItem item) {
int id = item.getItemId() ;
switch( id ) {
case CMNU_CREATE_CARD :
createCard( null);
return true;
case CMNU_DELETE_CARD :
removeCard() ;
return true;
}
return false;
}
Apăsând o singur ă dată pe un card, se va deschide activitatea de editare a acestuia.
Acest lucru se face prin trecerea a mai multor metode care într-un final va deschide
activitatea.
@Override
protected void onListItemClick (ListView l , View v, int position , long id)
{
super.onListItemClick(l , v, position , id);
mSelectedItemId = id;
editCard() ;
}
private void editCard () {
Intent intent = new Intent(this, CardEdit. class);
intent.putExtra(CardsTable. KEY_DECKID , mDeckId);
intent.putExtra(CardsTable. KEY_ROWID , mSelectedItemId );
startActivityForResult(intent , ACTIVITY _EDIT_CARD );
}
31
La deschiderea activit ății de editare a cardului selectat se apeleaza metoda de
creare a acesteia:
@Override
protected void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState) ;
mDb = new MemoryCardsDbAdapter( this);
mDb.open();
setContentView(R.layout. card_edit );
setRequestedOrientation(ActivityInfo. SCREEN_ORIENTATION_PORTRAIT );
mFrontTitle = (Button) findViewById(R.id. btnFrontTitle );
mBackTitle = (Button) findViewById(R.id. btnBackTitle );
mDeckId = (savedInstanceState == null) ? null :
(Long)
savedInstanceState.getSerializable(CardsTable. KEY_DECKID );
if (mDeckId == null) {
Bundle extras = getIntent().getExtras() ;
if( extras != null && extras.containsKey(C ardsTable. KEY_DECKID ) )
{
mDeckId = extras.getLong(CardsTable. KEY_DECKID );
}
}
mRowId = (savedInstanceState == null) ? null :
(Long)
savedInstanceState.getSerializable(CardsTable. KEY_ROWID );
if (mRowId == null) {
Bundle extras = getIntent().getExtras() ;
if( extras!= null && extras.containsKey(CardsTable. KEY_ROWID ) )
mRowId = extras.getLong(CardsTable. KEY_ROWID );
}
populateFields() ;
}
Metoda onCreate ini țializeaz ă variabilele pentru id -ul pachetului și id-ul cardului
cu valorile activit ății precedente , dup ă care apeleaz ă metoda pentru punerea textului pe
card.
private void populateFields () {
if (mRowId != null) {
Cursor card = mDb.mCards.fetchCard( mRowId);
mFrontTitle .setText(card.getString(
card.getColumnIndexOrThrow(CardsTable. KEY_FRONT )));
mBackTitle .setText(card.getString(
card.getColumnIndexOrThrow(CardsTable. KEY_BACK )));
}
}
32
Acțiunile pentr u cele 3 butoane de salvare, salvare și creare de card nou,
renun țare sunt împlementate prin urm ătoarele metode:
public void onSaveButton (View v) {
saveState() ;
setResult( RESULT_OK );
finish() ;
}
public void onAddMoreButton (View v) {
saveState() ;
mRowId = null;
clearFields() ;
alert("Card saved" );
}
public void onCancelButton (View v) {
setResult( RESULT_CANCELED );
finish() ;
}
Crearea de carduri noi sau de modificare a celor existente în baza de date s e fac
prin meto da saveState care se apeleaz ă în metodele precedente.
private void saveState () {
if (mRowId == null) {
mRowId = mDb.mCards.createCard(
mFrontTitle .getText().toString() ,
mBackTitle .getText().toString() ,
mDeckId
);
if( mRowId<=0 ) {
mRowId = null;
}
}
else {
mDb.mCards.updateCard(
mRowId.longValue() ,
mFrontTitle .getText().toString (),
mBackTitle .getText().toString() ,
mDeckId.longValue()
);
}
}
33
5. Prezentarea aplica ției
5.1 Meniul principal
Acesta este alc ătuit dintr -o imagine ce con ține numele aplica ției și 3 butoane: Play,
Decks și Help.
Primul buton, Play, ne va trimite la o nou ă fereastr ă ce contine o list ă de pachete
de carduri, pentru a începe verificarea cono ștințelor.
Al doilea buton, Decks, ne trimite la o fereastr ă asem ănătare cu cea de la butonul
precedent, doar c ă aceast a e servit ă pentru a modifica pachetele de carduri.
Ultimul buton, Help, ne afi șează comenzile aplica ției.
Fig.5.4 Meniul principal al aplica ției
34
5.2 Verificarea cunostin țelor
După apăsarea primului buton ne va ap ărea a doua fereastr ă, cea cu pachetele de
carduri. Fiecarui pachet îi sunt afi șate denumirea și num ărul de carduri pe care le con ține.
În exemplul urmator (Fig. 5.5 ) sunt afi șate 3 pachete de carduri fiecare con ținand 0 si 1
card. Aleg ând unul dintre pachete, verificarea va p orni dac ă in pachet sunt cel pu țin 2
carduri, altfel aplica ția va direc ționa utilizatorul spre cre area acestora.
Fig.5.5 Lista pachetelor de carduri
Odat ă început ă, aplica ția va afisa toate cardurile în ordine cresc ătoare a rating -ului
fiecărui card. Dacă un card a fost re ținut și are rating mare, va f i afișat la urm ă. Dac ă
rating -ul este egal la dou ă sau mai multe carduri (eg. începutul verific ării), acestea sunt
afișate al eator.
35
Fig.5.6 Card în verificare
După citirea întreb ării și dup ă ce se d ă răspunsul, se apas ă pe card iar aceasta se va
întoarce. Dac ă sa stiut r ăspunsul se va ap ăsa pe butonul verde iar rating -ul va cre ște cu 1
punct, daca nu , se va ap ăsa pe cel rosu iar rating -ul va sc ădea cu 1 punct. Dup ă apăsarea
unuia dintre cele 2 bu toane , va ap ărea cardul următor .
Fig.5.7 Răspunsul cardului în verificare
36
După afișarea tuturor cardurilor din pachetul respectiv, verificarea se va termina și se va
afișa o fereastr ă care va ar ăta num ărul întreb ărilor știute și cele ne știute.
Fig.5.8 Rezultatul verific ării
În fereastra de rezultate pe l ângă rezultatul afi șat, utilizatorul are doua op țiuni: s ă se
reexamineze cu acela și set de carduri sau s ă se duc ă la meniul principal al aplica ției.
37
3.3 Gestionarea pachetelor de carduri
Acest lucru se realizeaz ă în urma ap ăsării butonului Decks. Acest lucru ne duce la o
fereastr ă în care sunt afisate toate pachetele de carduri. Dac ă nu este creat nici un
pachet, utiliatorul va ave a posibilitatea de a crea unul.
Fig.5.9 Crearea unui pac het
Pentru crearea pachetului, trebuie introdus doar numele acestuia dup ă care se apas ă pe
butonul save, sau pe cancel daca se doreste anularea opera țiunii.
Fig.5.10 Denumirea unui pachet
38
Dacă exist ă pachete în baza de date, acestea vor fi afisate într-o list ă aseman ătoare c u cea
de la verificare, numai c ă aceasta con ține meniuri ascunse și mai multe op țiuni.
Fig.5.11 Lista pachetelor de carduri
Pentru crearea, stergerea unui pachet sau redenumirea lui trebuie accesat meniul ascuns.
Acest lucru se realizeaz ă ținand ap ăsat pe pachetul care se doreste f ăcuta una dintre
aceste opera țiuni.
Fig.5.12 Meniu de modificare a pachetelor
39
Pentru a accesa con ținutul unui pachet trebuie ap ăsat pe acesta. Dacă nu sunt carduri în
el, se po t crea.
Fig.5.13 Crearea unui card
Dacă exist ă carduri, acestea vor fi afi șate într-o lista. Fiecare card din list ă are 2 culori
distincte pe ele. Prima culoare reprezint ă textul de pe o pa rte a cardului, iar a doua
culoare reprezint ă partea opus ă a cardului.
Fig.5.14 Lista de carduri
40
Pentru a crea sau șterge un card, trebuie ap ăsat lung pe unul dintre ele și va ap ărea un
meniu cu aceste op țiuni.
Fig.5.15 Meniu de modificare a cardurilor
Pentru a intra în modul de editare a unui card se apas ă pe card iar fereastra d e editare se
va deschide .
Fig.5.16 Fereastra de editare a cardurilor
41
Acest meniu conține un card și 4 butoane. C ând este ap ăsat primul buton, FLIP CARD,
cardul se va întoarce și va ar ăta continutul de pe spate. Butonul SAVE salveaz ă cardul si
iese din meniul de editare, butonul SAVE&ADD NEW salveaz ă cardul și va crea unul nou
gol gata de editat. Butonul CANCEL va anula orice opera țiune f ăcuta. Pentru a modifica
textul de pe card este nevoi e doar de o atingere pe acesta.
Fig.5.17 Dialog de editare a textului
3.4 Ajutor
Fereastra de ajutor con ține câteva optiuni pe care il pot ajuta pe cel ce foloseste aplica ția
dacă acesta nu își dă seama cum func ționeaz ă aplica ția.
Fig.5.18 Fereastra de ajutor
42
6. Testarea aplica ției
Testatea aplica ției s-a facut manual pe 2 cazuri generale: unul de creare și unul de
functionare a verific ării.
Cazul 1:
În primul caz s-a creat un pachet de carduri. Dup ă creare acesta trebuie s ă indice
că conține 0 carduri . Pe urm ă s-a deschis pachetul și s-a creat un card nou , acesta trebuie
să fie afisat în lista de carduri. Ultimul pas este verificarea pachetului ca acesta s ă indice 1
card.
Fig.6.19 Primul caz de testare
43
Cazul 2:
În pachetul de test au fost create 3 carduri. Dup ă ce se selectez ă pachetul va începe
verificarea. Cardurile vor ap ărea în ordine aleatoare deoarece gradul de cunostin ță al fiec ăruia
este 0. Se va bifa corect cardul cu num ărul 3 și 1, iar cardul 2 va fi bifat greșit. În urma verific ării se
afișează statis tica cu 2 carduri corecte și unul gre șit.
Se va ap ăsa pe butonul retry și se va face o reexaminare. Cum cardul 2 a fost gre șit, el va
apărea primul iar cardurile 1 și 3 vor ap ărea dup ă acesta random . Se vor bifa corect toat e
cardurile iar rezultatul va f i corect pentru toate cele 3 carduri .
Fig.6. 20 Al doilea caz de testare
44
Concluzii
Aplica ția Memory Cards conține o baz ă de date suficient de mare pentru a stoca
oricâte carduri dore ște utilizatorul s ă creeze. Acestea pot fi create , modificate și șterse
după bunul plac al acestuia cu foarte mare u șurită datorit ă interfe ței grafice simpliste și
intuitive.
Crearea acestei metode pentru dispozitivele mobile face ca aplica ția să fie folosit ă
atât în mediu de lucru obi șnuit unde se poate folosi și metoda de învățat tradi țional ă dar
și în alte medi i.
Mediul de dezvoltare al aplica țiilor în Android : Android Studio, este foarte facil
atât pentru persoanele începătoare cât și pentu p rofesioniști. Sugestiile pe care acesta le
oferă programatorului cât și viteza de procesare al mașinei virtuale oferă un mediu ideal
de programare.
În concluzie aplica ția poate deveni fo arte util ă pentru toate persoanele ce doresc
portabilitate și interac tivitate a unui lucru pu țin pl ăcut dar necesar de care to ți vom avea
nevoie la un moment dat.
45
Bibliografie
[1] https://developer.android.com/training/basics/data -storage/databases.html
[2] http://ocw.cs.pub.ro/courses/eim/laboratoare/laborator01
[3] http://cs.curs.pub.ro/wiki/si/lab/2012/android2
[4] http://ctrl -d.ro/tips -and-tricks/hello -android -elementele -ce-dau-viata -unei-aplicatii/
[5] https://developer.android.com/reference/android/app/Activity.html
[6] http://www.itcsolutions.eu/2011/09/08/android -tutorial -concepte -activitati -si-resurse -ale-unei-
aplicatii -android
Copyright Notice
© Licențiada.org respectă drepturile de proprietate intelectuală și așteaptă ca toți utilizatorii să facă același lucru. Dacă consideri că un conținut de pe site încalcă drepturile tale de autor, te rugăm să trimiți o notificare DMCA.
Acest articol: Ș.l. dr. ing. Adina Cocu [607178] (ID: 607178)
Dacă considerați că acest conținut vă încalcă drepturile de autor, vă rugăm să depuneți o cerere pe pagina noastră Copyright Takedown.
