‏הצגת רשומות עם תוויות UI. הצג את כל הרשומות
‏הצגת רשומות עם תוויות UI. הצג את כל הרשומות

יום ראשון, אוקטובר 31

ווידג'טים 2: רשימה (ListView), וזה אפילו מדבר.


הכרנו שימוש בכפתורים וקלט\פלט של טקסט. כעת נכיר את ה-ListView המשמש להצגת רשימות. בהמשך נמשיך להכיר ווידג'טים נוספים. בתרגיל הזה נשלב גם TTS- Text To Speech. למרות הכלל הידוע לפיו יש להתמקד כל פעם בנושא חדש אחד, עברתי על הכלל כדי לגוון ולהוסיף עוד עניין. וגם כדי לראות שזה לא הרבה יותר מסובך מה- Android Inventor, שעליו עוד אכתוב, אבל רק אחרי שאקבל גישה אליו ואוכל לבדוק אותו מעבר לתעוד שפורסם. בכל מקרה, לפ הבנתי יש ל-Inventor מספר מגבלות, כולל אי יכולת לטפל במספר תצוגות באפליקציה אחת. ה-work around לכך הוא להפעיל אפליקציה אחרת לצורך החלפת התצוגה. אין אפשרות לייצר קוד ולשלב אותו באפליקציה או להכניס בו שינויים. אני בהחלט סקרן לבדוק את זה...בינתיים חזרה ל-ListView.
מטרה התרגיל: הצגת רשימת פריטים על המסך בעזרת ListView. בחירת אלמנט מהרשימה תגרום להשמעתו, תוך שימוש ביכולות Text To Speech הנתמכות החל מגרסה 1.5.
הנה התמונה של ה-UI בתרגיל זה:



השימוש ברשימה: השימוש ברשימות נעשה בעזרת רכיב מתאם - Adapter. ה-Adapter הוא רכיב המחבר בין אלמנט מסוג view, למשל ListView בו נשתמש כעת או ScrollView בו נשתמש בפוסט הבא, לבין רשימת הנתונים.
המחלקה class של אנדרואיד אותה נירש היא ListActivity. ה-ListActivity היא "בן" או Subclass של Activity עם התאמה לטיפול ברשימות, כך שהיא יודעת לטפל בארועים של בחירת אלמנט ברשימה על ידי המשתמש, כפי שנראה מיד.
אלמנט נוסף שנכיר הוא הממשק המתאם בין  ListView לבין רשימת הנתונים עצמה - ה - ListAdapter.

ניתן להוריד את כל חבילת הקוד ע"י לחצה על הקישור. לשם נוחות  myListActivity מוצג כאן בשלמותו. בהמשך נתעכב על החלקים המעניינים.


  1.  
  2. public class myListActivity extends ListActivity {
  3.     String[] listItems={"ikea", "porche", "b.m.w bmw", "volkswagen", "ronen",
  4.     "nike", "skype", "feijoa", "three thirty", "mushroom",
  5.     "android", "text to speech", "hizbulla", "hidden", "vladimir",
  6.     "munsento", "position"};
  7.     @Override
  8.     public void onCreate(Bundle icicle) {
  9.         super.onCreate(icicle);
  10.         setContentView(R.layout.main);
  11.         setListAdapter(new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,listItems));
  12.         initTextToSpeech();
  13.     }
  14.     public void onListItemClick(ListView parent, View v, int position,long id) {
  15.         speak(listItems[position]);
  16.     }
  17.     private static int TTS_DATA_CHECK = 1;
  18.     private TextToSpeech tts = null;
  19.     private boolean ttsIsInit = false;
  20.     private void initTextToSpeech() {
  21.         Intent intent = new Intent(Engine.ACTION_CHECK_TTS_DATA);
  22.         startActivityForResult(intent, TTS_DATA_CHECK);
  23.     }
  24.  
  25.     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  26.         if (requestCode == TTS_DATA_CHECK) {
  27.             if (resultCode != Engine.CHECK_VOICE_DATA_PASS) {
  28.                 Intent installVoice = new Intent(Engine.ACTION_INSTALL_TTS_DATA);
  29.                 startActivity(installVoice);
  30.             }
  31.             configTextToSpeech();
  32.         }
  33.     }
  34.     private void configTextToSpeech(){
  35.         tts = new TextToSpeech(this, new OnInitListener() {
  36.             public void onInit(int status) {
  37.                 if (status == TextToSpeech.SUCCESS) {
  38.                     ttsIsInit = true;
  39.                     if (tts.isLanguageAvailable(Locale.ENGLISH) >= 0)
  40.                         tts.setLanguage(Locale.ENGLISH);
  41.                     tts.setPitch(1);
  42.                     tts.setSpeechRate(1);
  43.                 }
  44.             }
  45.         });
  46.     }
  47.     private void speak(String inputString) {
  48.         if (tts != null && ttsIsInit) { 
  49.             tts.speak(inputString, TextToSpeech.QUEUE_ADD, null);
  50.         }
  51.     }
  52.    
  53.        
  54.     @Override
  55.     public void onDestroy() {
  56.         if (tts != null) {
  57.             tts.stop();
  58.             tts.shutdown();
  59.         }
  60.         super.onDestroy();
  61.     }
  62. }

    המתודות הממומשות ב- myListActivity הן:
    1.  onCreate - כרגיל נקראת עם יצירת האובייקט. אחראית לכל האיתחולים.
    2. onListItemClick - זהו callback של ListActivity שמופעל כשיש קליק על אלמנט ברשימה.
    3.  initTextToSpeech - מתודה לאיתחול ה TTS. מופעלת מתוך onCreate
    4. onActivityResult - זהו callback שמטפל בתשובה ל-Intent שנשלח לאובייקט ה-TTS. 
    5. configTextToSpeeh - יצירת האובייקט של ה-TTS, כולל קביעת פרמטרים של שפה ו- pitch.
    6. speak - המתודה שמבצעת את ה- TTS. מופעלת מתוך ה- onListItemClick.
    7. onDestroy - נקראת בסוף חיי האובייקט. נועדה לנקות את המערכת משאריות פעולת ה- TTS.

    נתחיל עם onCreate:
    1.     @Override
    2.     public void onCreate(Bundle icicle) {
    3.         super.onCreate(icicle);
    4.         setContentView(R.layout.main);
    5.         setListAdapter(new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,listItems));
    6.         initTextToSpeech();
    7.     }
     שורה 5: חיבור בין רשימת הdata, מערך בשם listItems, לבין אובייקט ה-ListView. המתודה
    setListAdapter מעבירה כפרמטר את המתאם של ListView. ה-Interface  מסוג ArrayAdapter נוצר.
    הפרמטרים ל-ArrayAdapter הם:
    context - ראה this.
    layout - במקרה הזה לא הגדרנו את ה-layout של הרשימה בקובץ xml והשתמשנו באלמנט אנדרואיד מוכן המייצג רשימה אנכית: simple_list_item_1.
    data array: מערך המחרוזות listItems.


    שורה 7 מפעילה מתודת השירות initTextToSpeech המאתחלת את ה- TTS - Text To Speech. נגיע למתודה זו בהמשך.
    onListItemClick: זוהי מתודה של ה- ListActivity  class. היא מופעלת ע"י הקלקה על פריט ברשימה.
    במקרה שלנו, היא מבצעת קריאה למתודה speak שמקבלת String כפרמטר ומפעילה את ה- TTS.

    initTextToSpeech: 
    שולח intent למודול ה- TTS כדי לבדוק אם ה- TTS מותקן במערכת.



    1.     private void initTextToSpeech() {
    2.         Intent intent = new Intent(Engine.ACTION_CHECK_TTS_DATA);
    3.         startActivityForResult(intent, TTS_DATA_CHECK);
    4.     }
    onActivityResult - זהו כאמור ה- callback שמטפל בתשובה של ה- TTS. אם עפ"י התשובה ה- TTS לא מותקן, הוא יותקן כעת ע"י שליחת Intent:

    1. protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    2.         if (requestCode == TTS_DATA_CHECK) {
    3.             if (resultCode != Engine.CHECK_VOICE_DATA_PASS) {
    4.                 Intent installVoice = new Intent(Engine.ACTION_INSTALL_TTS_DATA);
    5.                 startActivity(installVoice);
    6.             }
    7.             configTextToSpeech();
    8.         }
    9.     }
    שורה 2 בודקת שהתשובה קשורה ל-Intent שלנו.
    שורה 3 בודקת אם התשובה חיובית. אם אינה כזו, מתקינים את המודול ע"י שליחת Intent מתאים - שורות 4-6.
    שורה 7: קריאה למתודה שתקנפג את ה-tts - ראה בהמך.

    configTextToSpeeh : יצירת אובייקט tts והכנסת פרמטרים לאיתחול:


    1.     private void configTextToSpeech(){
    2.         tts = new TextToSpeech(this, new OnInitListener() {
    3.             public void onInit(int status) {
    4.                 if (status == TextToSpeech.SUCCESS) {
    5.                     ttsIsInit = true;
    6.                     if (tts.isLanguageAvailable(Locale.ENGLISH) >= 0)
    7.                         tts.setLanguage(Locale.ENGLISH);
    8.                     tts.setPitch(1);
    9.                     tts.setSpeechRate(1);
    10.                 }
    11.             }
    12.         });
    13.     }
     שורה 2: קריאה ל- constructor של TextToSpeech.הפרמטר שהוא מקבל הם:
    1. ה- context בו נשתמש - במקרה הנ"ל זה ה-Activity הנוכחי.
    2. onInitListener - ה-callback שיופעל כשה- TextToSpeech יסיים האיתחול ויהיה מוכן.
     המשך בשורה 3: ה-onInitListener נוצר בצורה "אנונימית",ומוגדרת המתודה onInit שתופעל עם כשה-TTS יהיה מוכן.
    שורה 6: בודקים האם השפה האנגלית נתמכת. במקרה שלנו התשובה תהיה או 0 שמשמעותה LANG_AVAILABLE או במקרה הפחות טוב,
    1- = LANG_MISSING_DATA או 2- =LANG_NOT_SUPPORTED.
    אם התשובה חיובית, נקנפג את השפה לאנגלית,  pitch =1 (זה תדר הדיבור הבסיסי) ו- קצב דיבור = 1. אפשר לשחק עם נפרמטרים האלה ולבחון את השפעתם על הדיבור המתקבל.

    speak: ראינו שהמתודה speak נקראת מתוך onClick.

    1.    private void speak(String inputString) {
    2.         if (tts != null && ttsIsInit) { 
    3.             tts.speak(inputString, TextToSpeech.QUEUE_ADD, null);
    4.         }
    5.     }

      שורה 2: בדיקת תקינות: tts קיים ומאותחל.
      שורה 3: הפעלת המתודה speak של tts שמבצעת את הדיבור. היא מקבלת 3 פרמטרים:
      • ה-input string
      • queueMode - קביעת אופן פעולת התור אליו שולחים את הנתונים להשמעה. במקרה שלנו בחרנו להוסיף את הנתונים לתור הקיים. האפשרות השניה - QUEUE_FLUSH - היא למחוק את כל הממתינים להשמעה ולדחוף את הקלט החדש שלנו במקום.
      • רשימה של פרמטרים לשימוש או null אם אין תוספת של פרמטרים. הפרמטרים ניתנים בצורת זוגות (key, value), ויכולים להיות   KEY_PARAM_STREAM או KEY_PARAM_UTTERANCE_ID. הראשון משמש להכוונת הדיבור ל-stream אחר והשני מאפשר לקנפג String שיוכל להיות trigger ל-callbak . אבל לא נרחיב על אלה עכשיו, אלא בפוסט מיוחד ל-TTS.
      onDestroy - כבר תארנו את מחזור חיי ה-Activity. המתודה onDestroy תסגור ותחזיר למערכת משאבים שנלקחו עבור ה-TTS.


      לפני סיום נציץ רגע לקובץ main.xml הלו הוא קובץ ה-layout:
      1. <?xml version="1.0" encoding="utf-8"?>
      2. <LinearLayout
      3. xmlns:android="http://schemas.android.com/apk/res/android"
      4. android:orientation="vertical"
      5. android:layout_width="fill_parent"
      6. android:layout_height="fill_parent" >
      7. <ListView
      8. android:id="@android:id/list"
      9. android:layout_width="fill_parent"
      10. android:layout_height="fill_parent"
      11. android:drawSelectorOnTop="false"
      12. />
      13. </LinearLayout>


      שים לב לשורה 8: ה-id של ה ListView מוגדר כ list. חובה להשתמש בשם זה אם ה-Activity הוא מסוג ListActivity - כמו במקרה שלנו. ה-ListActivity מניחה ש-list הוא השם שניתן לאלמנט.

      נסכם את הפוסט הזה: המטרה עיקרית היתה הכרות עם ה-class מסוג ListActivity. בדרך חיברנו לו גם יכולות TTS.



      יום שני, אוקטובר 18

      מושגי יסוד של אנדרואיד

      מספר מושגי יסוד של האנדרואיד: 
      גם אם בקריאה ראשונה ההגדרות עלולות להראות אבסטרקטיות מדי, אין ספק שבהמשך הן תתבהרנה.
      1. Activity
      2. Intent
      3. Service
       Activity - כל תצוגה על ממשק המשתמש (UI = User Interface) היא Activity. האפליקציה יכולה לכלול סדרה של תצוגות, שכל אחת מהן היא היא אקטיביטי בפני עצמה. כל Activity אחראית לשמור את הנתונים הקשורים אליה, כך שאם היא תופסק ותופעל מחדש, התצוגה תשתחזר.  הרחבה על היכולת לשחזר אינפורמציה בפרק שדן ב מחזור חיי האפליקציה.


      Intent - זהו המנגנון באמצעותו מועברות הודעות במערכת. בעזרת מנגנון זה ניתן ליצור קשר עם אלמנטים בתוך האפליקציה ומחוצה לה, וכך לבצע פעולות כמו:   מעבר בין מסכי התצוגה שנעשה ע"י שליחת Intent להפעלת  Activity  הרצוי; העברת נתונים בין Activities; גישה לשרותים של מערכת האנדרואיד, למשל שליחת אימייל; יציאת קשר עם שרותים אחרים ע"ע Service כאן בהמשך; גישה למודולים של גורם שלישי הקיימים על הפלטפורמה, העברת הודעות Broadcast במערכת. קיימים שני טיפוסים של Intent - מפורש ומרומז.   ב-Intent מפורש (Explicit) השולח מציין במפורש את שם יעד ההודעה. במקרה של אינטנט מרומז (Implicit) השולח לא מציין את יעד ההודעה, אבל לפי הפרמטרים שבה, ההודעה מכוונת ע"י ה- Intent Filter ליעד המתאים. מנגנון זה מאפשר קוד גמיש בו אין צורך לדעת מראש את זהות היעד של הודעה.
      באחד השיעורים הבאים נלמד את המנגנון לעומק.

      Service-   פעילות שרצה ברקע, ללא אינטראקציה ישירה עם המשתמש, כלומר בלי UI. ה- Service יכול לקבל פקודות מ-Activities - שהוא כאמור בעל ממשק למשתמש - באמצעות  Intent. ה- Service מתאים לפעילויות רקע כגון נגן, טיימר וכדומה.