NSSL logo EE logo

תדריך למעבדת (ניסוי) אנדרואיד


חומר רקע – תיאוריה

מבוא

Android הינה מערכת תוכנה מבוססת Linux המיועדת למכשירים ניידים (בעיקר מבוססי מגע) כגון טלפונים ומחשבי לוח (Tablets). המערכת מופצת על-ידי חברת Google

Android היא מערכת הבנויה בשכבות (Layers). מערכת Android כוללת מערכת הפעלה, תשתית לבניית אפליקציות, מכונה וירטואלית להרצת האפליקציות, ספריות גרפיות, בסיס נתונים, תמיכה במדיה, טלפוניה, רשת אלחוטית, מצלמה, ניווט GPS, חיישנים - וכן סביבת פיתוח עשירה המשתלבת במערכת הפיתוח Android Studio.

כאמור, Android מבוססת על ליבה של מערכת ההפעלה Linux. היא מגיעה עם קבוצת אפליקציות בסיסיות הכוללות: אפליקציה לדואר אלקטרוני, מסרונים (SMS), לוח שנה, מפות, דפדפן, אנשי קשר ועוד.

בעולם התעשייה כיום רוב הפיתוח לאנדרואיד נעשה בשפת Kotlin, אך עדיין קיימות אפליקציות רבות הכתובות ב-Java, C ו-C++. בניסוי זה נתמקד ב-Java כדי לחבר בין מה שלמדתם בקורסי OOP לבין פיתוח מובייל; רוב המושגים שתראו כאן מתורגמים בקלות גם ל-Kotlin.

סביבת הפיתוח הפתוחה מאפשרת למפתחים לבנות אפליקציות חדשניות ועשירות בקלות:

המבנה הייחודי של Android מאפשר לנהל בנוחות את הגישה לחומרת המכשיר, וכן לרכיבים הפריפריאליים שלו (למשל, מערכת המיקום – GPS, מערכת הודעות, התראות, וכיוצא באלה).

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

מערכת Android מספקת לנו ממשק משתמש גרפי (GUI) קל לתחזוקה וליצירה באמצעות קבצי XML. את הממשק הגרפי ניתן ליצור באמצעות "אבני בניין" של אלמנטים גרפיים, פשוטים ומסובכים כאחד, הנקראים Views (או Widgets ). לדוגמא כפתורים, תיבות טקסט, וכו', כמתואר:

דוגמא

Widget examples

TextVew, EditText, Button

ועוד שלל אפשרויות שונות.

האלמנטים הללו מתחברים לכדי Layout באופן נוח להגדרה כפי שתראו בסרטון ההדרכה.

קישור בין אפליקציות גם הוא מתבצע בצורה קלה, וניתן לגשת למידע של אפליקציות אחרות בכמה שיטות, כגון באמצעות ספקי תכן (Content Providers) , או על ידי שיתוף המידע של האפליקציה באופן ישיר.

רכיבי האפליקציה

רכיבי האפליקציה (Application Components) הם למעשה "אבני הבניין" הבסיסיים של ליבת האפליקציה. כל רכיב מהווה נקודת גישה דרכה המערכת יכולה לגשת לאפליקציה; לא כל רכיבי האפליקציה מהווים נקודת גישה למשתמש, וחלקם תלויים אחד בשני – אבל כל אחד מהם מתקיים כישות נפרדת (יכול להיות מופעל באופן אינדיבידואלי) ויש לו תפקיד ספציפי שעוזר לאפיין את התנהגות האפליקציה.

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

SERVICES

Service הוא רכיב שרץ ברקע ומאפשר ביצוע של תהליכים שרצים למשך זמן רב, או עבודה עבור תהליכים מרוחקים. Services אינם מספקים ממשק משתמש – למשל, Service של מוזיקה יכול לנגן מוזיקה ברקע בזמן שהמשתמש נמצא באפליקציה אחרת. דוגמא נוספת - Service יכול להוריד קובץ גדול מהאינטרנט בזמן שהמשתמש נמצא באפליקציה אחרת, וכו׳.

את הService - מפעילים, בדרך כלל, מרכיב אחר כגון (Activity).

CONTENT PROVIDERS

Content Providers מנהלים אוסף של מידע משותף עבור אפליקציות. ניתן לאחסן את המידע במספר דרכים, כגון במערכת הקבצים, במסד נתונים כמו ,(SQLite) ברשת, וכו'. באמצעות ה-Content Provider אפליקציות אחרות יכולות לגשת למידע ואף לשנותו.

למשל, במערכת ה-Android כל המידע הקשור לאנשי הקשר של המשתמש מנוהל על-ידי .Content Provider כל אפליקציה עם ההרשאות המתאימות יכולה לגשת למידע השמור במאגר הנתונים הנ"ל.

BROADCAST RECEIVERS

Broadcast Receivers הינם רכיבים שמגיבים לכריזות (Broadcast Announcements) מערכת. למשל, כריזה שמעדכנת על הדלקת המסך, על בטרייה אוזלת, וכו'. אפליקציות יכולות גם ליזום כריזות (כמו למשל כריזה שמודיעה על קובץ שסיים לרדת מהאינטרנט) וגם להאזין להן.

מעתה נתמקד רק ב - Activities.

ACTIVITIES

Activity הינה מחלקה המייצגת מסך בודד עם ממשק משתמש.

לדוגמא, באפליקציית דואר אלקטרוני יתכנו מספר מופעים של Activity - אחד המראה את רשימת הודעות הדואר החדשות, השני לכתיבת הודעת דואר חדשה, ואחר לקריאת הודעות - כל אחד מהם עובד בנפרד ואינו תלוי באחרים. אי-התלות הזאת הינה מרכיב המסייע רבות לתכן נכון ולגמישות אפליקציות, ומאפשרת שיתוף Activities בודדים בין אפליקציות – למשל, אפליקציה של מצלמה שיכולה לגשת ישירות למופע של Activity באפליקציית הדואר האלקטרוני שאחראי על שליחת מייל, כדי שהמשתמש יוכל לשלוח את התמונות שצילם.

מחזור החיים של Activity ומתודות CALLBACK

לכל Activity יש מחזור חיים (Lifecycle) – אוסף מצבים שה־Activity יכולה להיות בהם, והמעברים ביניהם. מערכת ההפעלה היא זו שקוראת למתודות השונות בהתאם למצב, ואנחנו רק מממשים את המתודות הרלוונטיות.

המתודות המרכזיות במחזור החיים הן:

• onCreate – נקראת כשנוצר Activity חדש. כאן מאתחלים את הרכיבים החיוניים, טוענים נתונים וקוראים ל־setContentView כדי לקשר בין המחלקה ל־Layout ה־XML הרלוונטי.
• onStart – ה־Activity עומד להיות נראה למשתמש.
• onResume – ה־Activity נמצא בחזית ומוכן לאינטראקציה (זה המצב ה"פעיל" הרגיל). • onPause – ה־Activity עומד לעבור לרקע (למשל, בגלל ש־Activity אחר עולה מעליו). כאן שומרים שינויים קלים, עוצרים אנימציות ועוד.
• onStop – ה־Activity כבר לא נראה למשתמש.
• onDestroy – ה־Activity עומד להיהרס, ומשחררים בו משאבים כבדים במידת הצורך.

אוסף המתודות הללו נקרא מתודות Callback – המערכת קוראת להן בזמן המתאים, ואנחנו מממשים רק את מה שצריך עבור האפליקציה שלנו.

בכל Activity לפחות מתודת onCreate חייבת להיות ממומשת. במימוש שלה אנחנו מאתחלים את הרכיבים החיוניים לאפליקציה, ובין השאר קוראים למתודה setContentView שמקשרת את המסך ל־Layout הרלוונטי שמוגדר על ידי קובץ XML (כפתורים, קופסאות טקסט, תמונות וכו'). דוגמה מוחשית לכך מופיעה בהמשך ובווידאו המצורף.

INTENT

אפליקציה ב - Android יכולה להתחיל ולהפעיל אפליקציה אחרת, או שירות מערכת.

באופן א-פורמאלי, Intent - כנובע משמו - מייצג את ה"כוונה" של אפליקציה 'לעשות משהו'. למעשה, מדובר בלא יותר מאשר הודעה של אפליקציה שהיא 'עשתה משהו' או רוצה ש'משהו יקרה'. כתלות ב-Intent, אפליקציות נוספות – או מערכת ההפעלה – יאזינו לו, ויגיבו בהתאם.

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

המעבר מתהליך לתהליך מתבצע באמצעות מערכת ההפעלה, כאשר אפליקציית הצילום פונה למערכת ההפעלה ומעבירה אליה אובייקט מסוג Intent.

האובייקט המועבר מכיל את המידע הדרוש להפעלת רכיב האפליקציה האחר - כמו איזו פעולה להפעיל, וכל מה שהפעולה המופעלת חייבת לדעת בנוסף.

דוגמאות: בקשה לתמונה, כתובת אתר WEB, מחרוזת כלשהי, הודעה להפצה ועוד.

את ההקשר בין Activity לבין Intent נראה באמצעות הדוגמא הבאה: נניח שאנו משתמשים באפליקציית דואר אלקטרוני וצופים בהודעות שקיבלנו. אנו פותחים הודעה מרשימת ההודעות, ובה יש לינק לאתר שבו יש תוכן וידאו (Streaming). אנו לוחצים על קישור זה והווידאו מוצג בנגן המדיה. להלן תרשים אבסטרקטי המייצג את הקשר בין האפליקציות השונות באמצעות Intent:

Intent digramma

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

משאבי האפליקציה

האבסטרקציה אותה מערכת ההפעלה Android מספקת לנו כמפתחים היא הרבה יותר מהאפשרות לתכנת ב-Java את האפליקציות שלנו ואספקת פונקציות ספריה.

כל אפליקציית Android מורכבת מקבצי מקור (Source Code Files), קבצי Java ומקבצי משאבים , עליהם נדבר בסעיף זה.

קבצי המשאבים מספקים מודולריות משמעותית לאפליקציות אנדרואיד באופן שבו ניתן לשנות ולהחליף את המשאבים ללא צורך לשנות את הקוד – למשל Layouts שונים לגדלי מסך שונים. דוגמא נוספת: אפשר להוסיף ארבע וריאציות לסמל של האפליקציה – כל וריאציה באיכות תמונה אחרת וכתלות בכמות הפיקסלים וגודל המסך של הטלפון שמריץ את האפליקציה יוצג סמליל אחר.

לכל משאב יש מזהה ייחודי (כגון מספר או מחרוזת) הנקרא Resource ID שניתן לפנות אליו ישירות מהקוד.

קובץ ה-MANIFEST

לכל אפליקציה חייב להיות קובץ בשם AndroidManifest.xml.

קובץ זה מייצג את כל המידע החיוני על האפליקציה כלפי מערכת האנדרואיד, עוד לפני שמערכת ההפעלה מריצה שורת קוד אחת. בקובץ זה אנחנו מצהירים על הדברים הבאים:

• שם החבילה (package) – משמש מזהה ייחודי לאפליקציה.
• רכיבי האפליקציה – Activities, Services, Broadcast Receivers, Content Providers.
• מאפייני מעטפת – שם האפליקציה, אייקון, ערכת נושא, תמיכה ב־RTL ועוד.
• הרשאות (Permissions) – לאילו משאבי מערכת האפליקציה רשאית לגשת.

דוגמה לקובץ Manifest פשוט של אפליקציה המכילה שני Activities והרשאת גישה לאינטרנט:

		

<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.ee.nssl.tutorial"> <uses-permission android:name="android.permission.INTERNET" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>


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

• INTERNET – גישה לרשת.
• ACCESS_NETWORK_STATE – בדיקת מצב הרשת.
• CAMERA – שימוש במצלמה.
• READ_CONTACTS / WRITE_CONTACTS – גישה לאנשי קשר.
• ACCESS_FINE_LOCATION – גישה למיקום מדויק (GPS).

מבחינת אנדרואיד יש שתי קבוצות עיקריות:

1. הרשאות "רגילות" (normal) – כמו INTERNET או ACCESS_NETWORK_STATE.
עבור הרשאות אלו מספיק להצהיר עליהן בקובץ ה־Manifest (באמצעות <uses-permission>), והמערכת מאשרת אותן בזמן התקנה ללא דיאלוג מיוחד למשתמש.
2. הרשאות "מסוכנות" (dangerous) – כמו CAMERA, READ_CONTACTS או ACCESS_FINE_LOCATION. עבור הרשאות אלו חובה גם:
• להצהיר עליהן בקובץ ה־Manifest
• וגם לבקש אותן בזמן ריצה (Run Time Permissions) – כלומר, להציג למשתמש דיאלוג שמבקש אישור מפורש.

במסגרת ניסוי זה נסתפק בהבנה העקרונית של ההבדל, ולא נממש בקוד בקשת הרשאות בזמן ריצה. חשוב רק שתדעו: אם האפליקציה שלכם מתעסקת במידע רגיש (מצלמה, אנשי קשר, מיקום וכו') – הגדרה ב־Manifest לבדה אינה מספיקה.

בעבר היה נהוג להגדיר במניפסט גם פרטים כמו גרסת אנדרואיד מינימלית (minSdk) וקונפיגורציית חומרה. בפרויקטים מודרניים מרבית ההגדרות האלה עברו לקובצי ה־Gradle של המודול (build.gradle), אבל הרעיון נשאר זהה – האפליקציה "מכריזה" למערכת אילו יכולות וגרסאות היא דורשת כדי לפעול.

קובץ ה-R

קובץ ה - R מקשר בין עולם קוד המקור עולם ה - (JAVA) לעולם המשאבים.

הוא נוצר עבורנו באופן אוטומטי על-ידי תוכנת ה - (Android Studio) ואין כל סיבה לשנות אותו: בכל פעם שמשנים משהו בספריות הרלוונטיות למשל מוסיפים קובץ ,(XML) קובץ ה-R מתעדכן .

כמפתחים, אין לנו כל צורך להסתכל בקובץ זה; מערכת ה - Android Studio מאפשרת להתייחס לערכים השמורים בו ונותנת לנו ממשק נוח לשינויים שבו.

ממשק המשתמש הגרפי – GUI

כאמור, האפליקציה של אנדרואיד נמצאת בשני סוגי קבצים: קבצי משאבים וקבצי קוד מקור.

בין קבצי המשאבים ישנם קבצי XML עבור הממשק הגרפי. קבצים אלו מתארים את האופן הפריסה- (Layout) של הצמתים המשתתפים בהיררכיה ואת תכונותיהם.

גם בקובץ קוד המקור (Java) אנו מגדירים את האלמנטים הגרפיים, אולם ההבדל בין הקבצים הוא מהותי: בקובץ ה-XML עדיף להגדיר את הפריסה הסטטית של האלמנטים (כלומר, היכן כל אלמנט מופיע על המסך), ואת ההתנהגות הדינאמית בקבצי הקוד (למשל, כיצד הכפתור יגיב כאשר ילחצו עליו – האם ישנה את הטקסט?).

VIEWGROUP, VIEW

כל אלמנטים ה-GUI באפליקציית Android מורכבים מאובייקטים שהם מופעים של המחלקות View ו-ViewGroup.

אובייקט View הוא אובייקט שמצייר על המסך אלמנט שהמשתמש יכול לעשות איתו אינטראקציה כלשהי (למשל, כפתור או תיבת טקסט). בתוך האובייקט יש מבנה נתונים השומר בתכונותיו את הפרמטרים של המבנה והתוכן של מלבן מסוים על המסך. הוא מטפל במידות של עצמו, בפריסה שלו על פני המסך, בציור, שינוי הפוקוס וכו' בתוך המלבן בו הוא יושב. בעזרתו נעשית האינטראקציה עם אירועי המשתמש.

ViewGroup הוא אובייקט מאגד שמכיל בתוכו מופעים של View (וגם של ViewGroup) כדי להגדיר את ה-Layout של הממשק (היכן יוצג איזה אובייקט), באופן הבא :

ViewGroup diagramma

קובץ ה-XML של ה-Activity הראשי של הפרויקט נמצא בקובץ: res/layout/main_activity.xml.

כדי לגרום לאפליקציה להציג על המסך את ה-Layout הרלוונטי אנו קוראים למתודה ()setContentView, ונותנים לה כפרמטר את ה-Resource ID של ה-Layout. למשל:

	  
		setContentView(R.layout.main_activity);
		

הספריות הסטנדרטיות ב-Android מספקות אוסף של מחלקות יורשות מ-View ומ-ViewGroup שמציעות אלמנטים בסיסיים לעבוד איתם (כמו כפתורים ושדות טקסט, ואף Layouts יחסיים או אבסולוטיים – כפי שתוכלו להתרשם מצפיה בווידאו ובמהלך הניסוי).

LAYOUT

הדרך המקובלת להגדיר את פריסת ה-Views, כלומר את ה-ViewGroup - היא בעזרת קבצי XML.

ה-ViewGroup הנפוץ ביותר הוא Layout מהסוגים, ConstraintLayout LinearLayout ו-Relative Layout, כאשר, כמובן, כל אחד מהם יכול להכיל מופעים נוספים.

קובץ ה-XML מציע מבנה קריא של מערך הפריסה, ומבנהו בדומה ל-HTML. באופן זה, קל לתאר את עץ הרכיבים בדומה לשרטוט בסעיף קודם. כל שם ב-XML מייצג אלמנט ממחלקה עם שם תואם בתוכנית ה-Java.

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

את קובץ ה-XML ניתן לערוך באופן טקסטואלי או באמצעות ממשק GUI נוח מאוד שה-Android Studio מספק.

הקובץ הבא הינו דוגמא המתארת LinearLayout אנכי הממלא את גובה ורוחב המסך, והמכיל בתוכו תיבת טקסט (TextView) וכפתור. הכפתור והטקסט תופסים רק את הרוחב הדרוש לטקסט המוצג בהם.

		
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="16dp"
        android:layout_marginBottom="16dp"
        android:text="TextView"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

		

INPUT EVENTS

האינטראקציה עם האובייקטים של הממשק נעשית בעזרת אירועים (Events) שגורמים לביצוע פעולות.

שמו של כל ממשק X הוא: On{X}Listener, כאשר הביטוי {X} מציין את סוג האינטראקציה עם המשתמש,
למשל:

• הממשק View.OnClickListener – ממשק המאפשר לאלמנט להגיב לאירוע לחיצת כפתור

• הממשק View.OnTouchListener – ממשק המאפשר לאלמנט להגיב לנגיעה במסך בשטח האלמנט

לכל ממשק שכזה יש מתודת CallBack בשם {On{X, שאותה יש לדרוס כדי לומר לאובייקט כיצד להתנהג.
למשל:

• למשל, עבור הממשק View.OnClickListener, יש להגדיר את מתודת ()onClick שתפקידה לבצע פעולה בתגובה לאירוע לחיצת עכבר בשטח האלמנט.

לאחר שיצרנו את הממשק ודרסנו את המתודה הרלוונטית, יש לקשר את אובייקט ה-View שלנו (למשל, הכפתור) אל ה-Listener המתאים, באמצעות המתודה (setOnClickListener(OnClickListener x.

בסרטון הווידאו ניתן לראות דוגמא לביצוע מתודולוגיה כזו. באחד הניסויים במעבדה תתאמנו על מימוש נוסף של המתודולוגיה.

תפריטים

תפריטים הינם חלק חשוב בממשק המשתמש. בעזרתם, ניתן לראות ולשנות את ההגדרות של האפליקציה, וליצור ממשק פונקציונלי נוח בין המשתמש לאפליקציה.

ישנם סוגים רבים של תפריטים. בתדריך זה נדון בשני סוגים מרכזיים – תפריט אופציות (Options Menu), וכן תפריט תכן (Context Menu).

OPTIONS MENU

תפריט האופציות הוא האוסף העיקרי של פריטי תפריט בשביל Activity ספציפי. בתפריט הזה כדאי למקם פריטים שיש להם השפעה גלובלית על האפליקציה – כגון "חיפוש" ו"הגדרות".

דוגמא

Options menu example

Options menu

CONTEXT MENU

Context Menu הינו תפריט צף שמופיע כאשר המשתמש לוחץ על אלמנט כלשהו (GUI Widget), שעליו הוגדר התפריט לחיצה ארוכה. כדאי שהתפריט יספק פעולות שניתן לייחס לאלמנט שעליו לחצנו.

דוגמא

Context menu example

תפריט Context Menu שצץ בלחיצה ארוכ לTextView

הגדרת תפריט

כדי להגדיר תפריט יש ליצור אובייקט שמייצג את התפריט (מופע של ContextMenu / Menu וכן מופעי MenuItem רלוונטיים), ולממש לכל הפחות מספר מתודות של המחלקה Activity ב-Activity הרלוונטי. להלן נפרט אותן ומה כל אחת עושה:

• עבור תפריט אופציות: (onCreateOptionsMenu(Menu menu – מתודה זו מאתחלת את תוכן התפריט הראשי של ה-Activity עם התפריט שלנו. כך ניתן להגדיר את הרכיבים שרוצים לכלול בתפריט. התפריט נכנס באופן אוטומטי לעץ ה-Views עם פריטי התפריט.

• עבור תפריט Context המתודה הרלוונטית היא ()onCreateContextMenu והיא מקבלת מעט יותר פרמטרים.

הטיפול באירועים ורישום המאזינים לאירועי התפריט נעשה ע"י התפריט עצמו ואין צורך לטפל בו. כאשר נבחר פריט בתפריט, מתבצעת קריאה למתודת ה-CallBack הבאות, בהתאמה:

()onOptionsItemSelected

()onContextItemSelected

יש להגדיר מתודות אלו לכל MenuItem.

• עבור תפריט Context יש לקשר את ה-View אל התפריט באמצעות קריאה למתודה (registerForContextMenu(View view.

ישנן, כמובן, מתודות נוספות שמאפשרות פעולות נוספות (כגון עדכון התפריט במהלך ריצת ה-Activity, ועוד), ועליהן ניתן לקרוא ב-API.

נעיר גם כי קיימת אפשרות להגדיר את רכיבי התפריט ישירות בקובץ ה-XML.

אחסון נתונים וקבצים


App-specific storage

• אחסון ספציפי לאפליקציה: אחסן קבצים שנועדו לשימוש האפליקציה שלך בלבד, או בתיקיות מוקדשות בתוך נפח אחסון פנימי או בתיקיות מוקדשות שונות בתוך אחסון חיצוני. השתמש בתיקיות בתוך האחסון הפנימי לשמירת מידע רגיש שאפליקציות אחרות לא אמורות לגשת אליו.

Shared storage

• אחסון משותף: אחסן קבצים שהאפליקציה שלך מתכוונת לשתף עם אפליקציות אחרות, כולל מדיה, מסמכים וקבצים אחרים.

Shared Preferences

• העדפות: אחסן נתונים פרימיטיביים ופרטיים בזוגות מפתח-ערך

Databases

• מסדי נתונים: אחסן נתונים מובנים במסד נתונים פרטי באמצעות ספריית Room.

יצירת אפליקציה ב - ANDROID STUDIO

בניסוי נפתח, נדבר ונריץ את האפליקציות בעזרת תוכנת ה-Android Studio המשמשת את רוב המפתחים שכותבים קוד ל-Android.

למערכת זו יש תמיכה רבה לפיתוח על גבי מערכת Android והיא מספקת פתרונות נוחים.

יצירת פרויקט חדש

הפעל את Android Studio

Screan start new project

ובחר: New Project


כעת יופיע החלון הבא:

Screan start new projec

יש לבחור Empty Views Activity כבסיס ולחצו על הכפתור Next:


כעת יופיע החלון הבא:

Screan start new projec

יש לבחור בשם האפליקציה (Name) ושם מייצג, שם הפרויקט במקרה שלנו יהיה זהה ויתעדכן אוטומטית.

המוסכמה היא ששם החבילה הוא שם ה-Domain של הארגון בצורה הפוכה, בתוספת מזהה אחד או יותר.
השם חייב להיות שם תקין של Java Package (ללא מקפים וכו').

ה-SDK המומלץ למעבדה הוא אחת הגרסאות היציבות האחרונות המופיעות אצלכם ברשימת ה-API (למשל אחת מגרסאות Android 13–15). אין חובה לבחור בדיוק את אותה גרסה שמופיעה בצילומי המסך, כל עוד הפרויקט נבנה ורץ בהצלחה.

לאחר שבחרתם שם אפליקציה, Package ו-SDK – ניתן ללחוץ Finish.

נוצר לנו פרויקט Android חדש שמפעיל Activity ובו כתוב !Hello World.

סביבת העבודה

כעת נעשה קצת סדר בדברים שלמדנו עד כה ונראה כיצד הם באים לידי ביטוי בסביבת העבודה:

Project structure

/java: ספריה עם קבצי קוד המקור (Java) של האפליקציה אותם יש צורך לשנות.

/res: מלשון Resources. כאן אנו שמים את קבצי המשאבים. המשאבים המרכזיים הם:
• משאבי layout – קבצי ה-XML שמגדירים את ממשק המשתמש. פתיחת קובץ ה-Layout תציג לנו ממשק גרפי נוח לעיבוד, וכן ניתן לגשת ישירות לקוד.
• משאבי values – כגון מחרוזות והגדרות של צבעים וגפנים.

/manifests/AndroidManifest.xml: זהו קובץ ההצהרה כפי שפורט קודם. הקובץ הזה מפרט את החלקים השונים הקשורים באפליקציה (Activities, מאפייני מעטפת והרשאות). בפרויקטים שתכתבו במעבדה, השינוי הידני העיקרי שתבצעו בו הוא הוספה או הסרה של הרשאות לפי הצורך.

הרצת האפליקציה

ניתן להשתמש בכלי אמולציה – Emulators, שמדמים את הפעולה של מכשיר ה-Android על גבי המחשב האישי שלכם.

כעת נראה כיצד נגדיר אמולטור ונריץ עליו את התכנית הבסיסית שלנו:

לחצו על תפריט AVD Manager - Tools.

Enter to AVD menu

בחלון שעולה, לחצו Create Virtual Device

Create Virtual Device

יופיע החלון הבא:

Select phone

נא לבחור Phone סטנדרטי מתוך הרשימה (למשל אחד ממכשירי Pixel) וללחוץ על הכפתור Next:


במסך הבא, בחרו גרסת Android יציבה מהרשימה (מומלץ אחת מהגרסאות האחרונות המסומנות Recommended או Stable). בדוגמאות בדף זה נעשה שימוש ב-Pie – API 28, אבל ניתן לבחור גם גרסה חדשה יותר. אם הגרסה עדיין לא מותקנת, לחצו על Download.

Select Android version

לחצו על הכפתור Next:


עדכנו כרצונכם את השם

Select Android version

ולחצו Finish.


כעת יופיע בפניכם החלון המקורי רק שתוכלו לבחור במכשיר הוירטואלי שיצרתם. לחצו על המכשיר, ולאחר מכן לחצו Start ו-Launch. המתינו עד שהתפריט הראשי של המכשיר עולה.

Project structure

כעת נוכל לחזור לפרויקט שיצרנו ולהריץ אותו על האמולטור, באופן הבא:

Run - Run app

Start android application

ו

Start android application

בתפריט שיעלה תוכלו לבחור את האמולטור שיצרתם, ולאחר ההרצה תוכלו לעבור לחלון האמולטור ולראות את התכנית רצה:

Project structure

נתחיל בגישה למשאבים השונים, ולאחר מכן נתבונן בקוד.

משאבים

MANIFEST 

במעבדה זו, אנחנו משתמשים בערכת נושא המבוססת על AppCompat (Theme.AppCompat.DayNight). בעולם התעשייה נפוצות היום גם ערכות נושא חדשות יותר (למשל Material 3), אך לצורך מעבדת הבסיס שלנו ערכת נושא זו מספיקה, ומפשטת את העבודה עם Activities ו־Layouts ב־XML.

כדי להתאים את האפליקציה שלכם למעבדה, מומלץ לוודא שני דברים עיקריים בקובץ AndroidManifest.xml:
1. שהפעילות הראשית (MainActivity) מוגדרת עם Intent Filter מתאים מסוג MAIN/LAUNCHER.
2. שהוגדרה ערכת נושא מתאימה (Theme.AppCompat.DayNight או ערכת נושא מותאמת שיורשת ממנה).

לדוגמה:

		
	<activity
		android:name=".MainActivity"
		android:exported="true"
		android:theme="@style/Theme.AppCompat.DayNight">
		<intent-filter>
			<action android:name="android.intent.action.MAIN" />
			<category android:name="android.intent.category.LAUNCHER" />
		</intent-filter>
	</activity>

		
VALUES

אם ניכנס לקובץ Strings.xml נוכל לראות את המחרוזות השונות שמרכיבות את האפליקציה: החל משם האפליקציה "Example".

כפי שניתן לראות, אפשר בקלות לשנות את הערכים הללו מבלי לגשת לקוד. זהו תכנות נכון שמקל בצורה משמעותית על המודולריות וחוסר הצימוד של הקוד.

כאשר רוצים להוסיף מחרוזת לממשק, עדיף להוסיף אותה כמשאב בקובץ המשאבים. בכך מרכזים את כל המחרוזות במקום אחד, וקל יותר לעדכן את הטקסט או לספק אלטרנטיבות לשפות שונות.

את המחרוזות שמים בקובץ res/values/strings.xml.

תוכלו לעבור בין הגרסה הטקסטואלית של צפייה בקובץ לגרסה הגראפית (open editor), ולעבוד עם כל אחת – בהתאם לנוחות.

LAYOUT

כעת נעבור לחלון ה-Layout המרכזי ונראה את האפשרויות שהוא מציע:

Project workspace

במרכז מופיע ה-Layout כפי שמתואר.

משמאל למעלה נתן לראות את ה-Palette: מכאן ניתן לבחור אלמנטים גרפיים מהסוג View ו-ViewGroup (כפי שהוסבר בתחילת התדריך).

משמאל למטה מופיע ה-Outline: זהו קיבוץ כל ה-ViewGroups שקובעים את סידור ה-Views (ראו דיאגרמה בתדריך). בשלב זה יש לנו ConstraintLayout אחד שמכיל בתוכו אלמנט TextView של תצוגת טקסט.

אם נלחץ (במסך המרכזי) על הטקסט, נוכל לראות את השדות והערכים השונים שלו מימין. שני פרטים חשובים הם –

ID property

1. Id – זוכרים שאמרנו שלכל אלמנט יש ID ייחודי? שדה זה הוא מאוד חשוב, ועוד על כך בוידאו. נא להוסיף id (לדוגמה txtHello).

2) גלילה מעט למטה תראה לנו את השדה Text.

נא למחוק את הטקסט Hello World! ולחץ על הסימן מימין השדה, בחלון שנפתח לחצו על + ו String Value.

Add String

למלא שדות Resource name ו Resource Value

Add String

לאחר שתלחצו על אישור זה יקשר את ה-TextView למחרוזת @string/hello_world, שנראה גם – ב-Values! שוב, שימו לב למודולריות וחוסר התלות בין הקוד, למחרוזות, ולממשק הגרפי.

כעת נלחץ על ה-Layout מימין למעלה ונשים לב לעוד כמה שדות חשובים:

Width, Height: ניתן להכניס גודל ספציפי בגדלים שונים או ערכים יחסיים כגון fill_parent, match_parent, fill_content. דוגמא לערכים אלו ניתן לראות בוידאו.

תוכלו לעבור בין הגרסה הטקסטואלית של צפייה בקובץ לגרסה הגראפית, ולעבוד עם כל אחת – בהתאם לנוחות.

קוד

כעת עיברו לקובץ הקוד MainActivity.java.

ניתן לראות את המחלקה MainActivity שיורשת ממחלקת האב Activity ודורסת את המתודה החשובה ()onCreate. ניתן לראות קריאה למתודה של מחלקת האב (חשוב), ולאחר מכן קריאה למתודה ()setContentView עם ה-ID של קובץ ה-XML Layout שראינו קודם – activity_main. כך שכאשר התכנית עולה, המסך שמוצג לנו הוא ה-Layout שיצרנו!


	  
	public class MainActivity extends AppCompatActivity {
	
	@Override
		protected void onCreate(Bundle savedInstanceState) {
			super.onCreate(savedInstanceState);
				EdgeToEdge.enable(this); // תומך בתצוגה Edge-to-Edge
				setContentView(R.layout.activity_main);

				ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
					Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
					v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
					return insets;
				});
			}
	}
	  
	  

למה נוסף קוד זה?
(EdgeToEdge.enable(this: פונקציה זו מתאימה את התצוגה כך שהאפליקציה תמלא את כל שטח המסך, כולל אזורים ליד החריצים. ViewCompat.setOnApplyWindowInsetsListener: מאפשרת לטפל ברווחים מערכתיים (לדוגמה, Status Bar או Navigation Bar) ולהוסיף Padding לאלמנטים בעיצוב. פונקציות אלו מועילות כאשר מעצבים ממשקי משתמש מתקדמים למכשירים מודרניים. עם זאת, עבור לימוד בסיסי, הן אינן נחוצות.

איך לפשט את הקוד?
מומלץ למחוק שורות שאינן נחוצות ולהשאיר קוד מינימליסטי ופשוט יותר. הקוד הסופי ייראה כך:


	  
	public class MainActivity extends AppCompatActivity {

		@Override
		protected void onCreate(Bundle savedInstanceState) {
			super.onCreate(savedInstanceState);
			setContentView(R.layout.activity_main); // הגדרת התצוגה הראשית
		}
	}
	  
	  

שדרוגים ושינויים

כעת נתרגל מעט שינויים קטנים ו'על הדרך' נתייחס לעוד כמה שדות חשובים של משאבים.

ממשק גרפי ומשאבים

נרצה להוסיף לאפליקציה שלנו שדה טקסט וכפתור.

ראשית נוסיף שלוש מחרוזות לקובץ ה-(Values..(string.xml נוסיף את שלוש השורות הצבועות בצהוב ישירות לקובץ ה-XML (ניתן גם לבצע זאת באמצעות הממשק הגרפי), כך שכעת הקובץ נראה כך:

		
	<resources>
    	<string name="app_name">My Application</string>
    	<string name="helloworld">Hello World!</string>
    	<string name="title_activity_main">MainActivity</string>
    	<string name="edit_message">Enter a Message</string>
    	<string name="button_send">Send!</string>
	</resources>

		

נחזור לקובץ ה-activity_main.xml) Layout), נעבור למצב עריכת טקסטואלי ונמחק 4 שורות:

		
	<TextView
        android:id="@+id/txtHello"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/helloworld"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
		
		

לאחר מכן אנו חוזרים למצב הגרפי ונוסיף EditText וButton על Layout

שימו לב לסוג ה-Layout שלנו הוא ConstraintLayout

מה שמחייב את כל הווידג'טים להיות קשורים אליו או עם ווידג'טים אחרים, אפשר ליצור חיבורים בכמה דרכים, אותם ניתן לראות בסרטון הווידאו:

השלב הבא הוא הרצון שלנו לקשר את הכפתור ותיבת הטקסט עם המחרוזות שקבענו קודם. נלחץ על הכפתור, נלך לשדה Text ומשם נבחר את המחרוזת button_send שהגדרנו קודם – ובדומה גם בתיבת הטקסט נבחר את המחרוזת edit_message. כעת נקבל:

Layout with TextView, EditText and Button

נסכם את קובץ ה-Layout כפי שהוא נראה כרגע:

		
	<?xml version="1.0" encoding="utf-8"?>
	<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    	xmlns:app="http://schemas.android.com/apk/res-auto"
    	xmlns:tools="http://schemas.android.com/tools"
    	android:layout_width="match_parent"
    	android:layout_height="match_parent"
    	tools:context=".MainActivity">

    	<TextView
        	android:id="@+id/txtHello"
        	android:layout_width="wrap_content"
        	android:layout_height="wrap_content"
        	android:layout_marginTop="16dp"
        	android:text="@string/helloworld"
        	app:layout_constraintEnd_toEndOf="parent"
        	app:layout_constraintStart_toStartOf="parent"
        	app:layout_constraintTop_toTopOf="parent" />

    	<EditText
        	android:id="@+id/editTextTextPersonName5"
        	android:layout_width="wrap_content"
        	android:layout_height="wrap_content"
        	android:layout_marginStart="16dp"
        	android:layout_marginTop="16dp"
        	android:ems="10"
        	android:inputType="textPersonName"
        	android:text="@string/edit_message"
        	app:layout_constraintStart_toStartOf="parent"
        	app:layout_constraintTop_toBottomOf="@+id/txtHello" />

    	<Button
        	android:id="@+id/button5"
        	android:layout_width="wrap_content"
        	android:layout_height="wrap_content"
        	android:layout_marginStart="8dp"
        	android:layout_marginTop="16dp"
        	android:text="@string/button_send"
        	app:layout_constraintStart_toEndOf="@+id/editTextTextPersonName5"
        	app:layout_constraintTop_toBottomOf="@+id/txtHello" />

	</androidx.constraintlayout.widget.ConstraintLayout>
		
		

שדה נוסף שחשוב להכיר הוא השדה android:hint שמכיל את המחרוזת שתופיע בתיבת טקסט, כאשר היא ריקה.
תשנו את שורה: "android:text="@string/edit_message ל "android:hint="@string/edit_message ותראו מה יקרה.

שימו לב שאם תריצו כרגע את התכנית על גבי האמולטור, תוכלו לראות את שדה הטקסט והכפתור. כמובן שכרגע הם לא עושים כלום – ואנו נרצה לשנות זאת!

נרצה, שבעת לחיצת כפתור, נעבור ל-Activity חדש שיציג את הערך שהכנסנו בתיבת הטקסט.

נעבור לקובץ ה-Java ונכריז על הכפתור ותיבת הטקסט :

נצהיר עליהם כמשתני מחלקה private:

	  
	private Button button;
    	private EditText editText;
	  
	  

נאתחל אותם (נקשר בינם לבין ה-Views שלהם) במהלך המתודה ()onCreate, באמצעות המתודה ()findViewById:

	  
	button = findViewById(R.id.button5);
        editText = findViewById(R.id.editTextTextPersonName5);
	  
	  

נגדיר Intent שיעביר את הנתונים מה-Activity הנוכחי ל-Activity החדש (התעלם משגיאה לגבי -DisplayMessageActivity.class):

	
	final Intent intent = new Intent(this, DisplayMessageActivity.class);
	
	

נגדיר לכפתור פעולת Listener, ובתוכה ניצור מופע חדש של OnClickListener בו:

• נקשר בין ה-Intent ל-Activity החדש (נקרא לו בשם DisplayMessageActivity)

• נוסיף ל-Intent את המידע הדרוש (מחרוזת שניקח משדה הטקסט)

• נאתחל את ה-Intent:

	  
	button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String message = editText.getText().toString();
                intent.putExtra(EXTRA_MESSAGE, message);
                startActivity(intent);
            }
        });
	  
	  

ה-Intent מעביר איתו זוגות של מפתח-ערך הנקראים extras. המתודה ()putExtra מקבלת כמפתח מחרוזת, ואת הערך כפרמטר שני. יש להגדיר את המפתח כקבוע בתוך MyFirstActivity:

	
	public final static String EXTRA_MESSAGE = "secret massage";
	
	

הקוד הסופי שאמורים לקבל הוא כדלהלן: (שימו לב שיש לבצע Import ללא מעט חבילות!)

	
	package com.ee.nssl.myapplication;

	import androidx.appcompat.app.AppCompatActivity;

	import android.content.Intent;
	import android.os.Bundle;
	import android.view.View;
	import android.widget.Button;
	import android.widget.EditText;

	public class MainActivity extends AppCompatActivity {

    	private Button button;
	private EditText editText;
    	public final static String EXTRA_MESSAGE = "secret massage";

	    @Override
    	protected void onCreate(Bundle savedInstanceState) {
        	super.onCreate(savedInstanceState);
	        setContentView(R.layout.activity_main);

		button = findViewById(R.id.button5);
        	editText = findViewById(R.id.editTextTextPersonName5);

	        final Intent intent = new Intent(this, DisplayMessageActivity.class);

    	    button.setOnClickListener(new View.OnClickListener() {
        	    @Override
            	public void onClick(View view) {
                	String message = editText.getText().toString();
                	intent.putExtra(EXTRA_MESSAGE, message);
                	startActivity(intent);
            		}
        		});

    		}
	}

	

כעת נותר ליצור את ה-Activity החדש, באופן הבא:

ניצור את המחלקה DisplayMessageActivity שיורשת מ-Activity.

נעדכן ב-Activity את הקוד הבא:

• "נשאב" את ה-Intent שעבר אלינו:

	
	Intent intent = getIntent();
        String message = intent.getStringExtra(MainActivity.EXTRA_MESSAGE);
	
	

אם צורך  נדרוס את המתודה ()OnCreate (אין זה אמור להפתיע אתכם, שכן *כל* Activity דורס מתודה זו!) באופן שבו תציג את המחרוזת על המסך.

הקוד הסופי שאמורים לקבל הוא כדלהלן: (שימו לב שיש לבצע Import ללא מעט חבילות!)

	
	package com.ee.nssl.myapplication;

	import androidx.appcompat.app.AppCompatActivity;

	import android.content.Intent;
	import android.os.Bundle;
	import android.widget.TextView;

	public class DisplayMessageActivity extends AppCompatActivity {

    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
        	super.onCreate(savedInstanceState);


        	Intent intent = getIntent();
        	String message = intent.getStringExtra(MainActivity.EXTRA_MESSAGE);

        	TextView textView = new TextView(this);
        	textView.setTextSize(40);
        	textView.setText(message);
        	setContentView(textView);

    		}
	}
	
	

הריצו את האמולטור ובידקו שהכל עובד כמו שצריך!

כעת נראה דוגמאות כלליות למספר דברים חשובים נוספים:

החזרת ערך מ-ACTIVITY

שליחת הערך

ניתן לאתחל Activity ולצפות לערך שיחזור כתוצאה. למשל, אפשר לאתחל אפליקציית צילום ולקבל את התמונה שצולמה כערך חוזר.

כדי לבצע זאת, יש להחליף את המתודה ()startActivity במתודה ()startActivityForResult.

למתודה זו נוסף ארגומנט מסוג Integer שהוא "request code" שמזהה את הבקשה.

כאשר מתקבל ה-Intent של התוצאה, המתודה המטפלת מספקת את אותו הקוד כך שהאפליקציה תדע איך לטפל בו. את הערך שחזר שולפים בעזרת מתודת ה-Callback בשם ()onActivityResult.

דוגמא לקוד שמאתחל Activity חדש עם המתודה ()StartActivityForResult

	
	static final int PICK_CONTACT_REQUEST = 1;
	...
	private void pickContact() {
        Intent pickContactIntent = new Intent(Intent.ACTION_PICK, Uri.parse("content://contacts"));
        pickContactIntent.setType(ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE);
        // Show user only contacts w/ phone numbers
        startActivityForResult(pickContactIntent, PICK_CONTACT_REQUEST);
    	}
	
	

קבלת התוצאה

כאשר חוזרים מה-Activity המשני, יש לקרוא למתודה ()onActivityResult ולספק לה שלושה פרמטרים:

• הקוד שנשלח לזיהוי הבקשה.

• קוד תוצאה שחזר, המציין אם הייתה הצלחה או ביטול תוצאה.

• Intent הנושא עימו את מידע התוצאה.

דוגמא לקוד המקבל תוצאה מה-Activity שאותחל והסתיים:

	
	@Override
	protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if(requestCode == PICK_CONTACT_REQUEST){
            // Make sure the request was successful
            if(resultCode==RESULT_OK){
                // The user picked a contact.
                // The Intent's data Uri identifies which contact was selected.
                // Do something with the contact
            }
        }
    }
	
	

תרגילים

תרגיל דוגמא 1

מטרה:

במשימה זו ניצור אפליקציית Hello World.

כאשר לוחצים על כפתור ה-Menu ניתן לבחור באופציה "Change String" שתשנה את הכתוב ל-Goodbye World.

שלבים לפתרון:

• יצירת פרויקט בדומה לתרגיל המודרך

• יצירת מחרוזות חדשות "Goodbye World" ו-"Change String" ב-strings.xml

• אין צורך לעדכן את ה-default layout – מלבד לתת ID ל-TextView

בקוד המקור:

• הגדרת משתנה private מסוג TextView

• במתודה ()onCreate, חיבור בין המשתנה ל-View באמצעות ה-ID

• הגדרת משתנה מחלקה סטטי קבוע MENU_CHANGE_STRING עבור הפריט בתפריט.

• הוסיפו את קובץ XML לres, File-New-Android Resource File

Add resorce file for menu

• הוסיפו את Menu item ותגדירו id וTitle

Add resorce file for menu

דריסת המתודה ()onCreateOptionsMenu ו()OnOptionsItemSelected והוספת ההתנהגות שמשנה את הטקסט.

להלן קוד ה-Java לעיונכם:

		
		package com.ee.nssl.example_01;

	import androidx.annotation.NonNull;
	import androidx.appcompat.app.AppCompatActivity;

	import android.os.Bundle;
	import android.view.Menu;
	import android.view.MenuInflater;
	import android.view.MenuItem;
	import android.widget.TextView;

	public class MainActivity extends AppCompatActivity {

    	private TextView textView;

	    @Override
    		protected void onCreate(Bundle savedInstanceState) {
        	super.onCreate(savedInstanceState);
        	setContentView(R.layout.activity_main);
	        textView = findViewById(R.id.txtShowText);
    	}

	    @Override
    		public boolean onCreateOptionsMenu(Menu menu) {
        	MenuInflater inflater = getMenuInflater();
	        inflater.inflate(R.menu.options_menu, menu);
    	    	return super.onCreateOptionsMenu(menu);
	    }

    	@Override
	    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
    	    	if (item.getItemId() == R.id.change) {
            		textView.setText(this.getString(R.string.goodbyeworld));
                	return true;
		}
			
    	    	return super.onOptionsItemSelected(item);

	    }
	}
		
		

פרויקט מלא ב- Github

תרגיל דוגמא 2

מטרה:

בתרגיל זה נשתמש באלמנטי טקסט, כפתור, ותיבת טקסט. בעת לחיצה על הכפתור, הטקסט ישנה את הערך שלו לערך שכתוב בתיבת הטקסט.

שלבים לפתרון:

• יצירת מחרוזת רלוונטית

• הגדרת קובץ Layout XML עם TextView, EditText ו-Button מתאימים ומתן ID הולם. חיבור בין המחרוזת שניתנה לשם הכפתור, לבין הכפתור.

בקוד המקור:

• הגדרת משתני private מסוג TextView, EditText ו-Button.

• במתודה ()onCreate, חיבור בין המשתנים לאלמנטי View באמצעות ה-ID

• הוספת מאזין לכפתור באמצעות ()SetOnClickListener ויצירת OnClickListener חדש ובתוכו כתיבת המתודה ()onClick.

קובץ ה-XML של ה-Layout:

		
	<?xml version="1.0" encoding="utf-8"?>
	<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    	xmlns:app="http://schemas.android.com/apk/res-auto"
    	xmlns:tools="http://schemas.android.com/tools"
    	android:layout_width="match_parent"
    	 android:layout_height="match_parent"
    	tools:context=".MainActivity">

    	<TextView
    	    android:id="@+id/txtShowText"
    	    android:layout_width="wrap_content"
    	    android:layout_height="wrap_content"
    	    android:text="Hello World!"
    	    app:layout_constraintBottom_toBottomOf="parent"
    	    app:layout_constraintLeft_toLeftOf="parent"
     	   app:layout_constraintRight_toRightOf="parent"
    	    app:layout_constraintTop_toTopOf="parent" />

    	<EditText
        	android:id="@+id/edtEnterText"
        	android:layout_width="wrap_content"
        	android:layout_height="wrap_content"
        	android:layout_marginStart="16dp"
        	android:layout_marginTop="16dp"
        	android:ems="10"
        	android:hint="@string/entertext"
        	android:inputType="text"
        	app:layout_constraintStart_toStartOf="parent"
        	app:layout_constraintTop_toTopOf="parent"
        	android:autofillHints="" />

    	<Button
        	android:id="@+id/btnMagic"
        	android:layout_width="wrap_content"
        	android:layout_height="wrap_content"
        	android:layout_marginStart="16dp"
        	android:layout_marginTop="16dp"
        	android:text="@string/magic"
        	app:layout_constraintStart_toEndOf="@+id/edtEnterText"
        	app:layout_constraintTop_toTopOf="parent" />

	</androidx.constraintlayout.widget.ConstraintLayout>
		
		

קוד המקור:

		
		package com.ee.nssl.example_02;

	import androidx.appcompat.app.AppCompatActivity;

	import android.os.Bundle;
	import android.view.View;
	import android.widget.Button;
	import android.widget.EditText;
	import android.widget.TextView;

	public class MainActivity extends AppCompatActivity {

    	private TextView textView;
    	private EditText editText;
    	private Button button;

    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
        	super.onCreate(savedInstanceState);
        	setContentView(R.layout.activity_main);

        	textView=findViewById(R.id.txtShowText);
        	editText=findViewById(R.id.edtEnterText);
        	button=findViewById(R.id.btnMagic);

        	button.setOnClickListener(new View.OnClickListener() {
            	@Override
            	public void onClick(View view) {
                	textView.setText(editText.getText());
            	}
        	});
    	}
	}
		
		

פרויקט מלא ב- Github

טיפים קטנים ושימושיים

Android Studio היא תוכנה מצוינת שנועדה למפתחים עצלנים – נצלו את יכולותיה:

• כאשר מילה מסומנת באדום, שימו עליה את הסמן. במרבית המקרים Android Studio תציע לכם לעשות import ל־package הרלוונטי והבעיה תיפתר (Alt+Enter).

• שימו לב לסיים הצהרה של אובייקט חדש בנקודה־פסיק – את זה Android Studio לא מוסיפה באופן אוטומטי.

• לחיצה על Ctrl+Space משלימה אוטומטית מילים ומציעה לכם את האפשרויות השונות (אוטו־קומפליט).

• השתמשו ב־Logcat כדי לאתר באגים: אפשר לכתוב Log.d("TAG", "message") ולחפש לפי ה־TAG בחלון Logcat.
שגיאות (Exceptions) יופיעו שם עם stack trace – נסו לקרוא ולהבין איפה בקוד שלכם הבעיה.

• אם האפליקציה קורסת בעת ההרצה, חפשו קודם כל את השורה הראשונה של ה־Exception ב־Logcat לפני שאתם ניגשים למתרגל.
ברוב המקרים היא מצביעה בדיוק על השורה הבעייתית בקוד

Keyboard Shortcuts

דו"ח מכין – שאלות להגשה

ענה בקצרה על השאלות הבאות. אפשר להיעזר בחומר התדריך, בסרטון המצורף ובתיעוד הרשמי של Android (developer.android.com).
חלק מהתשובות אינן מופיעות במפורש בתדריך, ותידרש מכם מעט חקירה עצמאית.

1. הסבר מה תפקידה של המתודה ()setContentView. מדוע חייבים לקרוא לה בכל Activity, ומה הקשר בינה לבין קובצי ה־Layout ב־XML?

2. הסבר כיצד מעבירים פרמטרים מ־Activity אחד לאחר באמצעות Intent ו־extras. תן דוגמה לסוג המידע שניתן להעביר כך.

3. הסבר כיצד מוסיפים לאפליקציה הרשאת גישה לאינטרנט בקובץ AndroidManifest.xml, והסבר בקצרה מה ההבדל בין הרשאת INTERNET (normal) לבין הרשאת CAMERA (dangerous).

4. הסבר מה עושה המתודה ()setOnClickListener וכיצד היא קשורה לממשק View.OnClickListener.

5. מה ההבדל בין Service ל־Activity? ציין לפחות שני הבדלים עיקריים.

6. הסבר מהם שני סוגי התפריטים העיקריים (Options Menu ו־Context Menu) ומה ההבדל ביניהם.

7. מה תפקידו של קובץ ה־R וכיצד הוא נוצר? מדוע לא כותבים אותו ידנית?

8. הסבר כיצד משנים את שם התוכנית כפי שהוא מופיע למשתמש במסך האפליקציות במכשיר.

9. הסבר כיצד מוגדר ממשק המשתמש בתוך ה־Activity: מה כתוב בקובצי ה־XML, ומה נעשה בקוד ה־Java?

10. הסבר כיצד קבצי המשאבים (Resources) תורמים להנדסת תוכנה נכונה, במיוחד בהקשר של מודולריות ותמיכה במספר שפות.

11. תאר את מחזור החיים של Activity: ציין את המצבים המרכזיים (onCreate, onStart, onResume, onPause, onStop, onDestroy) ושרטט דיאגרמת מצבים המתארת באילו מצבים הוא יכול להימצא ואת המעברים ביניהם.

12. הסבר אודות אובייקט מסוג Intent: מה תפקידו, אילו סוגי Intents קיימים (למשל explicit לעומת implicit), ומה ניתן לבצע באמצעות כל סוג.

13. ציין מה מציינים ראשי התיבות הבאים בהקשר של תכנות Android:

• APK
• API
• AVD