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

יום רביעי, נובמבר 3

ווידג'טים 3: Spinner


הכרנו כבר כמה מחלקות (classes) של widget, נזכיר את Button, EditText, TextView, ListView. נכיר עכשיו ווידג'ט חשוב נוסף: Spinner המוכר לפעמים במקומות אחרים כ-pull-down או תפריט גלילה. התפריט הזה מאפשר בחירה ע"י לחיצה וגלגול.
נבצע ונסביר תרגיל הכולל spinner. מטרת התרגיל: בחירה של שיר מתוך רשימת שירים.
הקלק על הלינק להורדת קבצי המקור. 
נתחיל מהסוף: ה-UI מוצג בתמונה הבאה:


בתצוגה הנ"ל מופיעים 4 אלמנטים (מלמעלה למטה):
  1. TextView עם הכותרת של ה-spinner
  2. ה-spinner
  3. כפתור להחלת הבחירה של ה-spinner (זאת העדפה שלי, אפשר לבצע בחירה ב-spinner גם ללא התניה בכפתור נוסף)
  4. TextView להצגת הבחירה.
כעת כשמטרת התרגיל ידועה, נסקור את קבצי הקוד.
נתחיל עם ה-layout:
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3.     android:orientation="vertical"
  4.     android:layout_width="fill_parent"
  5.     android:layout_height="fill_parent"
  6.     >
  7.     <TextView android:id="@+id/spinnerTitle"
  8.                 android:layout_width="wrap_content"
  9.                 android:layout_height="wrap_content"
  10.                 android:text="@string/spinner_title"
  11.                 android:layout_marginTop="30px" android:layout_marginBottom="20px" 
  12.                 android:layout_marginLeft="40px"
  13.     />
  14.        <Spinner    android:id="@+id/songs_spinner"
  15.                 android:layout_width="fill_parent"
  16.                 android:layout_height="wrap_content"
  17.                 android:layout_marginLeft="20px"
  18.     />
  19.        <Button        android:id="@+id/apply_button"
  20.                 android:text="@string/apply"
  21.                 android:layout_width="wrap_content"
  22.                 android:layout_height="wrap_content"
  23.                 android:layout_marginLeft="20px"
  24.     />
  25.     <TextView     android:id="@+id/output_box"
  26.                 android:layout_width="wrap_content"
  27.                 android:layout_height="wrap_content"
  28.                   android:layout_marginTop="30px" android:layout_marginBottom="20px"
  29.                 android:layout_marginLeft="20px"
  30.     />
  31. </LinearLayout>

ארבעת האלמנטים של התצוגה אותם ראינו מופיעים כאן. שימו לב ל-spinner - שורה 14. songs_spinner מתייחס ל-id שלו,  ובאמצעותו, כרגיל, נוכל להתייחס לאלמנט זה, כפי שנראה מיד. עד כאן ה-layout.

ה-spinner כאמור כולל רשימה של שירים. אפשר היה ליצר רשימה זו בתוך קובץ ה-java, כמו שנעשה בתרגיל של ה-ListView. מצד שני, שימוש ברשימה שנמצאת בקובץ נתונים מאפשרת הוספת או הורדת פריטים מהרשימה מבלי לגעת בקובץ java וזה בהחלט עדיף. היות שכך, את רשימת השירים נחזיק בקובץ res/values/array.xml, ובתוכו מערך בשם songsArray. לשם הנוחות תוכן הקובץ מוצג כאן:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <resources>
  3.     <array name="songsArray">             
  4.         <item>Valentino</item>             
  5.         <item>Yesterday</item>             
  6.         <item>My Way</item>             
  7.         <item>The Boxer</item>
  8.         <item>American Pie</item>             
  9.         <item>Niddles and Pins</item>
  10.         <item>Indian Summer</item>             
  11.         <item>Knowing Me Knowing You</item>             
  12.         <item>Rasputin</item>
  13.         <item>Elinor</item>             
  14.     </array>   
  15. </resources>


מעבר לקובץ ה-layout וה-array שהוצגו למעלה, שאר הקבצים די סטנדרטים (למשל strings.xml), כך שנוכל התרכז בעיקר - mySpinner.java.

הפעם מדובר בתרגיל פשוט, האובייקט mySpinner מכיל מתודה אחת בלבד - onCreate.
כרגיל, נציג תחילה את האובייקט כולו ואח"כ נסביר אותו.


  1. public class mySpinner extends Activity {
  2.     /** Called when the activity is first created. */
  3.         private Spinner songSelectSpinner;
  4.         private Button     applyButton;
  5.         private TextView textBox;   

  6. @Override
  7.     public void onCreate(Bundle savedInstanceState) {
  8.         super.onCreate(savedInstanceState);
  9.         this.setContentView(R.layout.main);
  10.         songSelectSpinner = (Spinner) findViewById(R.id.songs_spinner);
  11.         textBox    = (TextView)findViewById(R.id.output_box);
  12.         applyButton     = (Button)findViewById(R.id.apply_button);
  13.         ArrayAdapter<String> songsAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, getResources()
  14.             .getStringArray(R.array.songsArray));
  15.         songSelectSpinner.setAdapter(songsAdapter);
  16.         applyButton.setOnClickListener(new View.OnClickListener() {
  17.             public void onClick(View view) {
  18.                 String selectedSong =  getResources().getStringArray(R.array.songsArray)[songSelectSpinner.getSelectedItemPosition()];
  19.                 textBoxox.setText("Selected song is: " + selectedSong);
  20.             }
  21.         });
  22.     }
  23. }
התהליכים במתודה onCreate:
שלב 1. האיתחולים הרגילים: קריאה למתודת ה"אב" (super), ואתחול ה-UI בשורות 8-9.
שלב 2. הבאת 3 הווידג'טים על פי ה-id שלהם:

  1.         songSelectSpinner = (Spinner) findViewById(R.id.songs_spinner);
  2.         textBoxox    = (TextView)findViewById(R.id.output_box);
  3.         applyButton     = (Button)findViewById(R.id.apply_button);
שלב 3. יצירת המתאם (Adapter) בין הרשימה שתשמש כתפריט לבין הווידגט. בעזרת המתאם מחברים את רשימת השמות לאובייקט ה-spinner והופכים אותה לתפריט. הפרמטרים של ה-constructor:
  • context. ראה this.
  • ה-id של ה-resource. כאן אנחנו משתמשים בתבנית מוכנה מראש של אמדרואיד - simple_spinner_item. אנדרואיד כולל תבנית נוספת ל-spinner:נסו להחליף את  simple_spinner_item ב: simple_spinner_dropdown_item. העיצוב הגראפי שיתקבל יהיה מעט שונה.
  • רשימת הפריטים - במקרה שלנו רשימת השירים - הנמצאת בקובץ res/raw/array.xml.
אגב, יצירת ה-Adapter הודגמה כבר בתרגיל הקודם עבור ה-ListView. שם מערך השמות הוגדר בקובץ ה-java בתוך האובייקט.

   ArrayAdapter<String> songsAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, getResources( .getStringArray(R.array.songsArray));

שלב 4. חיבור ה-Adapter לאובייקט.
ה-Adapter שנוצר בשלב -3, songsAdapter, מחובר לאובייקט ה-spinner ע"י המתודה setAdapter.
songSelectSpinner.setAdapter(songsAdapter);

זהו. יצירת ה-spinner הושלמה.

כעת נותר להגדיר את ה-callback שיחבר בין הלחיצה על הכפתור (הוספנו כאמור כפתור רגיל בשם applyButton) לבין שליפת האלמנט בתפריט שנבחר ב-spinner. יצירת ה-callback נעשית כרגיל - קרה למטה. בתוך המתודה onClick מתבצעות שתי פעולות:
שורה 3: שליפת שם השיר שנבחר.
שורה 4: הצגת השיר הנבחר ב-UI.

  1.        applyButton.setOnClickListener(new View.OnClickListener() {
  2.             public void onClick(View view) {
  3.                 String selectedSong =  getResources().getStringArray(R.array.songsArray)[songSelectSpinner.getSelectedItemPosition()];
  4.                 textBox.setText("Selected song is: " + selectedSong);
  5.             }
  6.         });
עד כאן ה-spinner. אני מכין פוסט בנושא MediaPlayer. נבנה שם נגן MP3 פשוט. אחרי כן, התוכנית היא להציג אפליקציה המשלבת את הנגן עם ה-spinner לבחירת השירים.
נ.ב.
"גיליתי" מדריך ל-spinner באתר המפתחים של אנדרואיד:
http://developer.android.com/resources/tutorials/views/hello-spinner.html
יש שם למעשה מדריך מעניין ל-widgets and views.
מקווה שהפוסט הנ"ל יסייע במתן אוריינטציה לחומר הרב המופיע באתר של אנדרואיד. זאת הרי אחת ממטרותיו בעצם.

יום חמישי, אוקטובר 28

Explicit Intent - העברת הודעות בין Activities

עקרונות הפעולה של ה-Intents תוארו בפרק המבוא להעברת הודעות, כך שנוכל לעבור מיד להדגמה. באפליקציה שהכנתי מוגדרים שני Activities. הודעה נשלחת מ-Activity האחד לשני. השני מחזיר תשובה לראשון. כל אחד מהם מלווה פעולתו בהדפסות למסך. פעולת שליחת ההודעה מתחילה עם לחיצה על כפתור Send Message.
מצא קישור לקבצי המקור בתחתית הדף.
נראה 3 תצוגות המתקבלות בהרצת האפליקציה:
תצוגת המערכת לפני ההפעלה - פאזה 1:



תצוגת המערכת בסוף התהליך- פאזה 3:


תצוגת המערכת במצב הביניים, מיד עם קליטת התגובה ע" ה  -Receive Activity- פאזה 3:
היות שה-Receive Activity נמצא בפעולה למשך זמן קצר בלבד, לא ניתן להבחין בהודעה שהוא שולח למסך.נפצה על כך עם הודעת pop-up שאמנם תשלח על ידי ה- Receive Activity  אבל נוכל להבחין בה.  אחרי הקדמה זו, הנה תמונת המסך מיד אחרי קבלת ההודעה ע"י ה-Receive Activity:



נעבור לסקירת קוד עצמו. האפליקציה בנויה משני classes  לכן דרושים שני קבצי מקור של Java: אחד עבור ה- Activity ששולח את ההודעה והקובץ השני עבור מקבל ההודעה. לא נתעכב על קבצי ה-Layout וה- Manifest. אין בהם אלמנטים שלא נדונו בפרקים קודמים.
נתחיל עם הקוד של שולח ההודעה, הנמצא בבקובץ MessageCalling.java. לשם נוחות הקוד מובא כולו כאן, אך בהמשך נדון בחלקים המעניינים הדורשים הסבר.

package com.ahs.ronenh.expintent;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

  1. public class MessageCalling extends Activity {
  2.     public final static int RECEIVE_MESSAGE = 0;
  3.     /** Called when the activity is first created. */
  4.     @Override
  5.     public void onCreate(Bundle savedInstanceState) {
  6.         super.onCreate(savedInstanceState);
  7.         setContentView(R.layout.main);
  8.         Button sendMessage = (Button) findViewById(R.id.send);
  9.         Button CleanScreen = (Button) findViewById(R.id.clean);
  10.         sendMessage.setOnClickListener(new View.OnClickListener() {
  11.             public void onClick(View view) {
  12.                 TextView msg2screen = (TextView)findViewById(R.id.pong_message);
  13.                 msg2screen.setText("");
  14.                 Intent pingIntent = new Intent();
  15.                 pingIntent.setClass(MessageCalling.this, MessageReceiving.class);
  16.                 pingIntent.putExtra( "extra1", new String( "Message Received: Can you hear us?" ) );
  17.                 startActivityForResult(pingIntent, RECEIVE_MESSAGE);
  18.              }
  19.         });
  20.         CleanScreen.setOnClickListener(new View.OnClickListener() {
  21.             public void onClick(View view) {
  22.                 TextView msg2screen = (TextView)findViewById(R.id.pong_message);
  23.                  msg2screen.setText("");
  24.              }
  25.         });
  26.     }
  27.     protected void onActivityResult(int requestCode, int resultCode, Intent data){
  28.         switch(requestCode) {
  29.             case RECEIVE_MESSAGE:
  30.                 String     rec1    = data.getStringExtra("extra1");     
  31.                 Integer rec2    = data.getIntExtra("extra2",0);     
  32.      
  33.             if( rec1 != null ) {
  34.                 TextView msg2screen = (TextView)findViewById(R.id.pong_message);
  35.                 msg2screen.setText( rec1 + " " + rec2);
  36.             }
  37.          }
  38.     }
  39. }


ב- MessageCalling שתי מתודות עיקריות: ()onCreate, אודותיה כבר דנו, והיא המתודה המופעלת עם יצירת האובייקט. 
המתודה השניה ()onActivityResult היא callback שמופעל כאשר התשובה להודעה מגיעה.
שורה 6- כרגיל, כריעה ל-onCreate של class ה"אבא".
שורה 7 - בניית ה- UI על פי ה- layout.
שורה 8-9: הגדרת האובייקטים לכפתורי המסך: שליחת הודעה וניקוי תצוגה.
שורה 10-19: רגיסטרציה של callback עבור כפתור שליחת ההודעה.
שורה 11: המתודה onClick, היא ה-callback שיופעל עם הלחיצה על כפתור send.
שורה 12: השמת אוביקט מסוג TextView, המשמש לתצוגת טקסט ב- UI.
שורה 13: ניקוי התצוגה.

שורות 14-18: הכנת ה- Intent ושליחתו.
שורה 14: יצירת אוביקט מסוג Intent.
שורה 15: היות שמדובר ב explicit intent יש לציין במפורש את יעד ההודעה. זה נעשה ע"י המתודה של  ה-Intent הבא: setClass(context, class, כאשר היעד של ההודעה הוא MessageReceiving.class.
שורה 16: הוספת שדה ה-extra ב-intent. כאן מוכנס string שיודפס ע"י האובייקט המקבל.
שורה 17: שליחת ה-Intent ע"י המתודה  (startActivityForResult(intent, id. מתודה זו תפעיל את ה- Activity המקבל. ה- Activity השולח מצפה לתשובה אסינכרונית - זו התוצאה הנשלחת חזרה. עם קבלת התשובה, יופעל ה-callback אותו נסקור מיד.
שורות 20 עד 25 הם ה- callback של כפתור ניקוי המסך.
שורה 27: ה- callback שמופעל עם קבלת התשובה להודעה/
שורה 29: בדיקת הסוג של ההודעה- זה הזהוי שהוצמד להודעה המקורית.
שורות 30-31: שליפת שדות ה-extra מה- Intent. שדה אחד מסוג string והשני integer.
שורה 34: יצירת אוביקט מסוג TextVeiw. השדה שהוגדר לו ב- UI הוגדר בקובץ ה-Layout כ-pong_message לעומת ping_message שהוגדר עבור ההודעה היוצאת.
שורה 35: הדפסת שדות ה-extra על המסך. ראה התמונה של "פאזה 3".


כעת ניסקור את קובץ השני, MessageReceiving.java:



package com.ahs.ronenh.expintent;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;

/**
 * @author ronen
 *
 */
  1. public class MessageReceiving extends Activity{
  2.     /** Called with the activity is first created. */
  3.     @Override
  4.     public void onCreate(Bundle savedInstanceState)
  5.     {
  6.         super.onCreate(savedInstanceState);
  7.         setContentView(R.layout.main);
  8.         String extraMsg1 = getIntent().getExtras().getString("extra1");
  9.         if( ( extraMsg1 != null ) ) {
  10.           TextView msg2screen = (TextView)findViewById(R.id.ping_message);
  11.           msg2screen.setText(extraMsg1);
  12.          Toast.makeText(this, "Received Message:" + extraMsg1, Toast.LENGTH_LONG).show();
  13.         }
  14.         else
  15.         {
  16.              Log.e("Received Extra is null", "null");
  17.         }
  18.         Intent pongIntent = new Intent();
  19.         pongIntent.putExtra("extra1", "Response received: Yes, we can hear yall!");
  20.         pongIntent.putExtra("extra2", new Integer(555) );
  21.         setResult(RESULT_OK,pongIntent);
  22.         finish();
  23.     }
  24. }
 ה- MessageReceiving class מקבל את ה- Intent ושולח תשובה. הוא מכיל את המתודה ()onCreate בלבד.

שורה 8: שולפת את שדה ה-extra מה- intent הנקלט.
שורה 10: יצירת אובייקט מסוג TextView, המשוייך לאלמנט ה-ping_message ב-Layout של ה- UI.
שורה 11: שליחת שדה ה-extra שהתקבל ל-UI. הערה: לא נוכל לספיק לראות תצוגה זו, היות שה-Activity הנ"ל יתחלף במהירות חזרה ל-Activity השולח, עם שליחת התשובה. כזכור, עם כניסת Activity חדש, התצוגה של הקודם נעלמת.

שורה 12: "פיצוי" על העלמות ההודעה ב- UI, כפי שהוסבר בהערה הנ"ל: במקום זה, נדפיס הודעה בעזרת האובייקט Toast שמייצר הודעות pop-up.
שורה 18-21: בניית התשובה ושליחתה.
שורה 18: יצירת אובייקט intent.

שורות 19-20: הכנסת ערכים לשדה ה-extra. הם יודפסו למסך על-ידי ה- ()onActivityResult כפי שתואר קודם.
שורה 21:()setResult שולחת את התשובה ומפעילה את האובייקט MessageCalling.

למדנו די הרבה היום...:-).
קישור לקבצי המקור.