בשלב הקודם הפעלנו את View Binding והמרנו מסך פשוט. כאן נמיר את MainActivity של משחק איקס-עיגול. במסך הזה יש גם עדכון רגיל של EditText, וגם קוד שמאתר כפתור לפי שורה ועמודה. החלק השני דורש שינוי מחשבתי קטן וחשוב.
המטרה: לא יישארו ב-MainActivity קריאות findViewById.
לפני שמתחילים
ודאו ששלב 019a כבר בוצע:
buildFeatures {
viewBinding = true
}
אחרי Sync Project with Gradle Files, הקובץ activity_main.xml יוצר את המחלקה ActivityMainBinding.
שלב 1 - יצירת ה-binding
פתחו את:
app/src/main/java/com/example/tictacmenu/activities/MainActivity.java
הוסיפו את ה-import ואת השדה:
-import com.example.tictacmenu.R;
+import com.example.tictacmenu.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
+ private ActivityMainBinding binding;
בתחילת onCreate, החליפו את טעינת ה-layout:
-setContentView(R.layout.activity_main);
+binding = ActivityMainBinding.inflate(getLayoutInflater());
+setContentView(binding.getRoot());
binding.getRoot() הוא ה-View הראשי של activity_main.xml; לכן הוא הערך שמעבירים ל-setContentView.
שלב 2 - החלפה פשוטה של View יחיד
במתודה onConnectClick, אין צורך לחפש את שדה כתובת השרת לפי מזהה:
-EditText ipEdit = findViewById(R.id.editServerIP);
+EditText ipEdit = binding.editServerIP;
אפשר גם להשתמש ישירות ב-binding.editServerIP, אבל משתנה מקומי בשם ipEdit נשאר קריא ונוח אם משתמשים בו כמה פעמים.
העיקרון החשוב: R.id הוא מספר, binding הוא אובייקט
לפני ההמרה, R.id.button00 הוא מזהה משאב מסוג int. המזהה מתאים לפעולות כמו:
Button button = findViewById(R.id.button00);
לעומת זאת, binding.button00 הוא כבר האובייקט עצמו, מסוג Button:
Button button = binding.button00;
button.setText("X");
לכן אין להחליף את R.id.button00 ב-binding.button00 בתוך מקום שמצפה ל-int. למשל, המתודה הבאה לא יכולה להמשיך להחזיר int:
private int idFor(int row, int col) {
return binding.button00; // שגיאת קומפילציה: Button אינו int
}
זה בדיוק מקור הסימון האדום: binding לא מחזיר את ה-ID של הכפתור, אלא מחזיר את הכפתור. זו בדרך כלל תועלת — אין חיפוש נוסף, אין casting, והטיפוס נבדק בזמן קומפילציה — אך בקוד שבנוי סביב IDs צריך להתאים את סוגי המשתנים והמתודות.
כלל אצבע: אם הקוד צריך לבצע פעולה על View, השתמשו ב-binding.someView. אם API חיצוני דורש במפורש מזהה משאב, כגון int @IdRes, העבירו לו R.id.some_view ולא את שדה ה-binding.
שלב 3 - כפתור לפי שורה ועמודה
ב-MainActivity, הודעה שמגיעה מהשרת כוללת שורה ועמודה. בקוד הישן המתודה idFor החזירה מזהה int, ואחר כך הקוד חיפש באמצעותו את הכפתור. לאחר מעבר ל-binding, המתודה צריכה להחזיר Button ישירות; אין צורך, ואסור, לקרוא שוב ל-findViewById.
בצעו את שינויי החלק הזה לפי תמונת הדיפ:

שימו לב לשלוש ההחלפות המרכזיות בתמונה:
idForהופכת ל-buttonForוסוג ההחזרה משתנה מ-intל-Button.- במקום
int idואזfindViewById(id), מקבלים מידButton target = buttonFor(r, c). - ב-
resetBoardהמערך הואButton[], ולכן הלולאה מקבלתButtonישירות ולא מזהה.
הערך null בסוף buttonFor מציין ששילוב שורה/עמודה אינו תא חוקי. זו החלופה המתאימה ל-return 0 של מתודה שהחזירה int.
בדיקה
- הריצו
Build > Make Project. - הריצו את האפליקציה ופתחו את מסך המשחק.
- נסו להתחבר לשרת ולבצע מהלך מקומי.
- אם מתקבל מהלך מהשרת, ודאו שהכפתור המתאים מתעדכן.
- ודאו שניצחון או תיקו עדיין מאפסים את כל תשעת הכפתורים.
אם ActivityMainBinding מסומן באדום, בצעו Gradle Sync ובדקו שקיים app/src/main/res/layout/activity_main.xml.