اذهب إلى المحتوى

بناء تطبيق عمليّ باستخدام XAML


حسام برهان

نتابع عملنا في سلسلة تعلّم برمجة تطبيقات أندرويد باستخدام Xamarin.Forms. سنتعلّم في هذا الدرس تقنيّات جديدة لبناء واجهات متقدّمة أكثر باستخدام رماز XAML. كما سنتعلّم كيفيّة عرض رسائل تنبيه للمستخدم. يُعتبر هذا الدرس مساعدًا للدرس السابق في فهم كيفية التعامل مع رماز XAML. سنتناول في هذا الدرس تطبيقًا عمليًا مفيدًا، الهدف منه هو حل معادلة من الدرجة الثانيّة Quadratic Equation في مجموعة الأعداد الحقيقية R.

main.png

كيفيّة حل معادلة من الدرجة الثانية

للمعادلة من الدرجة الثانية الشكل العام التالي:

sde-general-form.png

لكي نحل هذه المعادلة في المجموعة R نحتاج إلى معرفة قيم المعاملات a وb وc وهي عبارة عن ثوابت حقيقيّة سنطلب من المستخدم أي يزوّد البرنامج بها. بعد ذلك سنطبّق القانون التالي لحساب مميّز هذه المعادلة:

determinant.png

من خلال قيمة هذا المميّز نكون أمام ثلاث حالات:

  • الحالة الأولى عندما يكون delta-greater-then-zero.png فيوجد عندئذ حلّين مختلفين للمعادلة المفروضة يمكن الحصول عليهما من العلاقتين التاليتين: two-solutions.png
  • الحالة الثانية عندما delta-equals-zero.png فيوجد عندئذ حل مضاعف يُعطى بالعلاقة التالية:one-solution.png
  • الحالة الثالثة عندما delta-smaller-then-zero.pngفلا يكون للمعادلة عندها أي حل في المجموعة R.

بناء تطبيق حل المعادلات من الدرجة الثانية

ابدأ بإنشاء مشروع جديد من النوع Blank App (Xamarin.Forms Portable) وسمّه SDESolverApp، ثم أبق فقط على المشروعين SDESolverApp (Portable) و SDESolverApp.Droid كما وسبق أن فعلنا في هذا الدرس. بعد ذلك سنضيف صفحة محتوى تعتمد على رماز XAML كما وسبق أن فعلنا في الدرس السابق سنسمّها SDESolverPage. سنقسّم الواجهة إلى ثلاثة أقسام. القسم الأوّل العلوي سيحتوي على رسالة ترحيبيّة، أمّا القسم الأوسط فسيضم المنطقة الخاصّة بإدخال قيم المعاملات a وb وc الذين تحدثنا عنهم قبل قليل مع لصيقة توضيحيّة. أمّا القسم الأخير السفلي فسيحوي زرًا عند نقره سيتم حل المعادلة. انظر الشكل التالي لمعرفة الشكل العام لهذا التطبيق:

fig01.png

في البداية أقترح وضع الواجهة السابقة ضمن مخطّط مكدّس يحيط بها بشكل كامل وذلك على الشكل التالي:

 <StackLayout>
    <StackLayout.Children>
	
    </StackLayout.Children>
  </StackLayout>

تمثّل النقاط الثلاث التي تظهر في الرماز السابق، المكان المفترض لرماز الواجهة الذي سنتحدّث عنه بعد قليل، حيث ستقع أي عناصر مرئيّة أخرى ضمن العنصر StackLayout.Children كما هو معلوم.
لنجهّز الرسالة الترحيبيّة التي ستظهر في الأعلى. في الحقيقة تتألّف هذه الرسالة من عنصر إطار Frame يحوي لصيقة تعرض النص الترحيبي. يمكن استخدام الرماز التالي لتحقيق هذا الشكل:


<Frame Padding="5"
        OutlineColor="Accent"
        BackgroundColor="#300000FF"
        VerticalOptions="StartAndExpand">
  <Frame.Content>
    <Label Text="تطبيق حل المعادلات من الدرجة الثانية"
            FontSize="Large"
            TextColor="Accent"
            HorizontalOptions="FillAndExpand"
            HorizontalTextAlignment="Center" />
  </Frame.Content>
</Frame>

بالنسبة لعنصر الإطار Frame فقد ضبطنا الخاصيّة Padding له لإضافة حشوة صغيرة تحيط بمحتوياته. كما ضبطنا خصائص لون الحد الخارجي OutlineColor لتحمل اللون Accent وخاصيّة لون الخلفية BackgroundColor لتكون "#300000FF". القيمة السابقة هي قيمة ستة عشرية تتكوّن من أربعة مكوّنات. كل مكّون يحجز محرفين. هذه المكوّنات من اليسار إلى اليمين: قيمة الشفافيّة alpha وتتراوح بين 00 و FF (استخدمنا في هذا المثال القيمة 30)، ثم يأتي اللون الأحمر فالأخضر فالأزرق. فمن خلال القيمة "#300000FF" سيظهر معنا لون أزرق لكنّه شفّاف بعض الشيء. ضبطنا أيضًا خاصيّة التموضع الرأسي VerticalOptions لتكون StartAndExpand ليظهر الإطار في البداية (في الأعلى في هذا التطبيق). بعد ذلك تأتي الخاصيّة Frame.Content والتي يمكن إسناد عنصر مرئي واحد إليها. أسندنا لصيقة كما هو واضح في الرماز السابق، وضبطنا خصائصها ليكون حجم الخط كبيرًا ولونه Accent، كما جعلنا محاذاة النص ضمن اللصيقة في الوسط. تنحصر مهمّة عنصر الإطار في إكساب ناحية جمالية للصيقة التي يحتويها.

لننتقل الآن إلى القسم الثاني الأوسط من واجهة التطبيق والذي يحتوي كما أشرنا على منطقة مدخلات المستخدم. سنضع هذا القسم بكامله ضمن مخطّط مكدّس. سيحتوي هذا المخطّط على ما يلي:

1- لصيقة ضمن إطار كما فعلنا تمامًا مع الرسالة الترحيبيّة السابقة في القسم العلوي. تحوي هذه اللصيقة نصًّا توضيحيًّا. لننظر إلى الرماز المسؤول عن إظهار اللصيقة التي تحتوي على النص التوضيحي:


<StackLayout VerticalOptions="CenterAndExpand">
  <StackLayout.Children>
      <Frame Padding="5"
              OutlineColor="Green"
              BackgroundColor="#3000FF00">
        <Frame.Content>
          <Label Text="أدخل المعاملات التالية لإيجاد حل المعادلة "
                  FontSize="Medium"
                  TextColor="Lime"
                  HorizontalOptions="FillAndExpand"
                  HorizontalTextAlignment="End" />
        </Frame.Content>
      </Frame>

      ...

  </StackLayout.Children>
</StackLayout>

النقاط الثلاث الموجودة ضمن الرماز السابق هي مكان الرماز الذي سيُظهِر مربّعات النص المسؤولة عن استقبال الدخل من المستخدم، حيث تجنبّت وضعه صراحةً لكي نركّز الآن على الرماز الحالي. لاحظ عنصر الإطار Frame كيف يحتوي ضمن الخاصيّة Content له على اللصيقة التي تعرض العبارة "أدخل المعاملات التالية لإيجاد حل المعادلة". لقد ضبطت الخاصيّة HorizontalTextAlignment لها لتكون End أي ليظهر النص كما لو أنّه محاذًا نحو اليمين.

2- مخطّط مكدّس آخر لعرض مربّعات الإدخال Entry لاستقبال الدخل من المستخدم، وهو يقع أسفل الإطار الذي يحوي اللصيقة التي تعرض العبارة التوضيحيّة (انظر رقم 1):


<StackLayout VerticalOptions="CenterAndExpand">
  <StackLayout.Children>
      ...

      <StackLayout Orientation="Horizontal">
        <StackLayout.Children>
          <Entry x:Name="txtA"
                  HorizontalOptions="StartAndExpand"
                  Placeholder="المعامل a"
                  FontSize="Medium"
                  Keyboard="Telephone" />

          <Entry x:Name="txtB"
                  HorizontalOptions="CenterAndExpand"
                  Placeholder="المعامل b"
                  FontSize="Medium"
                  Keyboard="Telephone" />

          <Entry x:Name="txtC"
                  HorizontalOptions="EndAndExpand"
                  Placeholder="المعامل c"
                  FontSize="Medium"
                  Keyboard="Telephone" />
        </StackLayout.Children> 
      </StackLayout>
  </StackLayout.Children>
</StackLayout>

الأمر الواضح هنا أنّني قد وضعت مربّعات النص Entry لاستقبال الدخل من المستخدم ضمن مخطّط مكدّس آخر، وهذا أمر طبيعي تمامًا في تصميم الواجهات. لقد جعلت خاصيّة الاتجاه Orientation لهذا المكدّس تحمل القيمة Horizontal أي أفقي. كما وضعت ضمن العنصر StackLayout.Children ثلاثة عناصر Entry لاستقبال المعاملات a وb وc على الترتيب. من الملاحظ أنّني استخدمت الخاصيّة x:Name لكلّ من هذه العناصر لكي نستطيع الوصول إلى محتوياتها ضمن ملف الشيفرة البرمجيّة المرافق لملف الرماز كما سنرى بعد قليل. كما استخدمت الخاصيّة Placeholder لكلّ منها لعرض نص توضيحي ضمن عنصر Entry يوضّح وظيفته. الأمر الأخير الملاحظ هنا هو استخدامي للخاصيّة Keyboard للعنصر Entry التي تسمح بتحديد لوحة المفاتيح التي ستظهر للمستخدم عندما يحاول الكتابة ضمن هذا العنصر. توجد عدّة لوحات مفاتيح تدعمها Xamarin.Froms. لقد استخدمت من أجل هذا المثال لوحة المفاتيح Telephone وهي لوحة مفاتيح مخصّصة لإدخال البيانات المتعلّقة بأرقام الهواتف. في الحقيقة توجد لوحة مفاتيح مخصّصة للأرقام اسمها Numeric ولكنّني آثرت لوحة مفاتيح Telephone عليها لأنّها تدعم إدخال إشارة السالب (-) وهذا ما لا توفّره لوحة المفاتيح الرقميّة القياسيّة Numeric.

ننتقل الآن إلى القسم الأخير السفلي من الواجهة، وهو يحتوي على زر بسيط عندما ينقره المستخدم سيعمل البرنامج على حل المعادلة ويعرض النتيجة. انظر إلى الرماز الخاص به:


<Button Text="حل المعادلة"`
        VerticalOptions="EndAndExpand"
        HorizontalOptions="FillAndExpand"
        HeightRequest="64"
        Clicked="btnSolve_Clicked"/>

استخدمت هذه المرّة الخاصيّة HeightRequest التي يمكن من خلالها ضبط ارتفاع العنصر بشكل تقريبي. لقد جعلت ارتفاعه مساويًا لـ 1 سم تقريبًا. توجد خاصيّة أخرى مشابهة لها لضبط عرض أي عنصر بشكل تقريبي وهي WidthRequest. ضبطّت أيضًا معالج الحدث Clicked الذي سيتم تنفيذه عندما ينقر المستخدم على هذا الزر. اسم هذا المعالج btnSolve_Clicked وسيكون في الطبع ضمن ملف الشيفرة البرمجيّة. إليك الآن الرماز الكامل لهذا لواجهة هذا التطبيق مجمّعًا على الشكل التالي:


<?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="SDESolverApp.SDESolverPage"
             Padding="5">
  
  <StackLayout>
    <StackLayout.Children>
      <Frame Padding="5"
             OutlineColor="Accent"
             BackgroundColor="#300000FF"
             VerticalOptions="StartAndExpand">
        <Frame.Content>
          <Label Text="تطبيق حل المعادلات من الدرجة الثانية"
                 FontSize="Large"
                 TextColor="Accent"
                 HorizontalOptions="FillAndExpand"
                 HorizontalTextAlignment="Center" />
        </Frame.Content>
      </Frame>

      <StackLayout VerticalOptions="CenterAndExpand">
        <StackLayout.Children>
            <Frame Padding="5"
                   OutlineColor="Green"
                   BackgroundColor="#3000FF00">
              <Frame.Content>
                <Label Text="أدخل المعاملات التالية لإيجاد حل المعادلة "
                       FontSize="Medium"
                       TextColor="Lime"
                       HorizontalOptions="FillAndExpand"
                       HorizontalTextAlignment="End" />
              </Frame.Content>
            </Frame>

            <StackLayout Orientation="Horizontal">
              <StackLayout.Children>
                <Entry x:Name="txtA"
                       HorizontalOptions="StartAndExpand"
                       Placeholder="المعامل a"
                       FontSize="Medium"
                       Keyboard="Telephone" />

                <Entry x:Name="txtB"
                       HorizontalOptions="CenterAndExpand"
                       Placeholder="المعامل b"
                       FontSize="Medium"
                       Keyboard="Telephone" />

                <Entry x:Name="txtC"
                       HorizontalOptions="EndAndExpand"
                       Placeholder="المعامل c"
                       FontSize="Medium"
                       Keyboard="Telephone" />
              </StackLayout.Children> 
            </StackLayout>
        </StackLayout.Children>
      </StackLayout>
      
      <Button Text="حل المعادلة"
              VerticalOptions="EndAndExpand"
              HorizontalOptions="FillAndExpand"
              HeightRequest="64"
              Clicked="btnSolve_Clicked"/>
    </StackLayout.Children>
  </StackLayout>
</ContentPage>

لننتقل إلى ملف الشيفرة البرمجيّة المرافق لملف الرماز السابق. اسم هذا الملف بالطبع هو SDESolverPage.xaml.cs. احرص على أن تكون محتوياته على الشكل التالي:


1	using System;
2	using Xamarin.Forms;
3	
4	namespace SDESolverApp
5	{
6	    public partial class SDESolverPage : ContentPage
7	    {
8	        public SDESolverPage()
9	        {
10	            InitializeComponent();
11	        }
12	
13	        private void btnSolve_Clicked(object sender, EventArgs e)
14	        {
15	            double a, b, c;
16	
17	            string txt_a = txtA.Text;
18	            string txt_b = txtB.Text;
19	            string txt_c = txtC.Text;
20	
21	            if (double.TryParse(txt_a, out a) &&
22	                double.TryParse(txt_b, out b) &&
23	                double.TryParse(txt_c, out c))
24	            {
25	                string result;
26	
27	                double delta = b * b - 4 * a * c;
28	
29	                if (delta > 0)
30	                {
31	                    //there are two different solutions.
32	                    double x1 = (-b + Math.Sqrt(delta)) / (2 * a);
33	                    double x2 = (-b - Math.Sqrt(delta)) / (2 * a);
34	
35	                    result = string.Format("يوجد حلين مختلفين في المجموعة R\nx1 = {0}\nx2 = {1}",
36	                        x1.ToString("#0.#"),
37	                        x2.ToString("#0.#"));
38	                }
39	                else if (delta == 0)
40	                {
41	                    //there are two equal solutions.
42	                    double x = -b / (2 * a);
43	
44	                    result = string.Format("يوجد حل مضاعف في المجموعة R\nx1 = x2 = {0}",
45	                        x.ToString("#0.#"));
46	                }
47	                else
48	                {
49	                    //there is no solutions in R.
50	                    result = "المعادلة مستحيلة الحل في المجموعة R";
51	                }
52	
53	                DisplayAlert("الحل", result, "موافق");
54	            }
55	            else
56	            {
57	                DisplayAlert("خطأ في المدخلات", "بعض المعاملات المدخلة غير صحيحة", "موافق");
58	            }
59	        }
60	    }
61	}

يحتوي الصنف SDESolverPage بشكل أساسيّ على معالج الحدث btnSolve_Clicked الذي سيُستدعى عندما ينقر المستخدم زر حل المعادلة من الواجهة. يحتوي هذا المعالج على كامل منطق التطبيق. يحصل البرنامج على قيم المعاملات في الأسطر من 17 حتى 19 حيث تكون قيم نصيّة بالطبع، ثمّ يحوّل هذه القيم النصيّة إلى قيم من النوع double وذلك في الأسطر من 21 حتى 23 من خلال استخدام التابع الساكن TryParse من الصنف double. يحاول هذا التابع أن يحوّل القيمة النصيّة المُمرّرة إليه في الوسيط الأوّل، فإن نجح في ذلك فإنّه يُسند القيمة المُحوّلة إلى المتغيّر a الذي نمرّره كوسيط ثانٍ إلى هذا التابع ويُرجع التابع القيمة true. أمّا إذا فشلت عمليّة التحويل فإنّ التابع سيُرجع القيمة false فيختلّ شرط عبارة if، وبالتالي لن يُتابع البرنامج عمله في حل المعادلة ويعرض رسالة بهذا الخصوص إلى المستخدم بسبب عدم صلاحيّة أحد العوامل المُدخلة على الأقل. لاحظ أنّنا قد مرّرنا الوسيط الثاني الذي سيحمل نتيجة التحويل في حال نجاحه باستخدام الكلمة المحجوزة out. التي تسمح بتمرير عنوان المتغيّر إلى التابع وليس قيمته.

يتابع البرنامج عمله في حال نجحت عمليّة تحويل المعاملات إلى double بحساب مميّز المعادلة ومقارنته بالحالات الثلاث المختلفة التي تحدثنا عنها مسبقًا. توجد ملاحظة أخيرة في أنّ التطبيق يعرض النتائج باستخدام صناديق رسائل قياسيّة في أندرويد، وذلك من خلال استخدام التابع DisplayAlert وهو من الصنف Page (السطران 53 و57) أيّ أنّه ينتمي إلى الصنف SDESolverPage من خلال الوراثة. يخضع التابع DisplayAlert لزيادة التحميل، وقد استخدمت الشكل الذي يتطلّب ثلاثة وسائط وهي بالترتيب عنوان الرسالة، والرسالة المراد عرضها، ثم النص المراد عرضه على الزر.

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

الخلاصة

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


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

أفضل التعليقات

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



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...