בונים חידון על נושא שאתם בוחרים. מסך אחד שואל, מסך שני עונה "צדקת" או "טעית". בדרך לומדים איך אפליקציה עוברת ממסך למסך ואיך מעבירים מידע ביניהם.
נושא החידון והשאלות הם בחירה שלכם: כדורגל, מוזיקה, משחקי מחשב, היסטוריה, סדרות. המדריך כתוב עם שאלה לדוגמה, החליפו אותה בשאלה שלכם.
| חלק | מה עושים | נקודות | זמן משוער |
|---|---|---|---|
| חזרה | מסך השאלה, מהזיכרון | 10 | 20 דק' |
| הפרויקט | מסך תוצאה, מעבר בין המסכים והעברת התשובה | 40 | 45 דק' |
| נקודת בדיקה | מסבירים למורה שלושה דברים קצרים | 10 | 10 דק' |
| הרחבה 1 | צבע לפי התוצאה | 10 | בזמן שנשאר ובבית |
| הרחבה 2 | הסבר לתשובה | 15 | |
| הרחבה 3 | שאלה שנייה | 15 | |
| מסלול מתקדם | שלוש משימות | בונוס עד 30 |
ניקוד על הרחבות ועל המסלול המתקדם נרשם רק אחרי שעברתם את נקודת הבדיקה מול המורה. אפליקציה שעובדת בלי שאתם יודעים להסביר אותה לא נחשבת.
TrueFalseQuiz. השם באנגלית בלבד, בלי רווחים ובלי תווים מיוחדים. תבנית Empty Views Activity, שפה Java, Minimum SDK API 26, Build configuration language Groovy DSL.activity_main.xml במצב Code, מחקו את כל התוכן שלו והדביקו במקומו את שלד ה-LinearLayout שמופיע מתחת לרשימה.TextView עם id בשם tvQuestion: משפט על הנושא שלכם שאפשר לענות עליו "אמת" או "שקר", גודל טקסט 24sp, רוחב match_parent, והטקסט ממורכז.LinearLayout עם horizontal) ובתוכה שני כפתורים: btnTrue עם הטקסט "אמת" ו-btnFalse עם הטקסט "שקר". כל כפתור תופס חצי מרוחב השורה.colors.xml הוסיפו צבע בשם screen_bg והגדירו אותו כרקע המסך.<?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, עם המילה 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>ב-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.
ב-app > res > values > colors.xml, לפני </resources>:
<color name="screen_bg">#FFF8EE</color>
ובתגית הפותחת של ה-LinearLayout הראשי, כשורה נוספת לפני הסוגר >:
android:background="@color/screen_bg"
רואים שאלה באמצע המסך, מתחתיה שני כפתורים ברוחב שווה, וכל כפתור מציג את ההודעה שלו. קראו למורה לרישום 10 הנקודות.
כל האפליקציות שבניתם עד עכשיו היו מסך אחד. אפליקציות אמיתיות בנויות מהרבה מסכים: מסך כניסה, מסך ראשי, מסך הגדרות. היום האפליקציה שלכם מקבלת מסך שני.
Activity הוא מסך אחד באפליקציה. כל Activity בנוי משני קבצים שעובדים יחד: קובץ XML שקובע איך המסך נראה, וקובץ Java שקובע מה המסך עושה. את הזוג הראשון אתם כבר מכירים: activity_main.xml ו-MainActivity.java. דימוי: חדר בבית. לכל חדר יש רהיטים משלו (ה-XML) וחוקים משלו (ה-Java).
מסך לא יכול "לקפוץ" למסך אחר בעצמו. הוא כותב בקשה ומוסר אותה למערכת אנדרואיד, והמערכת פותחת את המסך המבוקש. לבקשה הזו קוראים Intent. בבקשה כתובים שני דברים: מי מבקש, ולאן רוצים להגיע.
new Intent(MainActivity.this, ResultActivity.class)מסך התוצאה צריך לדעת אם צדקתם. המידע הזה נמצא במסך השאלה, ולכן צריך לשלוח אותו. מצמידים ל-Intent "פתק" שנקרא extra. לכל פתק יש שם (מפתח) ותוכן (ערך). המסך השולח כותב את הפתק עם putExtra, והמסך המקבל קורא אותו לפי אותו שם בדיוק.
finish() מסיר את הקלף העליון, והמסך שמתחתיו חוזר.AndroidManifest.xml הוא "תעודת הזהות" של האפליקציה. רשומים בו כל המסכים שלה. מסך שלא רשום שם לא יכול להיפתח, והאפליקציה תקרוס. כשיוצרים מסך בדרך שתלמדו מיד, Android Studio רושם אותו בשבילכם.
אחרי כל תיבה כחולה של "הריצו ובדקו" מריצים ובודקים. לא ממשיכים הלאה עם שגיאה.
בעץ הקבצים משמאל, פתחו את app > java ולחצו לחיצה ימנית על שם החבילה שלכם (השורה הראשונה, זו שבתוכה MainActivity).
בחרו New > Activity > Empty Views Activity. בחלון שנפתח:
ResultActivityactivity_result (מתמלא לבד)Javaלחצו Finish.
Android Studio יצר שני קבצים חדשים: ResultActivity.java ו-activity_result.xml. זה הזוג של המסך השני. בנוסף הוא רשם את המסך ב-Manifest.
פתחו את app > manifests > AndroidManifest.xml. חפשו את השורות האלה:
<activity
android:name=".ResultActivity"
android:exported="false" />זה הרישום של המסך החדש. לא משנים כלום, רק מוודאים שהוא שם. סגרו את הקובץ.
פתחו את app > res > layout > activity_result.xml במצב Code. מחקו את כל התוכן והדביקו:
<?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 בלי בעיה, כי כל מסך מכיר רק את הקובץ שלו.
פתחו את MainActivity.java. מצאו את הקוד של btnTrue מהחזרה. בתוך onClick, מחקו את שורת ה-Toast וכתבו במקומה את שתי השורות של ה-Intent. כך צריך להיראות הקוד של הכפתור:
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 של המכשיר מחזיר לשאלה. הכפתור "חזרה לשאלה" עדיין לא עובד.
חלק ראשון: מסך השאלה צריך לדעת מהי התשובה הנכונה לשאלה שלכם. מיד אחרי השורה public class MainActivity extends AppCompatActivity { הוסיפו:
boolean correctAnswer = false;
אם התשובה הנכונה לשאלה שלכם היא "אמת", כתבו true במקום false.
חלק שני: עדכנו את onClick של btnTrue. נוספו שלושה דברים: המשתנה userAnswer, בדיקת if שקובעת את isCorrect, ושורת putExtra.
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.
עדכנו את onClick של btnFalse באותה צורה. ההבדל היחיד: מה הערך של userAnswer כשהמשתמש לוחץ "שקר"?
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);
}
});שני הכפתורים פותחים את מסך התוצאה. הטקסט עדיין "כאן תופיע התוצאה", כי המסך השני עוד לא קורא את הפתק.
פתחו את ResultActivity.java. בתוך onCreate, אחרי השורות שכבר כתובות שם ולפני הסוגר } שסוגר את 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, ענו לא נכון ובדקו שכתוב "טעית...".
אל תמחקו קוד ואל תמשיכו הלאה. זו תקלה שכדאי להבין לבד:
getBooleanExtra מחזיר את ברירת המחדל?שם הפתק ב-putExtra (ב-MainActivity) חייב להיות זהה בדיוק לשם ב-getBooleanExtra (ב-ResultActivity), כולל אותיות גדולות וקטנות. "isCorrect" ו-"iscorrect" הם שני פתקים שונים. בדקו גם שלא שכחתם את שורת ה-putExtra באחד הכפתורים.
ב-ResultActivity.java, בסוף onCreate, אחרי ה-if:
Button btnBack = findViewById(R.id.btnBack);
btnBack.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});מסך השאלה לא נסגר כשעברתם למסך התוצאה. הוא מחכה מתחת, כמו קלף בערימה. finish() סוגר את המסך הנוכחי ומגלה את מה שמתחתיו. Intent חדש ל-MainActivity היה פותח עותק נוסף של מסך השאלה מעל הערימה, והערימה הייתה גדלה עם כל סיבוב.
קראו למורה. תתבקשו להסביר בעל-פה את שלושת הדברים הבאים. הכינו את התשובות לפני כן, ונסו כל דבר באפליקציה שלכם.
פתחו את AndroidManifest.xml, סמנו את שלוש השורות של ResultActivity וגזרו אותן (Ctrl+X). הריצו ולחצו על אחד הכפתורים. האפליקציה תקרוס.
פתחו את חלון Logcat בתחתית Android Studio וחפשו את השורות האדומות. מה שם השגיאה? איזו שאלה Android שואל אתכם בהודעה? הדביקו את השורות חזרה (Ctrl+V) וודאו שהכול עובד שוב.
נחשו קודם, ורק אחר כך נסו:
ResultActivity משנים את ברירת המחדל מ-false ל-true, וגם משנים את שם הפתק ל-"result". מה יוצג כשעונים לא נכון, ולמה?finish() ב-Intent חדש ל-MainActivity. עונים על השאלה שלוש פעמים, ובכל פעם חוזרים בכפתור. כמה לחיצות על Back של המכשיר צריך עכשיו כדי לצאת מהאפליקציה, ולמה?אל תשכחו להחזיר את הקוד התקין.
מה ההבדל בין Activity לבין קובץ layout? ומה תפקידו של כל אחד משני הפרמטרים ב-new Intent(MainActivity.this, ResultActivity.class)?
כאן אין צעד-צעד. יש דרישה, הגדרת הצלחה ורמז. כל מה שצריך כבר מופיע במדריך הזה או במדריכים הקודמים.
מסך התוצאה נראה אותו דבר כשצודקים וכשטועים. שנו את זה:
colors.xml שני צבעים חדשים: result_correct (ירקרק) ו-result_wrong (אדמדם).הצלחה: תשובה נכונה פותחת מסך ירקרק, תשובה שגויה פותחת מסך אדמדם.
הקופסה הראשית של המסך היא רכיב כמו כל רכיב אחר, ויש לה id. אפשר למצוא אותה עם findViewById(R.id.main) ולקרוא לפעולה setBackgroundColor. צבע מ-colors.xml מקבלים ב-Java עם getColor(R.color.result_correct).
חידון טוב גם מלמד. הוסיפו למסך התוצאה משפט שמסביר מהי התשובה הנכונה ולמה.
MainActivity, ליד השאלה, ונשלח למסך התוצאה כפתק שני ב-Intent.TextView חדש בשם tvExplanation, בין התוצאה לכפתור, והוא מציג את ההסבר שהתקבל.הצלחה: אם משנים את טקסט ההסבר ב-MainActivity בלבד, מסך התוצאה מציג את הטקסט החדש.
Intent יכול לשאת כמה פתקים, כל אחד עם שם אחר. לפתק מסוג טקסט קוראים עם getStringExtra, והוא מקבל רק את שם הפתק, בלי ברירת מחדל.
חידון של שאלה אחת הוא לא חידון. הוסיפו מסך שלישי עם שאלה נוספת.
SecondQuestionActivity, עם שאלה אחרת ושני כפתורים, שנראה כמו מסך השאלה הראשון.הצלחה: אפשר לעבור שאלה, תוצאה, שאלה שנייה, תוצאה, ובמסך התוצאה האחרון אין כפתור "לשאלה הבאה".
מסך התוצאה צריך לדעת מאיזו שאלה הגיעו אליו. זה עוד פתק ב-Intent. כדי להעלים כפתור: setVisibility(View.GONE).
פתוח לכל מי שסיים את הפרויקט ואת נקודת הבדיקה. כאן אין צעדים ואין רמזים. יש יעד וקישור לתיעוד הרשמי, ואתם לומדים לבד. על כל משימה תצטרכו להסביר למורה מה עשיתם ולמה זה עובד.
מסך לא רק "קיים" או "לא קיים". הוא עובר בין מצבים, ובכל מעבר אנדרואיד קורא לפעולה מיוחדת.
onStart, onResume, onPause, onStop ו-onDestroy, ובכל אחת (וגם ב-onCreate) הדפיסו שורה ל-Logcat עם Log.d, כולל שם המסך ושם הפעולה.finish(), ולחיצה על כפתור הבית של המכשיר.למורה: במעבר משאלה לתוצאה, מה קורה קודם, onPause של השאלה או onCreate של התוצאה? והאם onDestroy של השאלה נקרא?
ה-Intent שכתבתם נקרא מפורש: הוא נוקב בשם המסך. יש גם Intent מרומז, שאומר רק מה רוצים לעשות, ואנדרואיד מחפש אפליקציה שיודעת לעשות את זה.
ACTION_SEND.ACTION_VIEW.למורה: מה ההבדל בין Intent מפורש למרומז, ומי מחליט איזו אפליקציה תיפתח?
מסך נפרד לכל שאלה (כמו בהרחבה 3) לא מחזיק מעמד בחידון של עשרים שאלות. בנו את החידון נכון:
למורה: כמה שורות צריך לשנות אצלכם כדי להוסיף שאלה שישית?
סמנו מה שהמורה אישר. הסימון נשמר בדפדפן שלכם בלבד.
הראו למורה את האפליקציה רצה, ואז הגישו כרגיל: File ← Export ← Export to Zip File, והעלאה ל-Classroom.