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

مشروع: تطبيق أندرويد بشاشتين

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

مشروع: تطبيق أندرويد بشاشتين

كل ما تعلّمته في هذا البرنامج التعليمي — الأنشطة (Activities) والتخطيطات (layouts) ودورة الحياة والملف التعريفي (Manifest) والموارد والـ Intents والتسجيل — يتلاقى في هذا الدرس الأخير. ستبني تطبيق أندرويد كاملاً يعمل بالكامل ويضمّ شاشتين: شاشة رئيسية يكتب فيها المستخدم اسمه، وشاشة ترحيب تعرض رسالة ترحيب شخصية. تعتمد التنقل بينهما على Intent صريح يحمل Extra، تمامًا كما يكتبها مطوّر أندرويد محترف.

ما ستتدرّب عليه: إنشاء فئة Activity ثانية وتخطيطها، والإعلان عنها في الملف التعريفي، وتشغيلها بـ startActivity()، وتمرير البيانات عبر extras في Intent، وقراءة تلك البيانات في الـ Activity المستقبِلة، وتوصيل زر الرجوع — كل هذا بلغة Java.

نظرة عامة على هيكل التطبيق

يتألف التطبيق من شاشتين فقط:

  • MainActivity — حقل نصي (EditText) وزر "Say Hello" (Button).
  • GreetingActivityTextView يعرض "Hello, الاسم!" وزر "Go Back".

تتدفق البيانات في اتجاه واحد: تقرأ MainActivity الاسم من EditText، وتعبّئه في extra داخل Intent، ثم تشغّل GreetingActivity. تفكّ شاشة الترحيب العبوة وتعرضها. لا قاعدة بيانات ولا شبكة — تركيز تام على التنقل.

الخطوة الأولى — تخطيط الشاشة الرئيسية (activity_main.xml)

افتح res/layout/activity_main.xml واستبدل محتواه بـ 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:gravity="center" android:padding="32dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/prompt_name" android:textSize="18sp" 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="24dp" /> <Button android:id="@+id/btnSayHello" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/btn_say_hello" /> </LinearLayout>

الخطوة الثانية — تخطيط شاشة الترحيب (activity_greeting.xml)

أنشئ ملف تخطيط جديد: انقر بالزر الأيمن على res/layout ← New ← Layout Resource File ← سمّه activity_greeting. استخدم المحتوى التالي:

<?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:gravity="center" android:padding="32dp"> <TextView android:id="@+id/tvGreeting" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="24sp" android:textStyle="bold" android:layout_marginBottom="32dp" /> <Button android:id="@+id/btnGoBack" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/btn_go_back" /> </LinearLayout>

الخطوة الثالثة — موارد النصوص (res/values/strings.xml)

يجب أن يعيش كل نص مرئي للمستخدم في strings.xml — لا تُضمّن النصوص مباشرةً في التخطيطات أو كود Java:

<resources> <string name="app_name">TwoScreenApp</string> <string name="prompt_name">Enter your name:</string> <string name="hint_name">Your name</string> <string name="btn_say_hello">Say Hello</string> <string name="btn_go_back">Go Back</string> <string name="greeting_format">Hello, %1$s!</string> </resources>

يُستخدم المتغيّر %1$s مع getString(R.string.greeting_format, name) في Java لإنتاج رسالة الترحيب المنسّقة.

الخطوة الرابعة — MainActivity.java

هذا الـ Activity يقرأ الاسم ويُطلق الـ Intent:

package com.example.twoscreenapp; import android.content.Intent; import android.os.Bundle; import android.text.TextUtils; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; public class MainActivity extends AppCompatActivity { // ثابت مفتاح الـ extra — عرّفه هنا حتى يتفق الـ Activity-ان // على نفس النص دون تكرار public static final String EXTRA_NAME = "com.example.twoscreenapp.EXTRA_NAME"; private EditText etName; private Button btnSayHello; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); etName = findViewById(R.id.etName); btnSayHello = findViewById(R.id.btnSayHello); btnSayHello.setOnClickListener(v -> launchGreeting()); } private void launchGreeting() { String name = etName.getText().toString().trim(); if (TextUtils.isEmpty(name)) { Toast.makeText(this, "Please enter your name", Toast.LENGTH_SHORT).show(); return; } // بناء Intent صريح يستهدف GreetingActivity Intent intent = new Intent(this, GreetingActivity.class); intent.putExtra(EXTRA_NAME, name); // تعليق الاسم كـ extra startActivity(intent); // تشغيل الشاشة الثانية } }
عرّف مفاتيح الـ extras كـ public static final String في الـ Activity المُرسِل. يرجع إليها الـ Activity المستقبِل كـ MainActivity.EXTRA_NAME بدلًا من كتابة نص خام — فخطأ إملائي في نص خام يُعيد null بصمت، بينما خطأ التحويل البرمجي في الثابت يُكتشف فورًا.

الخطوة الخامسة — GreetingActivity.java

أنشئ الـ Activity الثانية: انقر بالزر الأيمن على الحزمة ← New ← Java Class ← سمّها GreetingActivity. امدد AppCompatActivity:

package com.example.twoscreenapp; import android.os.Bundle; import android.widget.Button; import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; public class GreetingActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_greeting); // استرجاع الاسم الذي وضعته MainActivity في الـ Intent String name = getIntent().getStringExtra(MainActivity.EXTRA_NAME); // تنسيق رسالة الترحيب وعرضها TextView tvGreeting = findViewById(R.id.tvGreeting); tvGreeting.setText(getString(R.string.greeting_format, name)); // "Go Back" ينهي هذا الـ Activity ويكشف MainActivity أسفله Button btnGoBack = findViewById(R.id.btnGoBack); btnGoBack.setOnClickListener(v -> finish()); } }

استدعاء finish() يُدمّر GreetingActivity ويسحبه من مكدّس الرجوع، مُعيدًا المستخدم إلى MainActivity — تمامًا كما يفعل زر الرجوع في النظام. أنت لا تبدأ نسخة جديدة من MainActivity؛ فذلك سيُنشئ نسخة مكرّرة ويُفسد مكدّس الرجوع.

لا تستدعِ startActivity(new Intent(this, MainActivity.class)) أبدًا من زر الرجوع. هذا يدفع نسخةً جديدة من MainActivity فوق المكدّس بدلًا من العودة إلى الموجودة. استخدم finish() دائمًا للرجوع مستوى واحدًا للخلف.

الخطوة السادسة — تسجيل GreetingActivity في الملف التعريفي

يجب الإعلان عن كل Activity في AndroidManifest.xml. يضيفها معالج Android Studio تلقائيًا عند استخدام New ← Activity، لكن إن أنشأت الفئة يدويًا يجب إضافتها بنفسك:

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.twoscreenapp"> <application android:allowBackup="true" android:label="@string/app_name" android:theme="@style/Theme.TwoScreenApp"> <!-- الـ Activity نقطة الدخول --> <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <!-- الشاشة الثانية — لا intent-filter مطلوب؛ تُشغَّل بشكل صريح --> <activity android:name=".GreetingActivity" android:exported="false" android:parentActivityName=".MainActivity" /> </application> </manifest>

يُخبر السمة android:parentActivityName النظامَ وزرَ الصعود (←) في شريط الإجراءات أن MainActivity هي الأب المنطقي. هذه ممارسة جيدة حتى وإن كان زر "Go Back" الصريح يتعامل مع التنقل بالفعل.

الخطوة السابعة — التشغيل والتحقق

شغّل التطبيق على المحاكي أو جهاز حقيقي. السلوك المتوقع:

  1. تظهر الشاشة الرئيسية مع EditText وزر "Say Hello".
  2. ترك الحقل فارغًا والنقر على الزر يُظهر Toast — ولا ينتقل التطبيق.
  3. كتابة اسم والنقر على "Say Hello" ينتقل إلى شاشة الترحيب التي تعرض "Hello, الاسم!".
  4. النقر على "Go Back" أو زر الرجوع في النظام يُعيد إلى الشاشة الرئيسية.
  5. يحتفظ حقل الاسم بقيمته لأن MainActivity لم تُدمَّر قط — كانت مُوقفة مؤقتًا فحسب.

مراجعة دورة الحياة

عند التنقل للأمام: MainActivity.onPause()MainActivity.onStop()GreetingActivity.onCreate()GreetingActivity.onStart()GreetingActivity.onResume(). عند العودة: GreetingActivity.onPause()GreetingActivity.onStop()GreetingActivity.onDestroy()MainActivity.onRestart()MainActivity.onStart()MainActivity.onResume(). لا تُدمَّر MainActivity كليًا أبدًا؛ بل تستأنف عملها مع حالة العرض سليمة.

الخلاصة

لقد بنيت تطبيق أندرويد كاملاً بشاشتين في Java. الأنماط الرئيسية التي تحملها معك: أعلن عن كل Activity في الملف التعريفي، واستخدم Intents صريحة بثوابت مفاتيح مكتوبة لتمرير البيانات، وادعُ finish() وليس Intent جديدًا للرجوع، واحفظ كل النصوص المرئية في strings.xml. هذه الأساسيات تتوسّع مباشرةً لتطبيقات بعشر شاشات أو عشرين — الآليات متطابقة بصرف النظر عن التعقيد.