التعامل مع القياسات على الشاشة في Xamarin


حسام برهان

سنتحدث في هذا الدرس من سلسلة برمجة تطبيقات الأجهزة المحمولة باستخدام Xamarin عن موضوع مهم يسبّب في الكثير من الأحيان بعض الارتباك لمبرمجيّ تطبيقات الأجهزة المحمولة، وهو موضوع التعامل مع القياسات والحجوم على الشاشة. سنبدأ أولًا بلمحة تاريخيّة سريعة، ننتقل من خلالها إلى فهم واحدات القياسات المستخدمة في Xamarin.Forms.

xamarin-007.png

لمحة تاريخيّة

يتكوّن أيّ جهاز عرض من مصفوفة مستطيلة من البيكسل Pixels. فأيّ جسم يُعرض على الشاشة سيكون له مساحة تُقدّر بالبيكسل. تعامل المبرمجون منذ البداية مع البيكسل كواحدة قياس معتمدة لرسم الأجسام والأشكال المختلفة على الشاشة. وعلى الرغم من أنّه لا ينبغي على المبرمج عادةً أن يعتمد على قياسات ثابتة للعناصر المرئيّة في التطبيقات التي يكتبها، إلّا أنّه في كثير من الأحيان يكون من الضروري القيام بذلك حسب متطلّبات التطبيق. فمع اختلاف أحجام الشاشات واختلاف كثافة البيكسل لكل شاشة نظرًا للتقدّم التقنيّ الذي شهده هذا المجال، أصبح أن يكون للتطبيق نفس الشكل تقريبًا على مختلف أنواع الشاشات أمرًا فيه تحدّ كبير للمبرمجين.
لشاشات سطح المكتب طيف واسع من قياسات البيكسل، من القياس القديم 640x480  (أي 640 بيكسل للعرض، و480 بيكسل للطول) حتى بلغت هذه الأيّام بضعة آلاف لكل بُعد. كما تتمتّع الشاشات أيضًا بقياس فيزيائي يُقدّر عادةً بالبوصة Inch، وهو المسافة القُطريّة لمستطيل الشاشة. فمن خلال قياس البيكسل لأيّ شاشة والقياس الفيزيائي لها، نستطيع حساب دقّة الشاشة resolution أو ما يُعرف بكثافة البيكسل في كلّ بوصة PPI وغالبًا ما يسمّى بعدد النقاط في كلّ بوصة DPI (اختصار لـ Dots Per Inch).
فمثلًا من أجل شاشة قديمة لها قياس بكسل 800x600، يمكننا ببساطة حسب نظرية فيثاغورث في المثلث القائم، أن نستنتج أنّ قطر هذه الشاشة يساوي 1000 بيكسل.

fig01.png

حيث استخدمنا العلاقة التالية في حساب القطر: formula-001.png

فإذا كان لهذه الشاشة القياس الفيزيائي 13 بوصة مثلًا فعندها يمكننا بسهولة أن نحسب كثافة البيكسل في البوصة أو DPI لها بالعلاقة البسيطة التالية:formula-002.png

بالمقابل ومن أجل نفس هذه الشاشة ذات 13 بوصة، يمكننا أن نجد في هذه الأيّام قياسات بيكسل مثل 2560x1600 وهذا ما يعطي DPI لهذه الشاشة يُقدّر بـ 230 تقريبًا. فهذا يعني أنّ أيّ جسم على الشاشة الجديدة، ولنقل أنّ مساحته 100 بيكسل مربّع مثلًا، سيشغل ثلث المساحة الظاهرية التي يشغلها الجسم نفسه ولكن على الشاشة القديمة، مما سيؤدّي بالطبع إلى اختلاف كبير في أشكال التطبيقات على الشاشات المختلفة.
بدأت تظهر حلول عمليّة في الواقع مع تطوّر أنظمة التشغيل الخاصّة بالحواسيب المكتبيّة، والتي تمّ تكييفها لاحقًا مع الأجهزة المحمولة. حيث عملت شركات مثل مايكروسوفت وآبل على ابتكار أنظمة قياس تعتمد على واحدات لا تتعلّق بالجهاز (الشاشة) device-independent units وذلك بدلًا من البيكسل. حيث يقع على عاتق نظام التشغيل أن يحوّل هذه القياسات المصمّمة بنظام القياس الجديد إلى قياسات بيكسل مناسبة. وهكذا يستطيع المبرمجون كتابة تطبيقات تعتمد نظام قياس مستقل، بحيث تظهر تطبيقاتهم بقياس موحّد تقريبًا على مختلف أنواع الشاشات.
سلكت غوغل في أندرويد نفس السلوك لضمان أن تظهر الأشكال ذات القياسات المحدّدة بشكل موحّد على جميع شاشات أجهزة أندرويد المتنوّعة أصلًا.

الحل الذي توفّره Xamarin.Forms

وفّرت Xamarin.Forms حلّا جيّدًا لهذه المسألة من خلال الفرضيّة البسيطة التالية: كل 160 وحدة قياس (مستقلة عن الجهاز) تقابل بوصة واحدة. وهذا ما يُعادل 64 وحدة قياس لكل سنتيمتر. والفرضيّة السابقة صالحة من أجل أي تطبيق يعمل على أندرويد أو iOS أو Windows Phone.
أيّ عنصر مرئي يظهر على الشاشة يرث من الصنف VisualElement. يمتلك هذا الصنف خاصيّتين مفيدتين: Width وHeight. تُعبّر هاتين الخاصيتين عن عرض Width وارتفاع Height أي عنصر مرئي على الشاشة بواحدات مستقلة عن الجهاز كما اتفقنا. تكون القيمة الابتدائيّة لكل منهما -1 في البداية، وتعطينا قيمًا صحيحة فقط عندما يُجهَّز التخطيط الذي سيظهر على الشاشة ويأخذ كلّ عنصر مكانه.
يُعرّف الصنف VisualElement أيضًا حدثّا اسمه SizeChanged والذي يحدث عندما تتغيّر قيمة إحدى الخاصيتين Width أو Height. قد تتغيّر قيمتي هاتين الخاصيتين لأسباب متنوّعة منها تدوير الشاشة مثلًا.
من الممكن تثبيت معالج للحدث SizeChanged من أجل أي عنصر مرئي يظهر على الصفحة بما فيها الصفحة نفسها. سنتناول في الفقرة التالية برنامجًا بسيطًا يُظهر قياس الشاشة التي يعمل عليها ولكن بالواحدات المستقلّة عن الجهاز.

برنامج الحصول على قياس الشاشة

أنشئ تطبيقًا جديدًا من النوع Blank App (Xamarin.Forms Portable) وسمّه GetSize. وكما اتفقنا احذف جميع المشاريع باستثناء المشروعين GetSize (Portable) وGetSize.Droid. أضف صفحة محتوى جديدة ContentPage إلى المشروع GetSize (Portable) وسمّها GetSizePage واحرص على أن يكون الصنف GetSizePage على الشكل التالي:
1

 
1	public class GetSizePage : ContentPage
2	{
3	    private Label label;
4	
5	    public GetSizePage()
6	    {
7	        this.label = new Label
8	        {
9	            FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)),
10	            HorizontalOptions = LayoutOptions.Center,
11	            VerticalOptions = LayoutOptions.Center,
12	        };
13	
14	        Content = this.label;
15	
16	        SizeChanged += GetSizePage_SizeChanged;
17	    }
18	
19	    private void GetSizePage_SizeChanged(object sender, EventArgs e)
20	    {
21	        label.Text = String.Format("{0} \u00D7 {1}", this.Width, this.Height);
22	    }
23	}

لاحظ في البداية أنّنا صرّحنا عن الحقل الخاص label وهو من النوع Label في السطر 3، حيث سنُسند إليه مرجع للصيقة جديدة سننشئها لاحقًا في بانية الصنف GetSizePage في السطر 7. سبب إنشاء هذا الحقل، هو الحاجة للوصول إلى اللصيقة ضمن معالج الحدث كما سنرى بعد قليل. أسندنا خياري التموضع الأفقي HorizontalOptions والتموضع الرأسي VerticalOptions في السطرين 10 و11 على الترتيب لهذه اللصيقة الجديدة لتظهر في وسط الصفحة. لاحظ أنّنا لا نستخدم مخطّط مكدّس في هذا البرنامج، بل نُسند اللصيقة مباشرةً إلى الخاصيّة Content للصفحة في السطر 14. الأمر الجديد بالنسبة إلينا في هذه السلسلة هو إسناد معالج حدث event handler للحدث SizeChanged للصفحة في السطر 16. ومعالج الحدث الذي أسميته GetPageSize_SizeChanged مصرّح عنه في الأسطر من 19 حتى 22.
إذا نظرت إلى السطر 21 ستجد أنّنا نولّد نصًّا تنسيقيًّا لعرضه ضمن اللصيقة (نُسنده إلى الخاصيّة Text لها). لاحظ الرمز \u00D7 الذي يُستخدم لتوليد إشارة الضربxكما سنرى بعد قليل. كما تجدر الملاحظة أنّنا نقرأ خاصيّتي العرض Width والارتفاع Height للصفحة وندرجها ضمن النص التنسيقي المولّد. انتقل إلى بانية الصنف App واحرص على أن تكون كما يلي:

public App()
{
    // The root page of your application
    MainPage = new GetSizePage();
}

نفّذ البرنامج باستخدام F5 لتحصل على شكل شبيه بما يلي:

fig02.png

أود أن أؤكّد على أنّ القياس الذي تراه في الشكل السابق هو بالواحدات المستقلّة عن الجهاز وليس بالبيكسل. وهو لا يتضمّن شريط الحالة status bar العلويّ، ولا يتضمّن أيضًا المساحة المخصّصة لظهور الأزرار في الأسفل (في حال كان الجهاز يدعم ذلك). إذا حاولت تدوير الشاشة 90 درجة. ستحصل على قياس مختلف:

fig03.png

ملاحظة
لقد نفّذت البرنامج السابق على جهاز Samsung Galaxy Core Prime.

الخلاصة

يُعتبر هذا الدرس أساسيًّا لفهم كيفيّة التعامل مع القياسات في تطبيقات Xamarin.Forms حيث تحدثنا عن القياسات بمفهوميها القديم والحديث. وسبل التعامل مع الواحدات المستقلّة عن الجهاز device-independent units ودورها الأساسيّ في توحيد قياسات العناصر المرئيّة ذات الأبعاد الثابتة على مختلف أنواع الشاشات. سنتناول في الدرس القادم أمثلة عمليّة مفيدة حول استخدام الواحدات المستقلّة عن الجهاز في كتابة تطبيقين بسيطين. يتناول التطبيق الأوّل رسم شكل بسيط ذو مساحة محدّدة على الشاشة، في حين يتناول التطبيق الثاني ساعة رقميّة بسيطة توائم النص المعروض بحسب حجم الشاشة التي يعمل عليها التطبيق.





تفاعل الأعضاء


لا توجد أيّة تعليقات بعد



يجب أن تكون عضوًا لدينا لتتمكّن من التعليق

انشاء حساب جديد

يستغرق التسجيل بضع ثوان فقط


سجّل حسابًا جديدًا

تسجيل الدخول

تملك حسابا مسجّلا بالفعل؟


سجّل دخولك الآن