أساسيات أندرويد بجافا

نشاطك الأول وتخطيط الشاشة

18 دقيقة الدرس 5 من 12

نشاطك الأول وتخطيط الشاشة

شاشة أندرويد هي توليف بين شيئين: النشاط (Activity) — فئة Java تملك منطق الشاشة ودورة حياتها — وملف XML للتخطيط الذي يصف شكل الشاشة. لا يعمل أيٌّ منهما دون الآخر. يأخذك هذا الدرس عبر بناء شاشة حقيقية وعملية من الصفر حتى يصبح هذا النمط طبيعةً ثانيةً لك.

الملفان اللذان يصنعان الشاشة

حين يُنشئ Android Studio مشروعًا جديدًا يخلق هذا الثنائي تلقائيًا:

  • app/src/main/java/…/MainActivity.java — فئة Java التي تستقبل استدعاءات دورة الحياة وتتحكم في السلوك.
  • app/src/main/res/layout/activity_main.xml — مورد XML يُعلن عن كل عنصر واجهة وكيفية ترتيبه على الشاشة.

ينشر النشاط (Activity) التخطيطَ في وقت التشغيل: يقرأ XML ويُنشئ كائنات View المقابلة ويربطها بالنافذة. الاستدعاء الذي يُطلق هذا هو setContentView(R.layout.activity_main) داخل onCreate().

ما معنى النشر (Inflation)؟ يُحلّل منشئ تخطيط أندرويد شجرة XML ويبني شجرة مقابلة من كائنات Java من النوع View في الذاكرة. بعد أن يُرجع setContentView تصبح كل عنصر واجهة أعلنت عنه في XML كائنًا Java حيًا يمكنك تحديده والتعامل معه.

نشاط بسيط إلى أقصى حد

هذا هو أصغر Activity يُترجَم ويعرض شاشة:

package com.example.myfirstapp; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // أخبر Android بملف XML المراد نشره لهذه الشاشة setContentView(R.layout.activity_main); } }

أمران إلزاميان هنا: استدعاء super.onCreate(savedInstanceState) — تطلب الإطار ذلك لإعداد حالته الداخلية — واستدعاء setContentView قبل محاولة الوصول إلى أي عنصر واجهة. استدعاء findViewById قبل setContentView يُرجع null ويُسبّب NullPointerException فور استخدام النتيجة.

بناء ملف XML للتخطيط

افتح res/layout/activity_main.xml. ملف التخطيط له جذر واحد فقط هو مجموعة عروض (view group). المجموعة الأكثر مرونة للمبتدئين هي ConstraintLayout، لكن LinearLayout أسهل في الفهم مبدئيًا. إليك شاشة عملية بعنوان وحقل نص وزر مرتبة عموديًا:

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="24dp"> <TextView android:id="@+id/tvGreeting" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/greeting" android:textSize="24sp" android:layout_marginBottom="16dp" /> <EditText android:id="@+id/etName" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/hint_name" android:inputType="textPersonName" android:layout_marginBottom="16dp" /> <Button android:id="@+id/btnSayHello" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/btn_hello" /> </LinearLayout>

عدة مفاهيم مهمة هنا:

  • android:id="@+id/tvGreeting" — البادئة @+id/ تأمر أداة البناء بإنشاء ثابت صحيح جديد في فئة R.id المُنشأة تلقائيًا. تستخدم هذا الثابت في Java لتحديد العنصر: R.id.tvGreeting.
  • match_parent — ملء المساحة المتاحة كلها من مجموعة العروض الأم.
  • wrap_content — الانكماش حتى يكفي لاحتواء المحتوى فقط.
  • @string/greeting — مرجع إلى مورد نصي مُعرَّف في res/values/strings.xml. كتابة النص مباشرةً في XML تعمل، لكن استخدام موارد النصوص إلزامي لدعم التعريب وتفرضه تحذيرات lint.
  • dp وsp — بكسلات مستقلة عن كثافة الشاشة للهوامش والحشو؛ وبكسلات مقياسية لأحجام الخطوط. استخدم هذه الوحدات دائمًا، لا البكسلات الخام (px).

ربط Java بالتخطيط: findViewById

بعد نشر التخطيط، تحدد العناصر بمعرّفاتها وتخزّنها كحقول لتتمكن من الاستجابة للأحداث:

package com.example.myfirstapp; 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 tvGreeting; private EditText etName; private Button btnSayHello; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // النشر أولًا // تحديد كائنات View الحية بثوابت id الخاصة بـ XML tvGreeting = findViewById(R.id.tvGreeting); etName = findViewById(R.id.etName); btnSayHello = findViewById(R.id.btnSayHello); // ربط مستمع نقر بالزر btnSayHello.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String name = etName.getText().toString().trim(); if (name.isEmpty()) { tvGreeting.setText("Hello, stranger!"); } else { tvGreeting.setText("Hello, " + name + "!"); } } }); } }
ViewBinding (البديل الموصى به لـ findViewById): تُفعّل المشاريع الحديثة ViewBinding في build.gradle (viewBinding { enabled = true }). تُنشئ أداة البناء فئة ربط مكتوبة بأنواع محددة لكل تخطيط، مما يُلغي جميع استدعاءات findViewById ويجعل NullPointerException الناتجة عن معرف مفقود خطأ في وقت التحويل لا في وقت التشغيل. في هذا الدرس التمهيدي يُوضّح findViewById آلية النشر بشكل أجلى، لكن خطّط للتحول إلى ViewBinding فور شعورك بالارتياح.

موارد النصوص

أضف النصوص المُشار إليها في XML إلى res/values/strings.xml:

<resources> <string name="app_name">MyFirstApp</string> <string name="greeting">What is your name?</string> <string name="hint_name">Enter your name</string> <string name="btn_hello">Say Hello</string> </resources>

يُشير التخطيط إليها بـ @string/greeting وما شابه. يُحلّل أندرويد المرجع في وقت التشغيل مختارًا مجلد القيم الخاص بالوحدة المحلية الصحيحة إن وُجدت ترجمات. هذا أساس نظام مؤهلات الموارد في أندرويد — الآلية ذاتها التي تتعامل لاحقًا مع كثافة الشاشة ووضع الليل وتخطيطات RTL.

فئة R — الجسر بين XML وJava

في كل مرة تبني المشروع، تفحص أدوات بناء أندرويد جميع الملفات ضمن res/ وتُنشئ تلقائيًا فئة اسمها R. تحتوي على فئات صحيحة ثابتة متداخلة: R.id وR.layout وR.string وR.drawable وغيرها. كل عدد صحيح هو مقبض فريد يستخدمه وقت التشغيل لتحديد المورد المقابل. لا تكتب R.java ولا تعدّله أبدًا — أنت فقط تقرأ منه.

لا تستورد فئة R الخاطئة. يستورد Android Studio أحيانًا android.R (فئة موارد النظام) بدلًا من R الخاصة بتطبيقك com.example.myfirstapp.R. إن أصبحت معرفاتك فجأة غير محلولة، تحقق من تعليمات الاستيراد أعلى الملف — يجب أن تستورد R الخاصة بحزمتك لا R الخاصة بأندرويد.

تشغيل التطبيق

اضغط زر Run الأخضر (أو اضغط Shift+F10). يُترجم Android Studio المشروع ويحزمه في APK ويُثبّته على المحاكي المختار أو الجهاز المتصل. ستظهر شاشتك، اكتب اسمًا، اضغط الزر، وشاهد التحية تتغير — كلها مدفوعة بـ onClick الذي كتبته للتو.

الخلاصة

كل شاشة أندرويد مبنية من نمط الملفين ذاته: فئة Java تمتد من AppCompatActivity تستدعي setContentView(R.layout.activity_main) داخل onCreate()، وملف XML للتخطيط يُعلن شجرة العناصر. بعد النشر، يُجسّر findViewById بين الاثنين بإرجاع كائن View الحي لأي معرف حدّدته في XML. تفصل موارد النصوص النص عن التخطيطات وتُتيح التعريب منذ اليوم الأول. في الدرس التالي ستدرس AndroidManifest.xml وتفهم كيف يعرف أندرويد أي Activity يُطلق عند بدء تشغيل التطبيق.