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

יום שלישי, נובמבר 16

על המפה


אפליקציות הקשורות למיקום ולמפות הן מהדברים הכי מעניינים את בעלי הסמארטפונים. לפיכך, נקדיש לנושא זה מספר פוסטים. כוונתי להתחיל מהפשוט מאד ולהוסיף דוגמאות מורכבות יותר בהדרגה. בפוסט הזה נדגים הצגה של מפות+  הצגה של אתרים על המפה. בפוסט שאחריו, נדגים ציור על גבי המפה (overlay), ע"י ציור של סיכה המסמנת מיקום. באותו פוסט, או אולי בפוסט מאוחר יותר (נשקול את העניין, כשהמטרה היא ללמוד בשלבים), נדגים גם את נושא ה-Geocoding, כשנהפוך שם של מקום לקואורדינטות על המפה.
אגב, לכל המעוניינים, באתר המפתחים של גוגל יש מדריך מבוא למפות לא רע. בכל זאת, אני מקוה שהפוסט הזה ואלה שאחריו יהיו אולי יעילים יותר. הקישור - Google Map View.
עוד  נושא די מצער לפני שמתחילים: המפות של גוגל אמנם כוללות גם את ישראל, אבל ה-API של גוגל מאפשר הצגה של ישראל ברזולוציה מאד נמוכה - ראה את התמונה הבאה להמחשה:



רואים את הסיכה שעל המפה? זו עפולה. ומימינה - לא, זה לא כביש 65 של ישראל. זו כבר ירדן.  מפת ישראל ברזולוציה גבוהה אמנם זמינה בגוגל, אבל אפשר לגשת אליה רק דרך ה- Browser. הסיבות לכך קשורות לעניינים מסחריים, כנראה בין גוגל לבין הבעלים של מפות הישראל.  חבל. יש סיכוי שזה ישתנה?  מקווה שכן אבל אין (לי) שום מידע בקשר לזה. מצד שני לא לשכוח את חצי הכוס המלאה: יש מפות לשאר העולם. הקישור הבא מפרט את המדינות עבורן google Map API נתמך:
http://gmaps-samples.googlecode.com/svn/trunk/mapcoverage_filtered.html
נעבור לדוגמא שלנו. נציג על המסך מפה + כפתורי שליטה. המיקום הראשוני שיקבע יהיה פאריס. נוסיף מספר כפתורים כדי להציג יכולות: כפתור למעבר בין תמונת לויין למפה, כפתורי זום, כפתור  clickable שיבחר בין תמונה סטטית לבין אפשרות לתזוזה.
קישור להורדת קבצי הפרויקט - למטה בסוף.
הנה ה-UI:



כמה הערות אדמיניסטרטיביות חשובות: העבודה עם המפות מחייבת רשיונות\הרשאות. מדובר בהרשאה לשימוש בספריות של גוגל, הרשאה כללית לגישה לאינטרנט ו-  API Key עליו נדון כשנסקור את קובץ ה-Layout.  זאת מפני שגוגל שומרת על הפרדה בין הספריות של אנדרואיד לבין ספריות ה-API שלה. האם הם זוממים משהו שאנחנו לא יודעים? מקווה מאד שלא.
מכאן ואילך אם כן נתאר את הנושאים הבאים:
  1. דגשים מיוחדים הקשורים לתמיכה במפות בזמן בבניית הפרויקט.
  2. דגשים מיוחד הקשורים למפות בקובץ ה-manifest.xml.
  3. קובץ ה-layout.
  4. תוכנית ה-java.
1. דגשים בבניית הפרוייקט :
הדגש היחידי, בחירת build target מסוג Google API. לכל ורסיה החל מ-1.5 יש ורסיה תואמת. למה? מפני שגוגל שומרת על הפרדה בין הספריות של אנדרואיד לבין ספריות ה-API שלה. האם הם זוממים משהו שאנחנו לא יודעים? מקווה מאד שלא. ליצירת הפרויקט ב-Eclipse נבחר: File->New->Android Project. בחלון הדוגמא שלפניכם מצגת הבחירה עבור 2.2 - Froyo.


יתר המאפיינים - כרגיל.
2. נעבור לקובץ ה-manifest. נציג אותו כאן בשלמותו. האלמנטים המיוחדים כאן מודגשים (שורות 7 ו-17):
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3.       package="com.ahs.adroheb.mapdisplay"
  4.       android:versionCode="1"
  5.       android:versionName="1.0">
  6.     <application android:icon="@drawable/icon" android:label="@string/app_name">
  7.      <uses-library android:name="com.google.android.maps" />
  8.     
  9.         <activity android:name=".MyMapDisplay"
  10.                   android:label="@string/app_name">
  11.             <intent-filter>
  12.                 <action android:name="android.intent.action.MAIN" />
  13.                 <category android:name="android.intent.category.LAUNCHER" />
  14.             </intent-filter>
  15.         </activity>
  16.     </application>
  17. <uses-permission android:name="android.permission.INTERNET"/>
  18.     <uses-sdk android:minSdkVersion="3" />
  19. </manifest>
השורות המעניינות:
שורה 7: הכללת ספריית ההמפות.
שורה 17 היא הרשאה לגישה לאינטרנט.
מעבר לזה, אפשר ללמוד מהקובץ הנ"ל שהאפליקציה שלנו מכילה Activity אחד בלדבד.
3. נציג את קובץ ה-Layout בשלמותו:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <!-- This file is /res/layout/geocode.xml -->
  3. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  4. android:layout_width="fill_parent"
  5. android:layout_height="fill_parent">
  6. <com.google.android.maps.MapView
  7. android:id="@+id/geoMap" android:clickable="true"
  8. android:layout_width="fill_parent"
  9. android:layout_height="wrap_content"
  10. android:apiKey="Put your API Key here"
  11. />
  12. <LinearLayout android:layout_width="fill_parent"
  13. android:layout_alignParentBottom="true"
  14. android:layout_height="wrap_content"
  15. android:orientation="horizontal" >
  16. <Button
  17. android:id="@+id/button_sat_map"
  18. android:layout_width="wrap_content"
  19. android:layout_height="wrap_content"
  20. android:text="@string/button_sat_map"/>
  21. <Button
  22. android:id="@+id/button_zoom_out"
  23. android:layout_width="wrap_content"
  24. android:layout_height="wrap_content"
  25. android:text="@string/button_zoom_out"/>
  26. <Button
  27. android:id="@+id/button_zoom_in"
  28. android:layout_width="wrap_content"
  29. android:layout_height="wrap_content"
  30. android:text="@string/button_zoom_in"/>
  31. <Button
  32. android:id="@+id/button_clickable"
  33. android:layout_width="wrap_content"
  34. android:layout_height="wrap_content"
  35. android:text="@string/button_clickable"/>
  36. </LinearLayout>
  37. </RelativeLayout>


מעבר להגדרת הכפתורים עליהם לא נתעכב, נסתכל על השורות המודגשות: 6-10.
שורות אלה מגדירות את האלמנט MapView בו נשתמש לצירת המפה. את ה-API Key יש לשתול בשורה 10. הקלק על הקישור לקבלת מדריך ליצירת ה-API Key. ללא ה-Key לא ניתן לצפות במפות.

4.  שתי  מחלקות (classes) עושות את עיקר העבודה הקשורה למפות: MapView ו- MapActivity.
MapActivity היא הרחבה (subclass) של Activity (לטובת טיפול במפות, וכדי להשתמש ב MapView חייבים להשתמש בה.
נציג תחילה את השלד של האוביקט והמתודות שלו:

  1. public class MyMapDisplay extends MapActivity implements OnClickListener{
  2.     MapView mapView;
  3.     private Button     buttonSatMap;
  4.     private    Button    buttonZoomIn;
  5.     private    Button    buttonZoomOut;
  6.     private    Button    buttonClick;
  7.     @Override
  8.     protected void onCreate(Bundle savedInstanceState)
  9.     {
  10.      }
  11.    
  12.     private void controlButtonManage(){
  13.    }
  14.     public void onClick(View view) {
  15.  
  16.     }
  17.     @Override
  18.     protected boolean isLocationDisplayed() {
  19.         return false;
  20.     }
  21.     @Override
  22.     protected boolean isRouteDisplayed() {
  23.         return false;
  24.     }


 שורה 1: ה-class כאמור   extends MapActivity.
הפעם הוספנו גם את: implements OnClickListener. המשמעות: ה-callback שמופעל עם לחיצת הכפתור - onClick ימומש כמתודה בתוך ה-class (ראו שורה 14 + הסברים בהמשך). שימו לב, זהו מימוש מעט שונה מזה שהשתמשנו בו בחלק מהדוגמאות הקודמות.
 לגבי שורות 17-24: שתי המתודות הנ"ל: isLocationDisplayed ו- isRouteDisplayed חייבות להיות ממומשות עבור classes של ה-MapActivity. זה חוק מחייב מעוגן ברישיון של גוגל, שנועד לאפשר לשרתים שלהם לעקוב אחרי אפליקציות אלה. מה הם זוממים שם??
  isLocationDisplayed - יחזיר true אם האובייקט מציג את מיקומו על סמך שימוש בסנסורים - למשל GPS. במקרה שלנו נחזיר false.
isRouteDisplayed - יחזיר true אם מוצגת אינפורמציה של הכוונת נתיב (routing), למשל - הוראות כיוון נסיעה.  גם כאן נחזיר false.



המתודה בה מתבצעת עיקר הפעילות היא onCreate, נציג אותה שלמותה:


  1.     @Override
  2.     protected void onCreate(Bundle savedInstanceState)
  3.     {
  4.         super.onCreate(savedInstanceState);
  5.         setContentView(R.layout.main);
  6.         mapView = (MapView)findViewById(R.id.geoMap);
  7.         mapView.setBuiltInZoomControls(true);
  8. // Coordinates of Paris:        
  9.         int lat = (int)(48.856826*1000000);
  10.         int lng = (int)(2.351317*1000000);
  11.         GeoPoint loc = new GeoPoint(lat,lng);
  12.         mapView.getController().setZoom(15);
  13.         mapView.getController().setCenter(loc);
  14.         controlButtonManage();
  15.      }

שורה 6 שולפת את האלמנט MapView .
שורה 7, מפעילה את הכפתור המובנה במפה לשליטה בזום. אמנם הוספנו כפתור זום משלנו אך הוא די מיותר. למעשה, הוספנו אותו כדי להציג את המתודה של הזום.
שורה 11 יוצרת את האובייקט  loc מסוג GeoPoint. הוא מחזיק מיקום על המסך על סמך קואורדינטות. במקרה הנ"ל הכנסנו את הקואורדינטות של פאריס.
שורה 12 קובעת את רמת הזום שתוצג.
שורה 13 מעבירה את תצוגת המפה לפי loc.

זה עיקר העניין. שורה 14 מפעילה מתודת שרות שמפעילה את הכפתורים ומחברת אותם לפונקציונליות שלהם. הנה המתודה:
  1.     private void controlButtonManage(){        buttonSatMap         = (Button)findViewById(R.id.button_sat_map); 
  2.         buttonZoomIn         = (Button)findViewById(R.id.button_zoom_in); 
  3.         buttonZoomOut         = (Button)findViewById(R.id.button_zoom_out);
  4.         buttonClick         = (Button)findViewById(R.id.button_clickable);
  5.         buttonSatMap.setOnClickListener(this);
  6.         buttonZoomIn.setOnClickListener(this);
  7.         buttonZoomOut.setOnClickListener(this);
  8.         buttonClick.setOnClickListener(this);
      
    }

נתייחס לשורות 5-7, המגדירות את ה-callback שיופעל עם לחיצת הכפתור - onClick. המימוש כאן שונה במקצת מהמימושים בהם השתמשנו בכמה מהדוגמאות הקודמות. התיחסנו לנושא כבר בפוסט זה - ראה למעלה. callback אחד יהיה משותף לכל הכפתורים, והפיצול לטיפול בכפתורים השונים יעשה בצוך המתודה.
אז הנה ה- onClick



  1.     public void onClick(View view) {
  2.         switch(view.getId()){
  3.             case     R.id.button_sat_map:
  4.                 if(mapView.isSatellite())
  5.                     mapView.setSatellite(false);
  6.                 else
  7.                     mapView.setSatellite(true);
  8.             break;
  9.             case    R.id.button_zoom_in:
  10.                 mapView.getController().setZoom(mapView.getZoomLevel() + 1);
  11.             break;
  12.             case    R.id.button_zoom_out:
  13.                 mapView.getController().setZoom(mapView.getZoomLevel() - 1);
  14.             break;
  15.             case    R.id.button_clickable:
  16.                 if(mapView.isClickable())
  17.                     mapView.setClickable(false);
  18.                 else
  19.                     mapView.setClickable(true);
  20.             break;
  21.         }
  22.     }


לא נתעכב עליה יותר מדי. בשורה 2 מתבצע פיצול פי הכפתור שנלחץ. בהמשך כל כפתור מקבל טיפול בהתאם. שימו לב למתודות המטפלות בהצגת תמונת לווין (satellite), זום, ו-click.
הקלק להורדת קבצי הפרויקט.