Εφαρμογή Υπολογισμού Φιλοδωρήματος : προσθήκη λειτουργικότητας Όταν δημιουργούμε μία εφαρμογή package com.example.expert.mytipcalculator; import android.support.v7.app.appcompatactivity; import android.os.bundle; public class MainActivity extends AppCompatActivity { @Override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); Η κλάση AppCompatActivity (Activity) του πακέτου android.support.v7.app παρέχει τις βασικές μεθόδους κύκλου ζωής μίας εφαρμογής Η κλάση Bundle του πακέτου android.os παριστά τις πληροφορίες κατάστασης μιας εφαρμογής. Το Android δίνει σε μία εφαρμογή την ευκαιρία να αποθηκεύσει την κατάστασή της, πριν εμφανιστεί μία άλλη εφαρμογή στην οθόνη. Οι προτάσεις package και import 1. // MainActivity.java 2. // Υπολογισμός 3. package com.example.expert.mytipcalculator; 4. 5. import android.app.activity; 6. import android.os.bundle; //για αποθήκευση πληροφοριών κατάστασης 7. import android.text.editable; // για χειρισμό συμβάντων του EditText 8. import android.text.textwatcher; // ακροατής του EditText 9. import android.widget.edittext; // για εισαγωγή ποσού λογαριασμού 10. import android.widget.seekbar; //για αλλαγή ποσοστού προσαρμοσμένου 11. import android.widget.seekbar.onseekbarchangelistener; // ακροατής SeekBar 12. import android.widget.textview; // για εμφάνιση κειμένου 13. 14. import java.text.numberformat; // για μορφοποίηση νομισματικής μονάδας 15. Οι γραμμές 5-14 εισάγουν (import) τις κλάσεις και διεπαφές που χρησιμοποιεί η εφαρμογή Η κλάση NumberFormat του πακέτου java.text (γραμμή 14) παρέχει δυνατότητα αριθμητικής μορφοποίησης
Η κλάση Activity του πακέτου android.app (γραμμή 5) παρέχει τις βασικές μεθόδους κύκλου ζωής μίας εφαρμογής. Η κλάση Bundle του πακέτου android.os (γραμμή 7) παριστά τις πληροφορίες κατάστασης μιας εφαρμογής. Το Android δίνει σε μία εφαρμογή την ευκαιρία να αποθηκεύσει την κατάστασή της, πριν εμφανιστεί μία άλλη εφαρμογή στην οθόνη. Η διεπαφή Editable του πακέτου android.text (γραμμή 8) επιτρέπει να τροποποιήσουμε το περιεχόμενο και τη σήμανση κειμένου μέσα σε μία ΓΔΧ Υλοποιούμε τη διεπαφή TextWatcher του πακέτου android.text (γραμμή 9) για να αποκρίνεται η εφαρμογή σε συμβάντα όταν ο χρήστης αλλάξει κείμενο μέσα σε ένα EditText Το πακέτο android.widget (γραμμές 10-13) περιέχει τα widgets (προβολές) και τις διατάξεις που χρησιμοποιούνται μέσα στη ΓΔΧ (EditText, SeekBar, TextView) Υλοποιούμε τη διεπαφή SeekBar.OnSeekBarChangeListener του πακέτου android.widget ( γραμμή 13) για να ανταποκρίνεται η εφαρμογή στη μετακίνηση του δείκτη του SeekBar από τον χρήστη. Activity της Εφαρμογής MyTipCalculator και ο κύκλος ζωής της Η κλάση MainActivity είναι η υποκλάση Activity της εφαρμογής MyTipCalculator 16. // Κλάση MainActivity της εφαρμογής MyTipCalculator 17. public class MainActivity extends Activity 18. { Οι μεταβλητές της κλάσης MainActivity δηλώνονται στις γραμμές 20-32 19. // μορφοποιήσες νομισματικής μονάδας και ποσοστού 20. private static final NumberFormat currencyformat = 21. NumberFormat.getCurrencyInstance(); 22. private static final NumberFormat percentformat = 23. NumberFormat.getPercentInstance(); 24. 25. private double billamount = 0.0; // Ποσόν του λογαρισμού που εισάγει ο χρήστης 26. private double custompercent = 0.18; // αρχικό προσαρμοσμένο ποσοστό 27. private TextView amountdisplaytextview; // εμφανίζει το μορφοποιημένο ποσόν του λογαριασμού 28. private TextView percentcustomtextview; // εμφανίζει το προσαρμοσμένο ποσοστό 29. private TextView tip15textview; // εμφανίζει το φιλοδώρημα 15% 30. private TextView total15textview; // εμφανίζει το σύνολο με 15% φιλοδώρημα 31. private TextView tipcustomtextview; // εμφανίζει το ποσόν του προσαρμοσμένου 32. private TextView totalcustomtextview; // δείχνει το συνολικό ποσόν με προσαρμοσμένο φιλοδώρημα 33. Η static μέθοδος getcurrencyinstance του NumberFormat επιστρέφει ένα αντικείμενο NumberFormat που μορφοποιεί τιμές ως νομισματικές μονάδες χρησιμοποιώντας τις προεπιλεγμένες τοπικές ρυθμίσεις της συσκευής. Αντίστοιχα, η static μέθοδος getpercentinstance μορφοποιεί τιμές ως ποσοστά χρησιμοποιώντας τις προεπιλεγμένες τοπικές ρυθμίσεις της συσκευής. Το ποσόν λογαριασμού που εισάγει ο χρήστης στο amounttextedit διαβάζεται και αποθηκεύεται ως double μέσα στην billamount (γραμμή 25). Το προσαρμοσμένο
ποσοστό που θέτει ο χρήστης χρησιμοποιώντας το SeekBar (ακέραιος μεταξύ 0 και 30), πολλαπλασιάζεται επί 0.01 και δημιουργεί ένα double ο οποίος χρησιμοποιείται σε υπολογισμούς και κατόπιν αποθηκεύεται στο custompercent (γραμμή 26). Η γραμμή 27 δηλώνει το TextView που εμφανίζει το μορφοποιημένο σαν νομισματική μονάδα ποσό λογαριασμού. Η γραμμή 28 εμφανίζει το προσαρμοσμένο ποσόν. Οι γραμμές 29-32 αναφέρονται στα TextView μέσα στα οποία η εφαρμογή εμφανίζει το υπολογισμένο φιλοδώρημα και τα σύνολα. Υπερκάλυψη της μεθόδου oncreate της κλάσης Activity 34. // καλείται όταν η εφαρμογή εκκινεί γαι πρώτη φορά 35. @Override 36. protected void oncreate(bundle savedinstancestate) 37. { 38. super.oncreate(savedinstancestate); // κλήση της εκδοχής της υπερκλάσης 39. setcontentview(r.layout.activity_main); // διόγκωση της GUI 40. 41. // παίρνει αναφορές προς τα TextView 42. // με τα οποία η MainActivity αλληλεπιδρά προγραμματιστικά 43. amountdisplaytextview = 44. (TextView) findviewbyid(r.id.amountdisplaytextview); 45. percentcustomtextview = 46. (TextView) findviewbyid(r.id.percentcustomtextview); 47. tip15textview = (TextView) findviewbyid(r.id.tip15textview); 48. total15textview = (TextView) findviewbyid(r.id.total15textview); 49. tipcustomtextview = (TextView)findViewById(R.id.tipCustomTextView); 50. totalcustomtextview = 51. (TextView) findviewbyid(r.id.totalcustomtextview); 52. 53. // ενημερώνει τη ΓΔΧ με βάση τις billamount και custompercent 54. amountdisplaytextview.settext( 55. currencyformat.format(billamount)); 56. updatestandard(); // ενημερώνει τα TextViewsτου 15% 57. updatecustom(); // ενημερώνει τα TextViews προσαρμοσμένου 58. 59. // θέτει το TextWatcher του amountedittext 60. EditText amountedittext = 61. (EditText) findviewbyid(r.id.amountedittext); 62. amountedittext.addtextchangedlistener(amountedittextwatcher); 63. 64. // θέτει το OnSeekBarChangeListener του customtipseekbar 65. SeekBar customtipseekbar = 66. (SeekBar) findviewbyid(r.id.customtipseekbar); 67. customtipseekbar.setonseekbarchangelistener(customseekbarlistener); 68. } // τέλος της oncreate 69. Παράμετρος Bundle της oncreate Οταν το σύστημα καλεί την oncreate, αυτή μεταβιβάζει ένα όρισμα Bundle, που περιέχει την αποθηκευμένη κατάσταση της Activity, αν υπάρχει. Τυπικά αποθηκεύουμε την κατάσταση στις μεθόδους onpause ή onsaveinstance (επόμενες εφαρμογές). Η γραμμή 38 καλεί τη μέθοδο oncreate της υπερκλάσης η οποία απαιτείται όταν υπερκαλύπτουμε την oncreate. Η παραγόμενη κλάση R περιέχει ID πόρων Όταν δομούμε την ΓΔΧ της εφαρμογής μας και προσθέτουμε πόρους στην εφαρμογή μας, το IDE παράγει μία κλάση με όνομα R, που περιέχει ένθετες κλάσεις, οι οποίες
παριστούν κάθε τύπο πόρου μέσα στον φάκελο res του έργου. Η κλάση αυτή βρίσκεται στον φάκελο gen που περιέχει αρχεία παραγόμενου κώδικα. Οι ένθετες κλάσεις δηλώνονται static ώστε να μπορούμε να τις προσπελάσουμε μέσα από τον κώδικά μας με τον συμβολισμό R.ΟνομαΚλάσης. Μέσα στις ένθετες κλάσεις της κλάσης R, το IDE δημιουργεί σταθερές static final int, οι οποίες μας επιτρέπουν να αναφερόμαστε στους πόρους της εφαρμογής προγραμματιστικά από τον κώδικα. Ενδεικτικά: Κλάση drawable: Κλάση id: Κλάση layout: Κλάση string: Διόγκωση (inflating) της ΓΔΧ Η κλήση στην SetContentView (γραμμή 39) λαμβάνει την R.layout_activity_main για να δηλώσει ποιο αρχείο XML παριστά την MainActivity. Στην περίπτωσή μας η σταθερά παριστά το αρχείο main.xml. Η μέθοδος SetContentView χρησιμοποιεί αυτή τη σταθερά για να φορτώσει το αντιστοιχο έγγραφο XML, το οποίο κατόπιν αναλύεται και μετατρέπεται στην ΓΔΧ της εφαρμογής. Λήψη αναφορών προς τα widgets Αφού διογκωθεί η διάταξη, μπορούμε να κάνουμε λήψη αναφορών προς τα widgets για να αλληλεπιδράσουμε με αυτά προγραμματιστικά. Αυτό γίνεται με τη μέθοδο findviewbyid της κλάσης Activity: Παίρνει μία ακέραια σταθερά int που παριστά το Id μίας συγκεκριμενης προβολής και επιστρέφει μία αναφορά προς την προβολή. Το όνομα της σταθεράς R.id κάθε προβολής καθορίζεται από την ιδιότητα id του συστατικού που καθορίσαμε όαν σχεδιάσαμε την ΓΔΧ. Γραμμές 43-51: Αναφορές προς τα TextView που αλλάζουν από την εφαρμογή. Γραμμές 43-33: Αναφορά προς amountdisplaytextview Γραμμές 45-46: Αναφορά προς percentcustomtextview Γραμμές 47-51: Αναφορά προς τα TextView όταν εμφανίζονται τα υπολογισμένα φιλοδωρήματα και τα σύνολα. Εμφάνιση αρχικών τιμών μέσα στα TextView Γραμμές 54-55: Θέτουν το κείμενο του amountdisplaytextview στην αρχική billamount (0.0) καλώντας τη μέθοδο format του αντικειμένου currencyformat. Γραμμές 56-57: Καλούν τις μεθόδους updatestandard (γραμμές 70-81 παρακάτω) και updatecustom (γραμμές 82-96 παρακάτω) ώστε να εμφανίσουν τις αρχικές τιμές μέσα στα TextView και συνόλου. Καταχώρηση των ακροατών συμβάντων Γραμμές 60-61: Λαμβάνουν μία αναφορά προς το amountedittext Γραμμή 62: Καλεί τη μέθοδο addtextchangedlistener για να καταχωρήσει τον TextChangedListener που θα ανταποκρίνεται σε συμβάντα που παράγονται όταν ο χρήστης αλλάξει το κείμενο μέσα στην EditText. Ο ακροατής αυτός ορίζεται ως ένα αντικείμενο ανώνυμης εσωτερικής κλάσης, που εκχωρείται στη μεταβλητή στιγμιοτύπου amountedittextwatcher (γραμμές 123-156 παρακάτω) Γραμμές 65-66: Λαμβάνουν αναφορά προς το customtipseekbar Γραμμή 67: Καλεί τη μέθοδο του setonseekbarchangelistener για να καταχωρήσει τον OnSeekBarChangeListener που θα ανταποκρίνεται σε συμβάντα, τα οποία παράγονται όταν χρήστης μετακινεί τον δείκτη του customseekbar. Ο ακροατής αυτός ορίζεται ως
ένα αντικείμενο ανώνυμης εσωτερικής κλάσης, που εκχωρείται στη μεταβλητή στιγμιοτύπου customseekbarlistener (γραμμές 98-120 παρακάτω) Μέθοδος updatestandard της κλάσης Activity 70. // ενημερώνει τα TextView του 15% 71. private void updatestandard() 72. { 73. // υπολογίζει το φιλοδώρημα 15% και το σύνολο 74. double fifteenpercenttip = billamount * 0.15; 75. double fifteenpercenttotal = billamount + fifteenpercenttip; 76. 77. // εμφανίζει το φιλοδώρημα display 15% και το σύνολο μορφοποιημένα ως νομισματικές μονάδες 78. tip15textview.settext(currencyformat.format(fifteenpercenttip)); 79. total15textview.settext(currencyformat.format(fifteenpercenttotal)); 80. } // τέλος μεθόδου updatestandard 81. Γραμμές 78-79: Εμφανίζουν τα ποσά με την τρέχουσα μορφοποίηση Μέθοδος updatecustom της κλάσης Activity 82. // ενημερώνει τα TextViews του προσαρμοσμένου και το σύνολο 83. private void updatecustom() 84. { 85. // εμφανίζει την custompercent μέσα στην percentcustomtextview μορφοποιημένη ως % 86. percentcustomtextview.settext(percentformat.format(custompercent)); 87. 88. // υπολογίζουν το προσραμοσμένο φιλοδώρημα και το σύνολο 89. double customtip = billamount * custompercent; 90. double customtotal = billamount + customtip; 91. 92. // εμφανίζει το προσαρμοσμένο φιλοδώρημα και το σύνολο μορφοποιημένα ως νομισματικές μονάδες 93. tipcustomtextview.settext(currencyformat.format(customtip)); 94. totalcustomtextview.settext(currencyformat.format(customtotal)); 95. } // τέλος της μεθόδου updatecustom 96. Γραμμή 86: θέτει το κείμενο του percentcustomtextview στην τιμή custompercent μορφοποιημένο ως προς το ποσοστό. Γραμμές 89-90: Υπολογίζουν τις customtip και customtotal Γραμμές 93-94: Εμφανίζουν τα ποσοστά σε μορφοποίηση νομισματικής μονάδας Ανώνυμη εσωτερική κλάση που υλοποιεί τη διεπαφή OnSeekBarChangeListener 97. // καλείται όταν ο χρήστης αλλάζει τη θέση του SeekBar 98. private OnSeekBarChangeListener customseekbarlistener = 99. new OnSeekBarChangeListener() 100. { 101. // ενημερώνει το custompercent, και κατόπιν καλεί την updatecustom 102. @Override 103. public void onprogresschanged(seekbar seekbar, int progress, 104. boolean fromuser) 105. {
106. // θέτει την custompercent για να τοποθετήσει τον δείκτη του SeekBar 107. custompercent = progress / 100.0; 108. updatecustom(); // ενημερώνει τα TextViews του προσαρμοσμένου 109. } // τέλος μεθόδου onprogresschanged 110. 111. @Override 112. public void onstarttrackingtouch(seekbar seekbar) 113. { 114. } // τέλος της μεθόδου onstarttrackingtouch 115. 116. @Override 117. public void onstoptrackingtouch(seekbar seekbar) 118. { 119. } // τέλος της μεθόδου onstoptrackingtouch 120. }; // τέλος OnSeekBarChangeListener 121. Υπερκάλυψη της μεθόδου onprogresschanged της διεπαφής OnSeekBarListener Γραμμές 102-119: Υλοποιούν τις μεθόδους της διεπαφής OnSeekBarChangeListener. Η μέθοδος onprogresschanged καλείται όταν αλλάζει η θέση του δείκτη SeekBar. Γραμμή 107: Υπολογίζει την custompercent χρησιμοποιώντας την παράμετρο progress της μεθόδου μία ακέραια int παράμετρο που παριστά τη θέση του δείκτη του SeekBar. Τη διαιρούμε δια 100.0 για βρούμε το προσαρμοσμένο ποσοστό. Γραμμή 108: Καλεί τη μέθοδο updatecustom ώστε να υπολογίσει εκ νέου και να εμφανίσει το προσαρμοσμένο φιλοδώρημα και το σύνολο. Υπερκάλυψη των μεθόδων onstarttrackingtouch και onstoptrackingtouch της διεπαφής OnSeekBarChangeListener Γραμμές 111-119: Παρέχουμε ένα κενό σώμα γι αυτές τις μεθόδους για να ικανοποιήσουμε τις απαιτήσεις της διεπαφής διότι η εφαρμογή δεν χρειάζεται να γνωρίζει πότε ο χρήστης αρχίζει να μετακινεί το δείκτη του αυξομοιωτή (onstarttrackingtouch) ή σταματά να τον μετακινεί (onstoptrackingtouch). Ανώνυμη εσωτερική κλάση που υλοποιεί τη διεπαφή TextWatcher 122. // αντικείμενο χειρισμού συμβάντος που αποκρίνεται σε συμβάντα του amountedittext 123. private TextWatcher amountedittextwatcher = new TextWatcher() 124. { 125. // καλείται όταν ο χρήστης εισάγει έναν αριθμό 126. @Override 127. public void ontextchanged(charsequence s, int start, 128. int before, int count) 129. { 130. // μετατρέπει το κείμενο του amountedittext σε double 131. Try 132. { 133. billamount = Double.parseDouble(s.toString()) / 100.0; 134. } // τέλος try 135. catch (NumberFormatException e) 136. { 137. billamount = 0.0; // προεπιλογή αν συμβεί μία εξαίρεση 138. } // τέλος catch 139. 140. // εμφανίζει το ποσόν του λογαριασμού μορφοποιημένο ως νομσιματική μονάδα 141. amountdisplaytextview.settext(currencyformat.format(billamount)); 142. updatestandard(); // ενημερώνει τα TextViews 15%
143. updatecustom(); // ενημερώνει τα TextViews προσαρμοσμένου 144. } // τελος μεθόδου ontextchanged 145. 146. @Override 147. public void aftertextchanged(editable s) 148. { 149. } // τέλος μεθόδου aftertextchanged 150. 151. @Override 152. public void beforetextchanged(charsequence s, int start, int count, 153. int after) 154. { } // τέλος μεθόδου beforetextchanged }; // τέλος amountedittextwatcher } // τέλος κλάσης MainActivity Γραμμές 123-156: Δημιουργούν το αντικείμενο της ανώνυμης εσωτερικής κλάσης amountedittextwatcher που αποκρίνεται στα συμβάντα του amountedittext. (H γραμμή 62 καταχωρεί αυτό το αντικείμενο ώστε να κάνει ακρόαση για συμβάντα του amounttextedit που προκύπτουν όταν αλλάζει το κείμενο. Υπερκάλυψη της μεθόδου ontextchanged της διεπαφής TextWatcher Γραμμές 126-144: Η μέθοδος ontextchanged καλείται όποτε τροποποιείται το κείμενο μέσα στο amounedittext. H μέθοδος δέχεται 4 παραμέτρους. Στο παράδειγμά μας χρησιμοποιούμε μόνο την CharSequence s, η οποία περιέχει ένα αντίγραφο του κειμένου του amountedittext. Οι άλλες παράμετροι δηλώνουν ότι οι χαρακτήρες με πλήθος count, αρχίζοντας από τη θέση start, αντικαθιστούν το προηγούμενο κείμενο μήκους before. Γραμμή 133: Μετατρέπει την είσοδο του χρήστη από amountedittext σε double. (ο χρήστης επιτρέπεται να εισάγει μόνο ακεραίους). Γραμμές 142-143: Καλούν τις updatestandard και updatecustom ώστε να υπολογίσουν εκ νέου και να εμφανίσουν τα φιλοδωρήματα και τα σύνολα. Άλλες μέθοδοι της amounttextwatcher TextWatcher 146-155: Αυτή η εφαρμογή δεν χρειάζεται να γνωρίζει ποιες αλλαγές πρόκειται να γίνουν στο κείμενο (beforetextchanged) ή ότι το κείμενο έχει αλλάξει ήδη (aftertextchanged) οπότε απλώς υπερκαλύπτουμε αυτές τις μεθόδους της διεπαφής TextWatcher με ένα κενό σώμα AndroidManifest.xml Το αρχεί0 AndroidManifest.xml δημιουργείται από το IDE όταν δημιουργούμε ένα νέο έργο εφαρμογής. Περιέχει πολλές από τις ρυθμίσεις που καθορίζουμε μέσα στο παράθυρο διαλόγου New Android Application, όπως το όνομα της εφαρμογής, το όνομα του πακέτου, το SDK-στόχο, του ελάχιστο SDK κ.α. Τροποποιούμε το αρχείο AndroidManifest.xml για να καθορίσουμε ότι η Activity αυτής της εφαρμογής υποστηρίζει μόνο τον κατακόρυφο προσανατολισμό μίας συσκευής και ότι το λογισμικό πληκτρολόγιο παραμένει πάντα επάνω στην οθόνη.