البحث في الموقع
المحتوى عن 'absolute-layout'.
-
تحدثنا في الدرس السابق عن المخطّط المطلق Absolute Layout وعن فائدته في التحكّم بمواضع وأحجام العناصر المرئيّة باستخدام الواحدات المستقلة عن الجهاز independent-device units. سنتابع عملنا في هذا الدرس ضمن سلسلة تعلّم برمجة تطبيقات أندرويد باستخدام Xamarin.Forms ببناء تطبيق عمليّ جميل يُعتبر تحسينًا لتطبيق تحريك المربّعات الثلاثة المتداخلة الذي أوردناه في نهاية الدرس السابق. يوضّح لنا هذا التطبيق المُحسّن كيفيّة التحكّم بهذه المربّعات باستخدام أزرار تمثّل الاتجاهات الأربعة الأساسيّة (أعلى – أسفل - يمين – يسار). وصف التطبيق واجهة هذا التطبيق بسيطة. فبالإضافة إلى المربّعات الثلاثة. سنضع أربعة أزرار عاديّة تمثّل الاتجاهات الأربعة الأساسيّة. بحيث تتوضّع هذه المربّعات أسفل ووسط الشاشة. عند بدء تشغيل التطبيق سنحصل على شكل شبيه بما يلي: لاحظ كيف تتوسّط الأزرار الأربعة الشاشة أفقيًّا، سنوضّح سبب ذلك بعد قليل. إذا حاول المستخدم في الوضع الحالي نقر أي زر من الأزرار الأربعة السابقة سيعرض له البرنامج رسالة يطلب منه فيها اختيار أحد المربّعات أولًا لتحريكه. عندما ينقر المستخدم على أحد المربّعات السابقة، ستتلوّن الأزرار الأربعة بنفس لون المربّع الذي نقره المستخدم. وعندها فقط يمكن استخدام الأزرار لتحريك المربّع المُختار في أيّ اتجاه يرغبه. في الشكل التالي قمت باختيار المربّع الأحمر، ومن ثمّ تحريكه إلى الأعلى ثم إلى اليمين: يمكنك تكرار نفس الخطوات السابقة لتحريك أيّ مربّع آخر. هذا هو مبدأ هذا البرنامج، وهو بسيط كما ترى، ولكن سنتعلّم من خلاله بعض التقنيّات المفيدة في ضبط التموضع ضمن المخطّط المطلق، بالإضافة إلى تعلّم كيفيّة إكساب عناصر BoxView قابلية الاستجابة إلى أحداث اللمس كما سنرى بعد قليل. تطبيق تحريك المربّعات باستخدام الأزرار أنشئ مشروعًا جديدًا من النوع Blank App (Xamarin.Forms Portable) وسمّه MoveSquaresByButtons، ثم أبق فقط على المشروعين MoveSquaresByButtons (Portable) و MoveSquaresByButtons.Droid كما وسبق أن فعلنا في هذا الدرس. بعد ذلك سنضيف صفحة محتوى تعتمد على رُماز XAML كما وسبق أن فعلنا في هذا الدرس سنسمّيها MoveSquaresByButtonsPage. احرص على أن تكون محتويات هذه الصفحة على الشكل التالي: 1 <?xml version="1.0" encoding="utf-8" ?> 2 <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 3 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 4 x:Class="MoveSquaresByButtons.MoveSquaresByButtonsPage" 5 SizeChanged="PageResize"> 6 7 <AbsoluteLayout> 8 <BoxView x:Name="boxAccent" 9 Color="Accent" 10 AbsoluteLayout.LayoutBounds="25, 100, 100, 100" 11 Opacity="0.5" /> 12 13 <BoxView x:Name="boxRed" 14 Color="Red" 15 AbsoluteLayout.LayoutBounds="75, 150, 100, 100" 16 Opacity="0.5"/> 17 18 <BoxView x:Name="boxGreen" 19 Color="Green" 20 AbsoluteLayout.LayoutBounds="125, 200, 100, 100" 21 Opacity="0.5"/> 22 23 <Button x:Name="btnMoveUp" 24 StyleId="MoveBoxUp" 25 Clicked="MoveBox_Clicked" 26 FontSize="Large" 27 BorderWidth="0" 28 Text="↑"/> 29 30 <Button x:Name="btnMoveDown" 31 StyleId="MoveBoxDown" 32 Clicked="MoveBox_Clicked" 33 FontSize="Large" 34 BorderWidth="0" 35 Text="↓"/> 36 37 <Button x:Name="btnMoveRight" 38 StyleId="MoveBoxRight" 39 Clicked="MoveBox_Clicked" 40 FontSize="Large" 41 Text="→"/> 42 43 <Button x:Name="btnMoveLeft" 44 StyleId="MoveBoxLeft" 45 Clicked="MoveBox_Clicked" 46 FontSize="Large" 47 Text="←"/> 48 </AbsoluteLayout> 49 50 </ContentPage> لاحظ معي أنّ عناصر BoxView الموجودة في الأسطر من 8 حتى 21 هي نفسها الموجودة في تطبيق المربّعات المتداخلة من الدرس السابق. الجديد هنا هو تعيين معالج للحدث SizeChanged للصفحة (السطر 5) الذي يُستدعى كلّما اقتضت الحاجة إلى تغيير حجم الصفحة، وهو يحدث عادةً عند البدء بتشغيل التطبيق، وأيضًا عندما يُغيّر المستخدم اتجاه الجهاز من عمودي إلى أفقي أو بالعكس. الجديد هنا أيضًا هو الأزرار الأربعة الموجودة في الأسطر من 23 حتى 47. من الواضح أنّ جميع العناصر المرئيّة السابقة موجودة ضمن مخطّط مطلق AbsoluteLayout يضمن لنا التحكّم بمواقعها وبأحجامها كما أشرنا. تأمّل معي هذا الرماز الذي يمثّل زر الحركة نحو الأعلى: <Button x:Name="btnMoveUp" StyleId="MoveBoxUp" Clicked="MoveBox_Clicked" FontSize="Large" BorderWidth="0" Text="↑"/> لقد وضعت القيمة "MoveBoxUp" للخاصيّة StyleId حيث سنستفيد منها ضمن معالج الحدث MoveBox_Clicked الذي أسندته للحدث Clicked كما هو واضح. سنرى بعد قليل الشيفرة البرمجيّة الخاصّة بهذا المعالج. الأمر الملفت هنا هو القيمة "&H8593;" التي ضبطُّها لخاصيّة النص. سيؤدي ذلك إلى وضع رمز السهم المتجه إلى أعلى ليظهر على هذا الزر. بنفس هذا الأسلوب تمّ تجهيز الأزرار الثلاثة الباقية التي سأسند إلى كلّ منها قيمة مختلفة للخاصيّة StyleId لتعبّر عن وظيفتها، ولكن مع إسناد نفس معالج الحدث MoveBox_Clicked لجميعها. لننتقل الآن إلى ملف الشيفرة البرمجية الموافق لصفحة المحتوى السابقة. احرص على أن تكون محتوياته على الشكل التالي: 1 using System; 2 using Xamarin.Forms; 3 4 namespace MoveSquaresByButtons 5 { 6 public partial class MoveSquaresByButtonsPage : ContentPage 7 { 8 private BoxView selectedBoxView; 9 private const double BUTTON_MARGIN = 5; 10 private const double PAGE_BUTTOM_MARGIN = 10; 11 private const double MOVE_AMOUNT = 15; 12 13 public MoveSquaresByButtonsPage() 14 { 15 InitializeComponent(); 16 17 TapGestureRecognizer tapGesture = new TapGestureRecognizer(); 18 19 tapGesture.Tapped += TapGesture_Tapped; 20 21 boxAccent.GestureRecognizers.Add(tapGesture); 22 boxRed.GestureRecognizers.Add(tapGesture); 23 boxGreen.GestureRecognizers.Add(tapGesture); 24 } 25 26 private void TapGesture_Tapped(object sender, EventArgs e) 27 { 28 selectedBoxView = (BoxView)sender; 29 30 btnMoveUp.BackgroundColor = selectedBoxView.Color; 31 btnMoveDown.BackgroundColor = selectedBoxView.Color; 32 btnMoveLeft.BackgroundColor = selectedBoxView.Color; 33 btnMoveRight.BackgroundColor = selectedBoxView.Color; 34 } 35 36 37 private void MoveBox_Clicked(object sender, EventArgs e) 38 { 39 Button moveButton = (Button)sender; 40 Rectangle rect; 41 42 if (selectedBoxView == null) 43 { 44 DisplayAlert("اختيار مربع", "اختر مربّعًا من فضلك", "موافق"); 45 return; 46 } 47 48 rect = AbsoluteLayout.GetLayoutBounds(selectedBoxView); 49 50 if (moveButton.StyleId == "MoveBoxUp") 51 { 52 rect = new Rectangle(rect.X, rect.Y - MOVE_AMOUNT, rect.Width, rect.Height); 53 } 54 else if (moveButton.StyleId == "MoveBoxDown") 55 { 56 rect = new Rectangle(rect.X, rect.Y + MOVE_AMOUNT, rect.Width, rect.Height); 57 } 58 else if (moveButton.StyleId == "MoveBoxRight") 59 { 60 rect = new Rectangle(rect.X + MOVE_AMOUNT, rect.Y, rect.Width, rect.Height); 61 } 62 else if (moveButton.StyleId == "MoveBoxLeft") 63 { 64 rect = new Rectangle(rect.X - MOVE_AMOUNT, rect.Y, rect.Width, rect.Height); 65 } 66 67 AbsoluteLayout.SetLayoutBounds(selectedBoxView, rect); 68 } 69 70 private void PageResize(object sender, EventArgs e) 71 { 72 ContentPage page = (ContentPage)sender; 73 74 double buttonWidth = btnMoveUp.Width; 75 double buttonHeight = btnMoveUp.Height; 76 double buttonsAreaWidth = buttonWidth * 3 + BUTTON_MARGIN * 2; 77 double buttonsAreaLeftDisplacement = (page.Width - buttonsAreaWidth) / 2; 78 79 AbsoluteLayout.SetLayoutBounds(btnMoveDown, 80 new Rectangle(buttonsAreaLeftDisplacement + buttonWidth + BUTTON_MARGIN, 81 page.Height - buttonHeight - PAGE_BUTTOM_MARGIN, 82 buttonWidth, 83 buttonHeight)); 84 85 AbsoluteLayout.SetLayoutBounds(btnMoveUp, 86 new Rectangle(buttonsAreaLeftDisplacement + buttonWidth + BUTTON_MARGIN, 87 page.Height - buttonHeight * 2 - BUTTON_MARGIN - PAGE_BUTTOM_MARGIN, 88 buttonWidth, 89 buttonHeight)); 90 91 92 AbsoluteLayout.SetLayoutBounds(btnMoveLeft, 93 new Rectangle(buttonsAreaLeftDisplacement, 94 page.Height - 3 * buttonHeight / 2 - PAGE_BUTTOM_MARGIN, 95 buttonWidth, 96 buttonHeight)); 97 98 AbsoluteLayout.SetLayoutBounds(btnMoveRight, 99 new Rectangle(buttonsAreaLeftDisplacement + buttonWidth * 2 + BUTTON_MARGIN * 2, 100 page.Height - 3 * buttonHeight / 2 - PAGE_BUTTOM_MARGIN, 101 buttonWidth, 102 buttonHeight)); 103 } 104 } 105 } يحتوي الصنف MoveSquaresByButtonsPage بشكل أساسيّ على بانية وعدّة توابع هي في الواقع معالجات للأحداث التي يحتاج إليها التطبيق. سنتحدّث عنها بشيء من التفصيل. البانية إذا نظرت إلى البانية (الأسطر من 13 حتى 24) فستجد أنّنا نعرّف ضمنها كائنًا من النوع TapGestureRecognizer (السطر 17) سنُسند هذا الكائن إلى المتغيّر tapGesture كما هو واضح. يفيد هذا الكائن في إكساب عنصر BoxView قابلية النقر لأنّه –وبشكل مغاير لعنصر الزر Button- لا يمتلكها أصلًا. نعمل في السطر 19 على تعيين معالج لحدث النقر (اللمس) Tapped للكائن السابق واسمه TapGesture_Tapped (سنتحدّث عنه بعد لحظة). العملية الأخيرة التي نجريها في البانية هي إضافة الكائن tapGesture إلى كل من المربّعات الثلاثة عن طريق التابع Add للمجموعة GestureRecognizers لكل مربع من هذه المربعات (الأسطر من 21 حتى 23). معالج حدث النقر على المربّعات TapGesture_Tapped يمكنك رؤية هذا المعالج في الأسطر من 26 حتى 34. الذي يحدث ضمن هذا المعالج هو أنّنا نحصل على مرجع المربّع الذي نقره المستخدم (السطر 28) ونُسنده إلى الحقل selectedBoxView وهو حقل خاص من النوع BoxView مصرّح عنه في السطر 8، ثم نلوّن أزرار التحريك الأربعة بنفس لون المربّع الذي نقره (لمسه) المستخدم. معالج حدث النقر على الأزرار MoveBox_Clicked بالنسبة لمعالج الحدث MoveBox_Clicked وهو موجود في الأسطر من 37 حتى 68 فهو يُستدعى عندما ينقر المستخدم أيًا من أزرار التحريك الأربعة. ووظيفته هو تحريك المربّع الذي اختاره المستخدم مسبقًا في الاتجاه المطلوب. نحصل في السطر 39 على مرجع الزر الذي تمّ نقره، ثمّ نختبر فيما إذا كان المستخدم قد قام فعلًا باختيار مربّع من قبل (الأسطر من 42 حتى 46) حيث يُعطي التطبيق تنبيهًا للمستخدم في حال لم يكن قد اختار مربّعًا من قبل. انظر الآن إلى السطر 48: rect = AbsoluteLayout.GetLayoutBounds(selectedBoxView); يُرجع التابع GetLayoutBounds من الصنف AbsoluteLayout بنية من النوع Rectangle لأيّ عنصر مرئي نمرّره له موجود ضمن المخطّط المطلق. نحتاج هنا إلى معرفة المستطيل المُحدّد للمربّع الذي اختاره المستخدم من قبل. يُعطينا المستطيل المحدّد معلومات حول إحداثيّي الزاوية اليسرى العليا للمربّع (الإحداثي X والإحداثي Y) بالإضافة إلى عرض Width وارتفاع Height المربّع. تقارن بعد ذلك عبارات if الموجودة في الأسطر من 50 حتى 65 الخاصيّة StyleId للزر الذي نقره المستخدم مع عدّة نصوص معدّة سلفًا، لمعرفة الاتجاه المطلوب التحريك وفقه. لكي يتمكّن البرنامج من تعديل الإحداثي المطلوب على المستطيل المحدّد للمربّع وذلك بالانتقال المسافة المعيّنة بالثابت MOVE_AMOUNT الذي عرّفناه في السطر 11. بعد ذلك يتم تعيين المستطيل المحدّد الجديد (بعد التعديل) إلى المربّع نفسه (السطر 67) مما يؤدي إلى إعطاء انطباع بالحركة. معالج حدث تغيير حجم الصفحة PageResize بالنسبة للمعالج PageResize (الأسطر من 70 حتى 103) فهو يُستدعى عندما يطرأ تعديل على حجم الصفحة. وتنحصر وظيفة هذا المعالج في الحقيقة بوضع أزرار الحركة الأربعة بالشكل الذي أوضحناه أوّل الدرس. حيث يعتمد على ثابتين: الثابت BUTTON_MARGIN (مصرّح عنه في السطر 9) ويمثّل التباعد بين أيّ زرّين. الثابت PAGE_BUTTOM_MARGIN (مصرّح عنه في السطر 10) ويمثّل الهامش السفلي لزر الحركة نحو الأسفل وذلك عن أسفل الشاشة. الأمر الملفت ضمن هذا المعالج هو كيفية حساب مسافة الإزاحة الأفقيّة لتوسيط مجموعة الأزرار أفقيًّا، حيث يتم ذلك بأسلوب حسابي بسيط. إذ أنّ الشيفرة تحسب العرض الإجمالي لثلاثة أزرار مضافًا إليها قيمتي هامشين (بين كل زرين هامش) وتضعه ضمن المتغيّر buttonsAreaWidth: double buttonsAreaWidth = buttonWidth * 3 + BUTTON_MARGIN * 2; ثمّ تقوم بطرح القيمة السابقة من عرض الصفحة page.Width وتقسّمه على 2. فنحصل على مقدار الإزاحة الأفقيّة الواجب تطبيقها من الجهة اليسرى وتضعها ضمن المتغيّر buttonsAreaLeftDisplacement: double buttonsAreaLeftDisplacement = (page.Width - buttonsAreaWidth) / 2; ما تبقى من عبارات برمجيّة هي بسيطة في الواقع ولا تعدو عن كونها عبارات تنظيميّة تهدف إلى وضع الأزرار على نحو متناسق. انتقل أخيرًا إلى الملف App.cs واحرص على أن تكون بانيته على الشكل التالي: public App() { // The root page of your application MainPage = new MoveSquaresByButtonsPage(); } أصبح التطبيق جاهزًا للتنفيذ. يمكنك تنفيذه بضغط المفتاح F5 كما تعلّمنا. الخلاصة تحدثنا في هذا الدرس عن تطبيق عمليّ يعتمد على المخطّط المطلق Absolute Layout. حيث تعلّمنا كيفيّة التحكم الدقيق بمواضع العناصر لإكساب واجهة المستخدم شكلًا دقيقًا مُعد مسبقًا. كما تعلّمنا كيفيّة إكساب أيّ عنصر من النوع BoxView إمكانية الاستجابة لحدث النقر عن طريق كائن من الصنف TapGestureRecognizer. وتعلّمنا أيضًا تقنيّات بسيطة لتحريك العناصر المرئية على الشاشة. سننهي بهذا المقال حديثنا عن المخطّطات التي تدعمها Xamarin. في الحقيقة يُعتبر الفهم الجيّد للمخطّطات مفتاحًا أساسيًّا لبناء واجهة المستخدم.
-
المخطّط المطلق Absolute Layout هو من المخطّطات المعتمدة في Xamarin، وتقوم فكرته على السماح للعناصر المرئيّة الموجودة ضمنه بأن يكون تموضعها بشكل مطلق، أي من الممكن ضبط مكان التموضع بالإضافة إلى عرض وارتفاع كل عنصر مرئي بشكل دقيق باستخدام الواحدات المستقلة عن الجهاز independent-device units. ولعلّ هذا المفهوم ليس جديدًا، بل هو من المفاهيم القديمة في عالم البرمجة، فقد كان المبرمجون قديمًا (وما زالوا في بعض الأحيان حتى اليوم) يعتمدون على بناء واجهات التطبيقات بتعريف موضع وأبعاد (العرض والارتفاع) كل عنصر بشكل دقيق ضمن الواجهة. سنطّلع في هذا الدرس ضمن سلسلة تعلّم برمجة تطبيقات أندرويد باستخدام Xamarin.Forms على كيفيّة التعامل مع هذا المخطّط باستخدام رماز XAML من خلال تطبيقين بسيطين. تطبيق المربّعات المتداخلة تعتمد فكرة هذا التطبيق على وجود 3 مربّعات متداخلة فيما بينها بحيث يمتلك كلّ منها شفافيّة بمقدار 50%. ستكون هذه المربّعات ضمن مخطّط مطلق Absolute Layout، وكلّ منها عبارة عن كائن ViewBox. سنعيّن مواقع وأبعاد هذه المربّعات باستخدام الواحدات المستقلة عن الجهاز independent-device units. أنشئ مشروعًا جديدًا من النوع Blank App (Xamarin.Forms Portable) وسمّه SimpleAbsoluteLayout، ثم أبق فقط على المشروعين SimpleAbsoluteLayout (Portable) و SimpleAbsoluteLayout.Droid كما وسبق أن فعلنا في هذا الدرس. بعد ذلك سنضيف صفحة محتوى تعتمد على رماز XAML كما وسبق أن فعلنا في هذا الدرس سنسمّيها SimpleAbsoluteLayoutPage. احرص على أن تكون محتويات هذه الصفحة على الشكل التالي: <?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="SimpleAbsoluteLayout.SimpleAbsoluteLayoutPage"> <AbsoluteLayout x:Name="absLayout"> <BoxView x:Name="boxAccent" Color="Accent" AbsoluteLayout.LayoutBounds="25, 100, 110, 110" Opacity="0.5"/> <BoxView x:Name="boxRed" Color="Red" AbsoluteLayout.LayoutBounds="50, 125, 110, 110" Opacity="0.5"/> <BoxView x:Name="boxGreen" Color="Green" AbsoluteLayout.LayoutBounds="75, 150, 110, 110" Opacity="0.5"/> </AbsoluteLayout> </ContentPage> محتويات هذه الصفحة بسيطة وواضحة. حيث نعمل على إنشاء مخطّط مطلق باستخدام الوسم . لاحظ كيف وضعنا ضمن هذا العنصر ثلاثة عناصر من النوع BoxView. وقد عيّنّا لكل عنصر منها اسمًا لأنّنا سنحتاجه في القسم الثاني من هذا الدرس. الملفت للنظر أنّ هذه العناصر قد تمّ وضعها مباشرةً ضمن العنصر AbsoluteLayout دون الحاجة إلى وجود الوسم وهذا جائز تمامًا. لاحظ بالنسبة إلى عنصر BoxView الأوّل: <BoxView x:Name="boxAccent" Color="Accent" AbsoluteLayout.LayoutBounds="25, 100, 110, 110" Opacity="0.5"/> الخاصيّة التي تتحكّم بموضعه هي AbsoluteLayout.LayoutBounds حيث أسندنا إليها النص: "25, 100, 110, 110" الذي يحتوي على أربعة أرقام تفصل بينها فواصل، وهي من اليسار إلى اليمين: بعد الزاوية اليسرى العليا من العنصر عن الحافة اليسرى للمخطّط المطلق ورمزه X (القيمة 25). بعد الزاوية اليسرى العليا من العنصر عن الحافة العليا للمخطّط المطلق ورمزه Y (القيمة 100). عرض العنصر ورمزه Width (القيمة 110). ارتفاع العنصر ورمزه Height (القيمة 110). انظر الشكل التخطيطي التالي لمزيد من التوضيح: في الحقيقة يمكنك اعتبار الشكل السابق كجملة إحداثية ديكارتيّة، ولكنّ محور التراتيب y-axis موجّه نحو الأسفل كما هو واضح بدلًا من الاتجاه المألوف نحو الأعلى. ينطبق نفس الأمر تمامًا على عنصريّ BoxView الباقيين: <BoxView x:Name="boxRed" Color="Red" AbsoluteLayout.LayoutBounds="50, 125, 110, 110" Opacity="0.5"/> <BoxView x:Name="boxGreen" Color="Green" AbsoluteLayout.LayoutBounds="75, 150, 110, 110" Opacity="0.5"/> وبما أنّ لكل من هذه العناصر الثلاثة نفس القيمة للعرض وللارتفاع، إذًا فهي عبارة عن مربّعات، وهي طبوقة فيما بينها. إذا نظرت إلى الأرقام الخاصّة بالموضع وبالحجم التي اخترتها "للمربّعات" الثلاث، فستلاحظ أنّني اخترتها بعناية كي تكون متداخلة فيما بينها. وقد جعلت الشفافيّة (الخاصية Opacity) لكلّ منها 50% (القيمة 0.5) لكي تعطي ذلك التأثير اللوني الجميل عندما تتداخل مع بعضها. انتقل الآن إلى الملف App.cs واحرص على ان تكون بانية الصنف App على الشكل التالي: public App() { // The root page of your application MainPage = new SimpleAbsoluteLayoutPage(); } نفّذ التطبيق باستخدام F5، لتحصل على شكل شبيه بما يلي: لنحرّك المربّعات! لنعمل على إضفاء القليل من الإثارة على التطبيق السابق. سنعمل على تحريك المربّعات السابقة بحركة رأسيّة تلقائيّة. أي أنّ المربّعات السابقة ستتحرّك نحو الأعلى حتى تصل إلى الحافة العليا للشاشة، ثم تعكس اتجاه الحركة نحو الأسفل حتى تصل إلى الحافة السفلى من الشاشة. ثم تعود من جديد لتعكس جهة الحركة لتصبح نحو الأعلى، وهكذا دواليك. في الحقيقة لن يطرأ أيّ تغيير على رماز XAML، فكل ما سنفعله هو إضافة الشيفرة البرمجيّة المناسبة لملف الشيفرة البرمجيّة الموافق لملف الرماز السابق. انتقل الآن إلى الملف SimpleAbsoluteLayoutPage.xaml.cs واحرص على أن تكون محتوياته على الشكل التالي: 1 using System; 2 using Xamarin.Forms; 3 4 namespace SimpleAbsoluteLayout 5 { 6 public partial class SimpleAbsoluteLayoutPage : ContentPage 7 { 8 private bool isUpDirection; 9 public SimpleAbsoluteLayoutPage() 10 { 11 InitializeComponent(); 12 13 isUpDirection = false; 14 15 Device.StartTimer(TimeSpan.FromMilliseconds(50), UpdatePositions); 16 } 17 18 private bool UpdatePositions() 19 { 20 Rectangle rect; 21 22 rect = AbsoluteLayout.GetLayoutBounds(boxAccent); 23 if (rect.Y - 10 <= 0) 24 { 25 isUpDirection = false; 26 } 27 28 rect = AbsoluteLayout.GetLayoutBounds(boxGreen); 29 if (rect.Y + rect.Height + 10 > this.Height) 30 { 31 isUpDirection = true; 32 } 33 34 BoxView[] boxViews = { boxAccent, boxRed, boxGreen }; 35 36 for (int i = 0; i < boxViews.Length; i++) 37 { 38 rect = AbsoluteLayout.GetLayoutBounds(boxViews[i]); 39 40 if (isUpDirection) 41 { 42 rect = new Rectangle(rect.X, rect.Y - 10, rect.Width, rect.Height); 43 } 44 else 45 { 46 rect = new Rectangle(rect.X, rect.Y + 10, rect.Width, rect.Height); 47 } 48 49 AbsoluteLayout.SetLayoutBounds(boxViews[i], rect); 50 } 51 52 return true; 53 } 54 } 55 } بالإضافة إلى بانية الصنف SimpleAbsoluteLayoutPage أنشأت تابعًا جديدًا أسميته UpdatePositions يمكنك أن تجده في الأسطر من 18 حتى 53. يُعتبر هذا التابع مسؤولًا عن تحريك المربّعات الثلاثة بالحركة الرأسيّة التي تحدثنا عنها قبل قليل. يتمّ استدعاء هذا التابع بشكل تلقائيّ كل 50 ميللي ثانيّة. التقنيّة التي استخدمناها في هذا الاستدعاء هي نفسها الموجودة في هذا الدرس. حيث استخدمنا التابع StartTimer من الصنف Device لهذا الغرض (انظر السطر 15). لاحظ الحقل isUpDirection من النوع bool المصرّح عنه في السطر 8. يُفيد هذا الحقل في الاحتفاظ بالاتجاه الحالي للمربّعات أثناء الحركة (أي هل الحركة نحو الأعلى أم نحو الأسفل). يبدأ التابع UpdatePositions بالتصريح عن المتغيّر rect من النوع Rectangle (السطر 20). و Rectangle هو عبارة عن بنية struct أي هو من نوع قيمة value type. يُفيد هذا النوع في وصف الأشكال المستطيلة عمومًا. فهو يمتلك أربع خصائص تصف حجم وموضع أيّ مستطيل على الشاشة. هذه الخصائص هي: X و Y و Width و Height وهي تصف على الترتيب: مقدار الإزاحة الأفقيّة عن الحد الأيسر، مقدار الإزاحة الرأسيّة عن الحد الأعلى، وعرض المستطيل، وارتفاع المستطيل. وذلك بمنطق مشابه لما ورد ذكره في الشكل الأوّل الذي تعرضنا له في هذا الدرس. نقوم في السطر 22 بالحصول على المستطيل (المربّع في واقع الأمر) الحاضن للعنصر boxAccent وهو المربّع الأوّل. ثمّ نختبر كون الإحداثي Y له قد أوشك أن يصبح سالبًا وذلك في السطر 23. فإن أوشك على ذلك فهذا يعني أنّ المربّع boxAccent قد ارتطم بالحد الأعلى للشاشة ويجب تغيير اتجاه الحركة ليصبح نحو الأسفل بدلًا من الأعلى، وهذا ما نفعله في السطر 25. نفس المنطق السابق نطبّقه على العنصر boxGreen وهو المربّع الثالث، وذلك في السطر 28. حيث نحصل على المستطيل الحاضن للعنصر boxGreen. ثمّ نختبر كون الحافة السفلى له (تأمل التعبير rect.Y + rect.Height + 10) قد أوشكت أن تتجاوز قيمتها ارتفاع الصفحة (وهو يماثل في مثالنا الارتفاع الكامل للمخطّط المطلق) وذلك في السطر 29. فإن أوشكت على ذلك فهذا يعني أنّ المربّع boxGreen سيتجاوز الحد السفلي للشاشة ويجب تغيير اتجاه الحركة ليصبح نحو الأعلى بدلًا من الأسفل، وهذا ما نفعله في السطر 31. بعد ذلك ننشئ المصفوفة boxViews بحيث تحتوي على عناصر المربّعات الثلاثة boxAccent و boxRed و boxGreen وذلك في السطر 34. ثمّ يدخل البرنامج حلقة for (الأسطر من 36 حتى 50) التي تنحصر وظيفتها في تحديث إحداثيات المربّعات الثلاثة بحيث تحرّكها نحو الأعلى أو نحو الأسفل بحسب قيمة الحقل isUpDirection. سيكون مقدار الانتقال الرأسي 10 وحدات في كل مرّة كما هو واضح في السطرين 42 و 46. ويتم إسناد الإحداثيّات الجديدة للمربّعات الثلاثة عن طريق استدعاء التابع الساكن AbsoluteLayout.SetLayoutBounds في السطر 49. حيث يتطلّب هذا التابع وسيطين. الأوّل هو المربّع المراد تطبيق الإحداثيات الجديدة عليه وهو موجود ضمن المصفوفة boxView، والثاني هو قيمة من النوع Rectange تحوي المستطيل (المربّع في مثالنا هذا) الحاضن الجديد. الخلاصة تحدثنا في هذا الدرس عن المخطّط المطلق Absolute Layout وكيفيّة استخدامه من خلال رماز XAML. يفيد هذا المخطّط كما أشرنا في التحكّم بحجم وتموضع أي عنصر مرئي على الشاشة على نحو دقيق. تناولنا تطبيقين بسيطين يوضّحان كيفيّة التحكّم بحجم وموضع كل عنصر من عناصر BoxView، كما استخدمنا التابع StartTimer من الصنف Device لتوليد استدعاءات تلقائيّة ذات أزمنة منتظمة إلى تابع مخصّص كي يتحكّم بمواضع هذه العناصر. سنتناول في الدرس القادم تطبيقًا عمليًّا هو في واقع الأمر تحديث للتطبيق الثاني في هذا الدرس لكي نتحكّم في مواضع العناصر بشكل مخصّص أكثر.
-
- absolute-layout
- xaml
-
(و 1 أكثر)
موسوم في: