מדריך למתחילים · Activity ו-Intent · שיעור כפול

חידון אמת או שקר
שני מסכים, מסר אחד

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

מה בונים היום: מסך שאלה עם שני כפתורים, "אמת" ו"שקר". לחיצה פותחת מסך שני שמודיע אם צדקתם. קודם חוזרים על LinearLayout מהמדריך הקודם, ואז מכירים את Activity (מסך), את Intent (בקשה לעבור מסך), ואת putExtra (מידע שנוסע עם הבקשה).
ניקוד

איך צוברים נקודות

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

חלקמה עושיםנקודותזמן משוער
חזרהמסך השאלה, מהזיכרון1020 דק'
הפרויקטמסך תוצאה, מעבר בין המסכים והעברת התשובה4045 דק'
נקודת בדיקהמסבירים למורה שלושה דברים קצרים1010 דק'
הרחבה 1צבע לפי התוצאה10בזמן שנשאר ובבית
הרחבה 2הסבר לתשובה15
הרחבה 3שאלה שנייה15
מסלול מתקדםשלוש משימותבונוס עד 30
חוק אחד חשוב

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

חלק 1

חזרה: בונים את מסך השאלה

  1. צרו פרויקט חדש עם שם משמעותי - למשל TrueFalseQuiz. השם באנגלית בלבד, בלי רווחים ובלי תווים מיוחדים. תבנית Empty Views Activity, שפה Java, ‏Minimum SDK ‏API 26, ‏Build configuration language ‏Groovy DSL.
  2. חובה, לפני שמוסיפים רכיב כלשהו: פתחו את activity_main.xml במצב Code, מחקו את כל התוכן שלו והדביקו במקומו את שלד ה-LinearLayout שמופיע מתחת לרשימה.
  3. בתוך השלד הוסיפו TextView עם id בשם tvQuestion: משפט על הנושא שלכם שאפשר לענות עליו "אמת" או "שקר", גודל טקסט 24sp, רוחב match_parent, והטקסט ממורכז.
  4. מתחתיו הוסיפו שורה אופקית (LinearLayout עם horizontal) ובתוכה שני כפתורים: btnTrue עם הטקסט "אמת" ו-btnFalse עם הטקסט "שקר". כל כפתור תופס חצי מרוחב השורה.
  5. לחיצה על כל כפתור מציגה Toast: "בחרת אמת" או "בחרת שקר".
  6. ב-colors.xml הוסיפו צבע בשם screen_bg והגדירו אותו כרקע המסך.
  7. הריצו ובדקו.

השלד שמדביקים ב-activity_main.xml

res/layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    android:padding="16dp">

    <!-- את הרכיבים שלכם מוסיפים כאן, בין שתי התגיות -->

</LinearLayout>

השלד זהה לזה של המדריך הקודם, עם שורה אחת חדשה: android:gravity="center" על הקופסה הראשית. היא ממרכזת את כל התוכן באמצע המסך.

בוחרים Empty Views Activity, לא Empty Activity

במסך בחירת התבנית יש שתי תבניות עם שם כמעט זהה, זו ליד זו. הנכונה היא Empty Views Activity, עם המילה Views.

איך מזהים שבחרתם לא נכון: במסך הבא אין שדה Language, ומתחת לכותרת כתוב "Jetpack Compose". במקרה כזה לחצו Previous, בחרו Empty Views Activity, ורק אז בחרו Java.

אל תמחקו את השורה android:id="@+id/main"

ב-MainActivity.java יש שורה ש-Android Studio כתב בשבילכם ומחפשת רכיב בשם main. בלי ה-id הזה האפליקציה לא תיבנה.

רמז: שורה אופקית עם שני כפתורים חצי-חצי

זה אותו רעיון של שתי העמודות מלוח התוצאות: רוחב 0dp ומשקל שווה.

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="24dp"
    android:orientation="horizontal">

    <Button
        android:id="@+id/btnTrue"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:layout_marginEnd="8dp"
        android:text="אמת" />

    <Button
        android:id="@+id/btnFalse"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="שקר" />

</LinearLayout>
רמז: הקוד של Toast בלחיצה על כפתור

ב-MainActivity.java, בתוך onCreate, אחרי השורות שכבר כתובות שם:

Button btnTrue = findViewById(R.id.btnTrue);
btnTrue.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Toast.makeText(MainActivity.this, "בחרת אמת", Toast.LENGTH_SHORT).show();
    }
});

מילה שמסומנת באדום: עמדו עליה, Alt+Enter, ‏Import class.

רמז: צבע רקע מ-colors.xml

ב-app > res > values > colors.xml, לפני </resources>:

<color name="screen_bg">#FFF8EE</color>

ובתגית הפותחת של ה-LinearLayout הראשי, כשורה נוספת לפני הסוגר >:

android:background="@color/screen_bg"
סיימתם את החזרה כש...

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

חלק 2

הסבר: Activity ו-Intent

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

Activity: מסך אחד

Activity הוא מסך אחד באפליקציה. כל Activity בנוי משני קבצים שעובדים יחד: קובץ XML שקובע איך המסך נראה, וקובץ Java שקובע מה המסך עושה. את הזוג הראשון אתם כבר מכירים: activity_main.xml ו-MainActivity.java. דימוי: חדר בבית. לכל חדר יש רהיטים משלו (ה-XML) וחוקים משלו (ה-Java).

Intent: בקשה לעבור מסך

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

MainActivity
מסך השאלה
Intent
←
ResultActivity
מסך התוצאה
new Intent(MainActivity.this, ResultActivity.class)
הפרמטר הראשון: מי מבקש. הפרמטר השני: לאן.

Extra: מידע שנוסע עם הבקשה

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

"isCorrect"
true
פתק אחד: המפתח הוא שם שאתם בוחרים, הערך הוא המידע עצמו.
ResultActivity (למעלה, זה מה שרואים)
MainActivity (מחכה מתחת)
מסך חדש נפתח מעל הקודם, כמו קלף שמונח על ערימה. finish() מסיר את הקלף העליון, והמסך שמתחתיו חוזר.
ועוד קובץ אחד שכדאי להכיר: Manifest

AndroidManifest.xml הוא "תעודת הזהות" של האפליקציה. רשומים בו כל המסכים שלה. מסך שלא רשום שם לא יכול להיפתח, והאפליקציה תקרוס. כשיוצרים מסך בדרך שתלמדו מיד, Android Studio רושם אותו בשבילכם.

חלק 3

בונים צעד-צעד

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

1

יוצרים מסך שני

בעץ הקבצים משמאל, פתחו את app > java ולחצו לחיצה ימנית על שם החבילה שלכם (השורה הראשונה, זו שבתוכה MainActivity).

בחרו New > Activity > Empty Views Activity. בחלון שנפתח:

  • Activity Name: ResultActivity
  • Layout Name: activity_result (מתמלא לבד)
  • Launcher Activity: לא מסומן
  • Source Language: Java

לחצו Finish.

מה קרה פה

Android Studio יצר שני קבצים חדשים: ResultActivity.java ו-activity_result.xml. זה הזוג של המסך השני. בנוסף הוא רשם את המסך ב-Manifest.

2

מציצים ב-Manifest

פתחו את app > manifests > AndroidManifest.xml. חפשו את השורות האלה:

<activity
    android:name=".ResultActivity"
    android:exported="false" />

זה הרישום של המסך החדש. לא משנים כלום, רק מוודאים שהוא שם. סגרו את הקובץ.

3

מעצבים את מסך התוצאה

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

פתחו את app > res > layout > activity_result.xml במצב Code. מחקו את כל התוכן והדביקו:

res/layout/activity_result.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/screen_bg"
    android:gravity="center"
    android:orientation="vertical"
    android:padding="16dp">

    <TextView
        android:id="@+id/tvResult"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="כאן תופיע התוצאה"
        android:textSize="36sp" />

    <Button
        android:id="@+id/btnBack"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:text="חזרה לשאלה" />

</LinearLayout>
מה כתבנו פה

אותו שלד של LinearLayout אנכי, עם שני רכיבים. גם כאן ה-id של הקופסה הראשית הוא main, כי גם ResultActivity.java מחפש אותו. שני מסכים יכולים להשתמש באותו id בלי בעיה, כי כל מסך מכיר רק את הקובץ שלו.

4

עוברים למסך התוצאה עם Intent

פתחו את MainActivity.java. מצאו את הקוד של btnTrue מהחזרה. בתוך onClick, מחקו את שורת ה-Toast וכתבו במקומה את שתי השורות של ה-Intent. כך צריך להיראות הקוד של הכפתור:

MainActivity.java
Button btnTrue = findViewById(R.id.btnTrue);
btnTrue.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Intent intent = new Intent(MainActivity.this, ResultActivity.class);
        startActivity(intent);
    }
});

אם Intent מסומן באדום: עמדו עליו, Alt+Enter, ‏Import class.

מה כתבנו פה

שורה ראשונה: כותבים את הבקשה. "אני, MainActivity, מבקש לפתוח את ResultActivity".

שורה שנייה: startActivity מוסר את הבקשה למערכת, והיא פותחת את המסך.

הריצו ובדקו

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

5

שולחים את התשובה עם putExtra

חלק ראשון: מסך השאלה צריך לדעת מהי התשובה הנכונה לשאלה שלכם. מיד אחרי השורה public class MainActivity extends AppCompatActivity { הוסיפו:

MainActivity.java (בראש המחלקה)
    boolean correctAnswer = false;

אם התשובה הנכונה לשאלה שלכם היא "אמת", כתבו true במקום false.

חלק שני: עדכנו את onClick של btnTrue. נוספו שלושה דברים: המשתנה userAnswer, בדיקת if שקובעת את isCorrect, ושורת putExtra.

MainActivity.java
btnTrue.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        boolean userAnswer = true;

        boolean isCorrect;
        if (userAnswer == correctAnswer) {
            isCorrect = true;
        } else {
            isCorrect = false;
        }

        Intent intent = new Intent(MainActivity.this, ResultActivity.class);
        intent.putExtra("isCorrect", isCorrect);
        startActivity(intent);
    }
});
מה כתבנו פה

userAnswer הוא מה שהמשתמש בחר. בכפתור "אמת" הוא true.

ה-if משווה את הבחירה לתשובה הנכונה. אם הן שוות, המשתמש צדק ו-isCorrect מקבל true. אחרת הוא מקבל false.

putExtra מצמיד ל-Intent פתק בשם "isCorrect", והערך שלו הוא מה שנמצא במשתנה isCorrect.

6

כפתור "שקר": עכשיו אתם

עדכנו את onClick של btnFalse באותה צורה. ההבדל היחיד: מה הערך של userAnswer כשהמשתמש לוחץ "שקר"?

נתקעתי: הקוד המלא של btnFalse
Button btnFalse = findViewById(R.id.btnFalse);
btnFalse.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        boolean userAnswer = false;

        boolean isCorrect;
        if (userAnswer == correctAnswer) {
            isCorrect = true;
        } else {
            isCorrect = false;
        }

        Intent intent = new Intent(MainActivity.this, ResultActivity.class);
        intent.putExtra("isCorrect", isCorrect);
        startActivity(intent);
    }
});
הריצו ובדקו

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

7

קוראים את התשובה במסך התוצאה

התוצאה הרצויה בסוף הצעדתשובה נכונה מציגה "צדקת!", תשובה שגויה מציגה "טעית...". באיור הרקע ירוק רק להמחשה, את הצבע תוסיפו בהרחבה 1.

פתחו את ResultActivity.java. בתוך onCreate, אחרי השורות שכבר כתובות שם ולפני הסוגר } שסוגר את onCreate, הוסיפו:

ResultActivity.java (בסוף onCreate)
        TextView tvResult = findViewById(R.id.tvResult);

        boolean isCorrect = getIntent().getBooleanExtra("isCorrect", false);

        if (isCorrect) {
            tvResult.setText("צדקת!");
        } else {
            tvResult.setText("טעית...");
        }
מה כתבנו פה

getIntent() מחזיר את הבקשה שפתחה את המסך הזה.

getBooleanExtra("isCorrect", false) קורא ממנה את הפתק בשם "isCorrect". הערך השני, false, הוא ברירת מחדל: מה לקבל אם אין פתק בשם הזה.

הריצו ובדקו

ענו נכון ובדקו שכתוב "צדקת!". חזרו עם Back, ענו לא נכון ובדקו שכתוב "טעית...".

תמיד כתוב "טעית...", גם כשעניתם נכון?

אל תמחקו קוד ואל תמשיכו הלאה. זו תקלה שכדאי להבין לבד:

  1. שום דבר לא אדום והאפליקציה לא קורסת. כלומר הקוד תקין מבחינת Java, אבל המידע לא מגיע.
  2. שאלו את עצמכם: מתי getBooleanExtra מחזיר את ברירת המחדל?
  3. השוו אות אחרי אות בין שני הקבצים.
רמז

שם הפתק ב-putExtra (ב-MainActivity) חייב להיות זהה בדיוק לשם ב-getBooleanExtra (ב-ResultActivity), כולל אותיות גדולות וקטנות. "isCorrect" ו-"iscorrect" הם שני פתקים שונים. בדקו גם שלא שכחתם את שורת ה-putExtra באחד הכפתורים.

8

כפתור החזרה: finish

ב-ResultActivity.java, בסוף onCreate, אחרי ה-if:

        Button btnBack = findViewById(R.id.btnBack);
        btnBack.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });
למה finish ולא Intent חדש

מסך השאלה לא נסגר כשעברתם למסך התוצאה. הוא מחכה מתחת, כמו קלף בערימה. finish() סוגר את המסך הנוכחי ומגלה את מה שמתחתיו. Intent חדש ל-MainActivity היה פותח עותק נוסף של מסך השאלה מעל הערימה, והערימה הייתה גדלה עם כל סיבוב.

בדיקת סיום של הפרויקט
  • מסך השאלה מציג שאלה שלכם ושני כפתורים.
  • תשובה נכונה פותחת מסך עם "צדקת!", תשובה שגויה פותחת מסך עם "טעית...".
  • "חזרה לשאלה" מחזיר למסך השאלה.
  • אחרי חזרה למסך השאלה, לחיצה אחת על Back של המכשיר יוצאת מהאפליקציה.
חלק 4

נקודת בדיקה

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

א. קריסה מכוונת

פתחו את AndroidManifest.xml, סמנו את שלוש השורות של ResultActivity וגזרו אותן (Ctrl+X). הריצו ולחצו על אחד הכפתורים. האפליקציה תקרוס.

פתחו את חלון Logcat בתחתית Android Studio וחפשו את השורות האדומות. מה שם השגיאה? איזו שאלה Android שואל אתכם בהודעה? הדביקו את השורות חזרה (Ctrl+V) וודאו שהכול עובד שוב.

ב. מה יקרה אם

נחשו קודם, ורק אחר כך נסו:

  1. ב-ResultActivity משנים את ברירת המחדל מ-false ל-true, וגם משנים את שם הפתק ל-"result". מה יוצג כשעונים לא נכון, ולמה?
  2. מחליפים את finish() ב-Intent חדש ל-MainActivity. עונים על השאלה שלוש פעמים, ובכל פעם חוזרים בכפתור. כמה לחיצות על Back של המכשיר צריך עכשיו כדי לצאת מהאפליקציה, ולמה?

אל תשכחו להחזיר את הקוד התקין.

ג. במילים שלכם

מה ההבדל בין Activity לבין קובץ layout? ומה תפקידו של כל אחד משני הפרמטרים ב-new Intent(MainActivity.this, ResultActivity.class)?

חלק 5

הרחבות

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

הרחבה 1: צבע לפי התוצאה10 נק'

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

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

רמז

הקופסה הראשית של המסך היא רכיב כמו כל רכיב אחר, ויש לה id. אפשר למצוא אותה עם findViewById(R.id.main) ולקרוא לפעולה setBackgroundColor. צבע מ-colors.xml מקבלים ב-Java עם getColor(R.color.result_correct).

הרחבה 2: הסבר לתשובה15 נק'

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

הצלחה: אם משנים את טקסט ההסבר ב-MainActivity בלבד, מסך התוצאה מציג את הטקסט החדש.

רמז

Intent יכול לשאת כמה פתקים, כל אחד עם שם אחר. לפתק מסוג טקסט קוראים עם getStringExtra, והוא מקבל רק את שם הפתק, בלי ברירת מחדל.

הרחבה 3: שאלה שנייה15 נק'

חידון של שאלה אחת הוא לא חידון. הוסיפו מסך שלישי עם שאלה נוספת.

הצלחה: אפשר לעבור שאלה, תוצאה, שאלה שנייה, תוצאה, ובמסך התוצאה האחרון אין כפתור "לשאלה הבאה".

רמז

מסך התוצאה צריך לדעת מאיזו שאלה הגיעו אליו. זה עוד פתק ב-Intent. כדי להעלים כפתור: setVisibility(View.GONE).

חלק 6

מסלול מתקדם

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

מתקדם 1: מחזור החיים של מסך+10

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

למורה: במעבר משאלה לתוצאה, מה קורה קודם, onPause של השאלה או onCreate של התוצאה? והאם onDestroy של השאלה נקרא?

תיעוד: The activity lifecycle

מתקדם 2: Intent שיוצא מהאפליקציה+10

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

למורה: מה ההבדל בין Intent מפורש למרומז, ומי מחליט איזו אפליקציה תיפתח?

תיעוד: Intents and intent filters

מתקדם 3: חמש שאלות, מסך שאלה אחד+10

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

למורה: כמה שורות צריך לשנות אצלכם כדי להוסיף שאלה שישית?

חלק 7

הניקוד שלי

סמנו מה שהמורה אישר. הסימון נשמר בדפדפן שלכם בלבד.

0 נקודות (100 בלי בונוס, 130 עם בונוס)
סיימתם

הראו למורה את האפליקציה רצה, ואז הגישו כרגיל: File ← Export ← Export to Zip File, והעלאה ל-Classroom.