واجهة أندرويد والأنشطة والتنقّل

العناصر المرئية والأدوات الشائعة

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

العناصر المرئية والأدوات الشائعة

كل عنصر مرئي على شاشة Android هو فئة فرعية من android.view.View. يأتي Android مزوّدًا بعشرات الفئات الفرعية الجاهزة — تُسمّى الأدوات (Widgets) — تغطّي الغالبية العظمى من احتياجات واجهة المستخدم. في هذا الدرس ستعمل عن كثب مع أربعة منها وهي الأساسية: TextView وButton وEditText وImageView. ستتعلّم كيف يُعلن عن كلٍّ منها في XML، وكيف يُربط في Java، والسمات الرئيسية واستدعاءات الواجهة البرمجية التي يلجأ إليها كل مطوّر Android يوميًا.

التعريف بـ XML مقابل الإنشاء في Java

يحثّ Android بشدة على تعريف واجهة المستخدم في ملفات XML للتخطيط (داخل res/layout/)، ثم الإشارة إلى تلك العناصر في Java عبر findViewById(). هذا الفصل يُبقي اهتمامات التصميم بعيدة عن المنطق التجاري ويُتيح لأدوات تحرير التخطيط وأدوات إمكانية الوصول فحص واجهة المستخدم بمعزل عن الكود التشغيلي.

يجب أن يوفّر كل عنصر XML بالضرورة سمتَي android:layout_width وandroid:layout_height. القيمتان الأكثر شيوعًا هما wrap_content (الحجم وفق المحتوى) وmatch_parent (ملء المساحة المتاحة).

TextView — عرض النصوص

TextView هو الأداة الأكثر استخدامًا في Android. يعرض سلسلة نصية ويدعم التنسيق الغني والروابط القابلة للنقر والرسومات المصاحبة والامتدادات المُعلَّمة بـ HTML.

<!-- res/layout/activity_main.xml --> <TextView android:id="@+id/tvGreeting" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello, World!" android:textSize="20sp" android:textColor="#212121" android:fontFamily="sans-serif-medium" android:layout_margin="16dp" />

السمات الرئيسية: android:text يضبط التسمية الثابتة (يُفضَّل استخدام موارد السلاسل: @string/greeting). يجب أن تستخدم android:textSize وحدات sp حتى تحترم تفضيل المستخدم لحجم الخط في النظام. يقبل android:textColor ألوانًا بالتنسيق الست عشري أو موارد الألوان.

في Java تحصل على مرجع وتتعامل معه في وقت التشغيل:

// MainActivity.java import android.os.Bundle; import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; public class MainActivity extends AppCompatActivity { private TextView tvGreeting; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tvGreeting = findViewById(R.id.tvGreeting); tvGreeting.setText("Welcome back, Edrees!"); // اجعل النص قابلًا للتحديد (مفيد للبريد الإلكتروني وأرقام الهاتف) tvGreeting.setTextIsSelectable(true); } }
استخدم دائمًا sp لأحجام النص وdp لكل شيء آخر. يتناسب sp (البكسل المستقل عن الحجم) مع إعدادات إمكانية الوصول للخط الخاصة بالمستخدم. تثبيت حجم النص بـ px أو dp يجعل تطبيقك غير قابل للوصول للمستخدمين الذين يحتاجون نصًا أكبر.

Button — التعامل مع النقرات

يمتدّ Button من TextView، لذا تنطبق عليه كل سمات النص. مهمته الأساسية هي تشغيل إجراء عندما ينقر المستخدم عليه. الطريقة المُفضَّلة لتلقّي النقرات هي إرفاق OnClickListener في الكود — لا تستخدم السمة المهملة android:onClick في المشاريع الجديدة.

<!-- res/layout/activity_main.xml --> <Button android:id="@+id/btnSave" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Save" android:backgroundTint="@color/colorPrimary" android:textColor="@android:color/white" android:paddingHorizontal="24dp" />
// MainActivity.java import android.view.View; import android.widget.Button; import android.widget.Toast; Button btnSave = findViewById(R.id.btnSave); btnSave.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MainActivity.this, "Saved!", Toast.LENGTH_SHORT).show(); } });

الصيغة اللامبدية أكثر إيجازًا بمجرد أن تتقن الفئات المجهولة:

btnSave.setOnClickListener(v -> Toast.makeText(this, "Saved!", Toast.LENGTH_SHORT).show() );
عطّل الزر أثناء تشغيل العمليات غير المتزامنة. استدع btnSave.setEnabled(false) قبل بدء عملية شبكة أو قاعدة بيانات، ثم btnSave.setEnabled(true) عند اكتمالها. يمنع هذا عمليات الإرسال المكررة — إحدى أكثر الأخطاء شيوعًا في نماذج Android.

EditText — قبول مدخلات المستخدم

EditText هو TextView مع تفعيل التحرير. إنه الطريقة الأساسية لكتابة المستخدمين في تطبيقك. تُعدّ سمة android:inputType بالغة الأهمية: فهي تُعلم لوحة المفاتيح الناعمة بنوع المدخلات المطلوب وتُفعّل تلميحات التحقق التلقائي.

<!-- res/layout/activity_main.xml --> <EditText android:id="@+id/etEmail" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Enter your email" android:inputType="textEmailAddress" android:maxLines="1" android:imeOptions="actionDone" android:layout_margin="16dp" />

قيم inputType الشائعة: text وtextPassword وtextEmailAddress وphone وnumber وnumberDecimal وtextMultiLine. تُغيّر كل قيمة تخطيط لوحة المفاتيح المعروضة للمستخدم.

// MainActivity.java import android.widget.EditText; EditText etEmail = findViewById(R.id.etEmail); // اقرأ القيمة الحالية — اقطع المسافات البيضاء دائمًا String email = etEmail.getText().toString().trim(); if (email.isEmpty()) { etEmail.setError("Email is required"); etEmail.requestFocus(); return; }

استخدم setError() لعرض رسالة تحقق مباشرة تحت الحقل — يرسم Android أيقونة حمراء ومؤشر نصي تلقائيًا. اقرنها مع requestFocus() لتمرير المستخدم نحو حقل المشكلة.

لا تقرأ محتوى EditText داخل onResume() أو مؤقّت. اقرأ المحتوى فقط عندما يُشغّل المستخدم إجراءً (نقر زر، أو إجراء IME). القراءة المتواصلة تُبطل الغرض من المدخلات التي يبادر بها المستخدم وتُهدر طاقة المعالج.

ImageView — عرض الصور

يعرض ImageView موارد drawable أو صورة bitmap محمّلة في وقت التشغيل أو صورة بعيدة (عبر مكتبة كـ Glide أو Picasso). أهم سمتين هما android:src (مصدر الصورة) وandroid:scaleType (كيفية تحجيم الصورة أو قصّها داخل حدود العرض).

<!-- res/layout/activity_main.xml --> <ImageView android:id="@+id/ivAvatar" android:layout_width="80dp" android:layout_height="80dp" android:src="@drawable/ic_avatar_placeholder" android:scaleType="centerCrop" android:contentDescription="User avatar" android:layout_margin="16dp" />

قيم scaleType الشائعة:

  • centerCrop — تحجيم متناسق حتى يملأ كلا البُعدين العرض؛ قصّ الزيادة. الأفضل لصور الملفات الشخصية.
  • fitCenter — تحجيم متناسق حتى تتناسب الصورة كاملةً داخل العرض مع وجود هوامش. جيد للشعارات والأيقونات.
  • centerInside — مثل fitCenter لكنه لا يُكبّر الصورة الأصغر من العرض أبدًا.
  • fitXY — تمديد لملء الحجم تمامًا متجاهلًا نسبة العرض إلى الارتفاع. يبدو خاطئًا في معظم الحالات.

تغيير الصورة في وقت التشغيل:

// MainActivity.java import android.widget.ImageView; ImageView ivAvatar = findViewById(R.id.ivAvatar); // من مورد drawable ivAvatar.setImageResource(R.drawable.ic_user_photo); // من كائن Bitmap (مُفكَّك من ملف أو نتيجة كاميرا) // Bitmap bmp = BitmapFactory.decodeFile(path); // ivAvatar.setImageBitmap(bmp);
اضبط دائمًا android:contentDescription على كل ImageView. تُعلن قارئات الشاشة (TalkBack) هذه السلسلة للمستخدمين ضعاف البصر. إذا كانت الصورة زخرفية بحتة، اضبطها على سلسلة فارغة ("") لكي يتخطّاها TalkBack. يولّد إغفالها تحذير lint وتفشل في تدقيقات إمكانية الوصول.

ربط كل شيء معًا — مثال واقعي

إليك نموذج تسجيل مضغوط يوضّح عمل الأدوات الأربع معًا:

// RegistrationActivity.java public class RegistrationActivity extends AppCompatActivity { private EditText etName, etEmail; private Button btnRegister; private ImageView ivLogo; private TextView tvStatus; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_registration); etName = findViewById(R.id.etName); etEmail = findViewById(R.id.etEmail); btnRegister = findViewById(R.id.btnRegister); ivLogo = findViewById(R.id.ivLogo); tvStatus = findViewById(R.id.tvStatus); ivLogo.setImageResource(R.drawable.ic_app_logo); btnRegister.setOnClickListener(v -> { String name = etName.getText().toString().trim(); String email = etEmail.getText().toString().trim(); if (name.isEmpty()) { etName.setError("Name is required"); etName.requestFocus(); return; } if (email.isEmpty() || !android.util.Patterns.EMAIL_ADDRESS.matcher(email).matches()) { etEmail.setError("Valid email is required"); etEmail.requestFocus(); return; } btnRegister.setEnabled(false); tvStatus.setText("Registering " + name + "..."); // ... تنفيذ التسجيل ثم إعادة تفعيل الزر }); } }

الخلاصة

يعرض TextView النصوص؛ تحكّم في حجمه بـ sp وفي أسلوبه بسمات النص المتعددة. يُشغّل Button OnClickListener — أرفقه في Java لا في XML. يجمع EditText المدخلات المكتوبة؛ حدّد inputType دائمًا وتحقّق من الصحة بـ setError(). يعرض ImageView الصور؛ اختر scaleType المناسب وزوّد دائمًا contentDescription. في الدرس القادم ستتناول أحداث تفاعل المستخدم بعمق أكبر.