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

الموارد: النصوص والألوان والأبعاد

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

الموارد: النصوص والألوان والأبعاد

من أبرز القرارات المعمارية في نظام Android فصل القيم عن الكود فصلاً تامًا. الألوان والنصوص والأحجام والصور — لا شيء من هذا يصح وضعه مُضمَّنًا داخل فئة Java. إنها تقيم في ملفات XML للموارد تحت مجلد res/، ويولّد نظام البناء فئةً اسمها R تمنح كودك معرّفات صحيحة النوع (type-safe integer handles) لكل مورد. يتناول هذا الدرس أنواع موارد القيم الثلاثة الأساسية — النصوص والألوان والأبعاد — ويشرح كيفية استخدامها بشكل صحيح على كل مستوى من مستويات التطبيق.

لماذا نُخرج القيم إلى موارد خارجية؟

تخيّل أنك وضعت لونًا مضمَّنًا مباشرةً في Java:

textView.setTextColor(0xFF1A73E8); // قيمة سداسية مضمَّنة — تجنّب هذا

الآن تخيّل أن المصمم غيّر اللون الرئيسي للعلامة التجارية. عليك البحث في كل ملف Java، وإيجاد كل قيمة سداسية حرفية، وأن تأمل أنك لم تفتك أيٌّ منها. وإذا أضفت دعم اللغة العربية لاحقًا، فإن كل نص مضمَّن يصبح عبئًا على الصيانة.

مع ملفات الموارد تغيّر سطرًا واحدًا في colors.xml فيتحدث كل عنصر واجهة يشير إلى @color/brand_primary تلقائيًا — في كل التخطيطات وكل السمات وكل كثافات الشاشة وكل اللغات.

بنية مجلد res/

ينظّم Android الموارد في مجلدات فرعية مُصنَّفة داخل app/src/main/res/:

  • res/values/strings.xml — ثوابت النصوص والجمع
  • res/values/colors.xml — ثوابت الألوان
  • res/values/dimens.xml — ثوابت الأبعاد (dp, sp)
  • res/values/styles.xml — الأنماط والسمات
  • res/drawable/ — الرسومات المتجهة والنقطية
  • res/layout/ — ملفات التخطيط XML
  • res/values-ar/strings.xml — تجاوزات النصوص العربية (التوطين)
  • res/values-night/colors.xml — تجاوزات ألوان الوضع الليلي
مؤهّلات الإعداد (Configuration qualifiers) هي لواحق المجلدات كـ -ar و-night و-sw600dp. يختار محلّل موارد Android المجلد الأفضل تطابقًا وقت التشغيل. مجلدك الافتراضي values/ هو الاحتياطي عندما لا يتطابق أي مؤهّل. هذه الآلية الواحدة تتعامل مع التوطين والوضع الليلي وحجم الشاشة والكثافة والاتجاه — كل ذلك دون عبارة if واحدة في Java.

النصوص (Strings)

كل نص مرئي للمستخدم ينبغي أن يعيش في res/values/strings.xml:

<!-- res/values/strings.xml --> <resources> <string name="app_name">My App</string> <string name="welcome_message">Welcome, %1$s!</string> <string name="login_button">Log In</string> <string name="items_count">You have %1$d items in your cart.</string> </resources>

للإشارة إلى نص داخل XML للتخطيط استخدم البادئة @string/:

<Button android:id="@+id/btnLogin" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/login_button" />

لتحميل نص في كود Java استخدم getString() — متاحة على أي Context (Activity أو Fragment أو Service):

// نص بسيط String appName = getString(R.string.app_name); // نص بوسيطات تنسيق (محدّد مكان %1$s) String greeting = getString(R.string.welcome_message, "Edrees"); // النتيجة: "Welcome, Edrees!" textView.setText(greeting);
استخدم وسيطات التنسيق بدلاً من تسلسل النصوص. getString(R.string.items_count, cart.size()) يتيح للمترجمين إعادة ترتيب الجملة بصورة طبيعية في لغتهم. مع التسلسل تُلزم كل لغة بالترتيب الإنجليزي للكلمات، وهو ما قد يبدو غريبًا في العربية أو اليابانية أو الألمانية.

الجمع (Plurals)

كثير من اللغات تمتلك أكثر من صيغتَي مفرد وجمع. استخدم <plurals> للتعامل معها بشكل صحيح:

<!-- res/values/strings.xml --> <plurals name="unread_messages"> <item quantity="one">You have %1$d unread message.</item> <item quantity="other">You have %1$d unread messages.</item> </plurals>
int count = 3; String msg = getResources().getQuantityString( R.plurals.unread_messages, count, count); // "You have 3 unread messages."

الألوان (Colors)

تنتمي الألوان إلى res/values/colors.xml كقيم سداسية بصيغة ARGB أو RGB:

<!-- res/values/colors.xml --> <resources> <color name="brand_primary">#1A73E8</color> <color name="brand_primary_dark">#1558B0</color> <color name="surface">#FFFFFF</color> <color name="on_surface">#1C1B1F</color> <color name="error">#B3261E</color> </resources>

استخدم الألوان في XML بالبادئة @color/:

<TextView android:textColor="@color/on_surface" android:background="@color/surface" ... />

حلّ لون في Java باستخدام ContextCompat.getColor():

import androidx.core.content.ContextCompat; int brandColor = ContextCompat.getColor(this, R.color.brand_primary); myView.setBackgroundColor(brandColor);
لا تستدعِ getResources().getColor(int) مباشرةً. هذه الدالة أُهملت في API 23 لأنها تتجاهل السمة الحالية. استخدم دائمًا ContextCompat.getColor(context, R.color.xxx)، وهي آمنة على جميع مستويات API وتحترم السمات بشكل صحيح.

الأبعاد (Dimensions)

تُخزَّن الأبعاد في res/values/dimens.xml. ثمة وحدتان في Android يجب أن تعرفهما:

  • dp (بكسل مستقل عن الكثافة) — استخدمه لجميع أحجام التخطيط (الهوامش والحشو والعروض والارتفاعات). يعادل 1 dp بكسلاً فيزيائيًا واحدًا على شاشة بكثافة 160 dpi؛ يقوم النظام بالتوسع تلقائيًا على الشاشات عالية الكثافة.
  • sp (بكسل مستقل عن الحجم) — استخدمه حصرًا لأحجام النصوص. مثل dp لكنه يتوسع أيضًا مع إعداد حجم الخط في النظام لدى المستخدم. لا تضع textSize أبدًا بوحدة dp.
<!-- res/values/dimens.xml --> <resources> <dimen name="screen_margin">16dp</dimen> <dimen name="card_corner_radius">12dp</dimen> <dimen name="button_height">48dp</dimen> <dimen name="text_body">14sp</dimen> <dimen name="text_headline">24sp</dimen> </resources>

طبّق الأبعاد في XML:

<Button android:layout_width="match_parent" android:layout_height="@dimen/button_height" android:layout_margin="@dimen/screen_margin" android:textSize="@dimen/text_body" />

اقرأ بُعدًا في Java (يُرجع بيكسلات مُعدَّلة وفق الشاشة الحالية):

float marginPx = getResources().getDimension(R.dimen.screen_margin); // استخدم TypedValue للتحويل اليدوي من dp إلى px عند الحاجة: float dp = 16f; float px = TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());

الوصول إلى الموارد — الفئة R

في كل مرة تبني فيها مشروعك، يولّد ملحق Android Gradle الفئةَ R.java (أو ما يعادلها ثنائيًا). تحتوي على فئات ساكنة متداخلة — R.string وR.color وR.dimen وR.id وR.layout وغيرها — تحمل كل منها ثوابت صحيحة (integer constants) تُعيّن إلى المورد الفعلي. لا تُنشئ هذه الفئة ولا تعدّلها؛ أنت فقط تقرأ منها.

// كل هذه تُترجَم إلى عمليات بحث صحيحة وقت التشغيل: R.string.app_name // عدد صحيح int R.color.brand_primary // عدد صحيح int R.dimen.screen_margin // عدد صحيح int
إذا أظهر Android Studio رسالة "Cannot resolve symbol R" بعد إضافة مورد جديد، فهذا يعني في الغالب أن XML مكتوب بشكل خاطئ (خطأ إملائي في اسم وسم أو عنصر إغلاق مفقود). أصلح XML وأعد البناء — Build > Make Project. تُعاد توليد الفئة R عند كل بناء ناجح.

التوطين: توفير نصوص بديلة

لترجمة النصوص إلى العربية، أنشئ res/values-ar/strings.xml ووفّر فقط النصوص المختلفة. لا حاجة لتكرار كل نص — تجاوز فقط ما يتغير:

<!-- res/values-ar/strings.xml --> <resources> <string name="app_name">تطبيقي</string> <string name="welcome_message">أهلاً بك، %1$s!</string> <string name="login_button">تسجيل الدخول</string> </resources>

عندما يكون لغة الجهاز عربية، يُعيد getString(R.string.login_button) تلقائيًا "تسجيل الدخول" دون أي تغيير في الكود. هذه هي القيمة الكاملة لنظام الموارد.

الخلاصة

ملفات الموارد ليست مجرد إجراء شكلي — إنها الآلية التي تجعل تطبيقك صحيحًا على كل جهاز ولغة وسمة دون منطق تفريعي في Java. ضع جميع النصوص المرئية للمستخدم في strings.xml، وجميع الألوان في colors.xml، وجميع قياسات التخطيط في dimens.xml. أشر إليها بـ @string/ و@color/ و@dimen/ في XML وبالفئة R في Java. في الدرس القادم ستستخدم Intents للتنقل بين Activity والأخرى — وستنتقل معك التسميات والألوان التي عرّفتها هنا.