تطبيق حساب تكرار المحارف باستخدام مخطّط الشبكة


حسام برهان

سنتناول في هذا الدرس من سلسلة تعلّم برمجة تطبيقات أندرويد باستخدام Xamarin.Forms تطبيقًا عمليًّا مفيدًا آخرًا، نتعلّم من خلاله المزيد عن مزايا مخطّط الشبكة Grid Layout واستخداماته العمليّة في التطبيقات التي نطوّرها. سيستخدم هذا التطبيق مخطّطًا تكراريًّا Histogram سيكون مصمّمًا باستخدام مخطّط الشبكة. إذا أردت معرفة المزيد عن مخطّط الشبكة فيمكنك مراجعة هذا الدرس. وإلّا، فيمكنك البدء مباشرةً في هذا الدرس.

main.png

فكرة التطبيق

تتلخّص فكرة هذا التطبيق في أنّ المستخدم سيُدخل نصًّا بأيّ لغة يرغبها. ثم ينقر على زر "احسب" ليعمل التطبيق بعد ذلك على حساب تكرار كلّ محرف من محارف النص المُدخل. بعد الانتهاء من ذلك، سيعمل التطبيق على إظهار مخطّط تكراري Histogram على شكل مخطّط أعمدة bars chart يمكن من خلاله ملاحظة الأحرف الأكثر تكرارًا والأقل تكرارًا من نظرة واحدة.

سيعتمد التطبيق على عنصر رسومي من النوع Editor لاستقبال الدخل النصيّ من المستخدم. وكذلك على عنصر الزر Button للبدء بإيجاد القيم التكراريّة، وأيضًا سيستخدم مخطط شبكة لعرض النتائج التكراريّة. من أجل كل حرف موجود في النص، سيضيف التطبيق عمود bar سيكون عبارة عن عنصر BoxView يكون ارتفاعه متناسبًا مع تكرار الحرف ضمن النص المُدخَل. بعد إظهار مخطّط الأعمدة سيكون من الممكن النقر على أي عمود (عنصر BoxView) ليعرض التطبيق رسالة للمستخدم تحوي الحرف الذي يمثّله هذا العمود مع عدد مرًّات تكراره ضمن النص المُدخَل. أي بشكل مشابه لما يلي:

fig01.png

سنتناول في الفقرة التالية كامل الشيفرة البرمجيّة لهذا التطبيق.

الشيفرة البرمجيّة للتطبيق

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

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="GridBarChart.GridBarChartPage">
5	  <StackLayout>
6	    <StackLayout VerticalOptions="Start">
7	      <Label Text="تطبيق حساب تكرار المحارف" 
8	             HorizontalOptions="CenterAndExpand"  
9	             HorizontalTextAlignment="Center"
10	             FontSize="Large" 
11	             TextColor="Green"/>
12	      <Editor x:Name="txtInput" />
13	      <Button x:Name="btnCalculate" 
14	              Text="احسب" 
15	              HorizontalOptions="FillAndExpand"
16	              Clicked="btnCalculate_Clicked"  />
17	    </StackLayout>
18	    
19	    <Grid  x:Name="grdLettersHistogram"
20	           VerticalOptions="FillAndExpand"
21	           BackgroundColor="#EBF2F5"
22	           Padding="5,5,5,0">
23	    </Grid>
24	  </StackLayout>
25	</ContentPage>

من الملاحظ أنّ رماز XAML لهذا التطبيق صغير نسبيًّا. يبدأ الرماز بتعريف مخطّط مكدّس StackLayout سيعمل على احتواء كامل واجهة التطبيق. سيحتوي مخطّط المكدّس هذا على عنصرين آخرين:

  • العنصر الأوّل عبارة عن مخطّط مكدّس آخر (الأسطر من 6 حتى 17) يحتوي على لصيقة تعريفيّة بالتطبيق، وعلى عنصر الإدخال النصيّ Editor الذي أسميته txtInput وهو يسمح بكتابة أكثر من سطر نصيّ واحد ضمنه، بالإضافة إلى زر البدء بالحساب الذي أسميته btnCalculate والذي سيؤدّي نقره إلى الانتقال إلى معالج حدث النقر btnCalculate_Clicked. المعالج السابق سيكون بالتأكيد ضمن ملف الشيفرة البرمجيّة الذي سنطّلع عليه بعد قليل.
  • العنصر الثاني هو مخطّط الشبكة (الأسطر من 19 حتى 23) والذي أسميته grdLettersHistogram. لاحظ أنّ هذا المخطّط لا يحوي حاليًّا أيّة عناصر. سنضيف لاحقًا إلى هذا المخطّط عناصر BoxView يمثّل كل منها محرفًا موجودًا ضمن النص المُدخَل، وذلك عن طريق الشيفرة البرمجيّة كما سنرى بعد قليل. انتقل الآن إلى الملف GridBarChartPage.xaml.cs وهو ملف الشيفرة البرمجيّة التابع لملف رماز XAML السابق. احرص على أن تكون محتوياته على الشكل التالي:
1	using System;
2	using System.Collections.Generic;
3	using Xamarin.Forms;
4	
5	namespace GridBarChart
6	{
7	    public partial class GridBarChartPage : ContentPage
8	    {
9	        public GridBarChartPage()
10	        {
11	            InitializeComponent();
12	        }
13	
14	        private void btnCalculate_Clicked(object sender, EventArgs e)
15	        {
16	            Dictionary<char, int> freq = new Dictionary<char, int>();
17	            List<View> bars = new List<View>();
18	            TapGestureRecognizer tapGesture = new TapGestureRecognizer();
19	            int maxFreq = -1;
20	
21	            tapGesture.Tapped += TapGesture_Tapped;
22	
23	            string s = txtInput.Text;
24	
25	            foreach (char t in s)
26	            {
27	                if (char.IsLetterOrDigit(t))
28	                {
29	                    if (!freq.ContainsKey(t))
30	                    {
31	                        freq.Add(t, 1);
32	                    }
33	                    else
34	                    {
35	                        freq[t] = ++freq[t];
36	                    }
37	                }
38	            }
39	
40	            grdLettersHistogram.Children.Clear();
41	
42	            //find the max letter frequency.
43	            foreach(var v in freq.Values)
44	            {
45	                if(maxFreq< v)
46	                {
47	                    maxFreq = v;
48	                }
49	            }
50	
51	            //calculate measurment unit will apply on bars.
52	            double measureUnit = grdLettersHistogram.Height / maxFreq;
53	
54	            foreach (var c in freq.Keys)
55	            {
56	                var boxView = new BoxView
57	                {
58	                    HeightRequest = measureUnit * freq[c],
59	                    BackgroundColor = Color.Accent,
60	                    VerticalOptions = LayoutOptions.End,
61	                    StyleId = string.Format("المحرف '{0}' له التكرار {1}", c, freq[c]),
62	                };
63	
64	                boxView.GestureRecognizers.Add(tapGesture);
65	
66	                bars.Add(boxView);   
67	            }
68	
69	            grdLettersHistogram.Children.AddHorizontal(bars);
70	        }
71	
72	        private void TapGesture_Tapped(object sender, EventArgs e)
73	        {
74	            DisplayAlert("رسالة", ((BoxView)sender).StyleId, "اغلاق");
75	        }
76	    }
77	}

تكمن معظم الشيفرة البرمجيّة في هذا الملف ضمن معالج حدث نقر زر حساب التكرارات btnCalculate_Clicked الذي يمتد من السطر 14 حتى السطر 70. في البداية تمّ تعريف المتغيّر freq في السطر 16 ليكون من النوع العمومي Dictionary، وهو عبارة عن بنية معطيات مشهورة تُعرف بالقاموس dictionary. سيحتوي هذا المتغيّر على جميع الأحرف المختلفة الموجودة ضمن النص المُدخَل بالإضافة إلى عدد مرّات التكرار لكلّ حرف. أمّا في السطر 17 فقد تمّ تعريف المتغيّر bars وهو من نوع القائمة List. سيحتوي هذا المتغيّر على جميع عناصر BoxView التي سننشئها لاحقًا من الشيفرة البرمجيّة. بعد ذلك نعرّف المتغيّر tapGesture وهو من النوع TapGestureRecognizer. سنتمكّن من خلال هذا المتغيّر أن نُكسِب لأي عنصر BoxView إمكانيّة أن ينقر عليه المستخدم ليعرض عند ذلك الرسالة التي تحدثنا عنها مسبقًا. أمّا في السطر 19 فقد عرّفنا المتغيّر maxFreq الذي سيحوي التكرار الأكبر من بين الحروف المختلفة.,>

Quote

بنية معطيات القاموس dictionary بسيطة من حيث المبدأ. حيث يتطلّب كل عنصر ضمن القاموس وجود مفتاح key وقيمة value لهذا المفتاح. جميع العناصر الموجودة ضمن القاموس الواحد يجب أن تكون مفاتيحها مختلفة عن بعضها. أمّا قيم تلك المفاتيح فلا مشكلة في أن تتماثل. بالنسبة لتطبيقنا هذا، اخترت النوع العمومي Dictionary وهذا يعني أنّ أيّ عنصر موجود في هذا القاموس سيكون مفتاحه من النوع char (المحرف الموجود في النص المُدخَل)، وقيمة هذا المفتاح ستكون من النوع int (قيمة تمثّل تكرار هذا المحرف ضمن النص).,>

,>

Quote

يمكن إكساب أيّ عنصر رسومي يرث من لصنف View إمكانيّة الاستجابة لحدث النقر عليه Tap من قِبَل المستخدم. يُعرّف الصنف View الخاصيّة GestureRecognizers وهي مجموعة عناصرها عبارة عن كائنات من الصنف GestureRecognizer. الآن ولكي يستطيع العنصر الرسومي الاستجابة لحدث النقر ينبغي إضافة كائن واحد على الأقل للخاصيّة GestureRecognizers والذي سيكون من الصنف TapGestureRecognizer وهو يرث بطبيعة الحال من الصنف GestureRecognizer. يمكن من خلال هذا الكائن ربط معالج حدث للنقر يتمّ استدعائه عندما ينقر المستخدم على ذلك العنصر الرسومي.

 

نعمل في السطر 21 على إضافة معالج الحدث TapGesture_Tapped (مصرّح عنه في الأسطر من 72 حتى 75) لحدث النقر Tapped للكائن tapGesture. بعد أن نحصل على دخل المستخدم ونضعه ضمن المتغيّر s في السطر 23، سندخل حلقة foreach التكراريّة بحيث نقرأ محتويات النص s محرفًا تلو الآخر، ونختبر كون هذا المحرف هو حرف أو رقم في السطر 27 لأنّنا لا نريد قبول الفراغات على سبيل المثال. في السطر 29 سنختبر في حال كان هذا المحرف موجود فعلًا ضمن القاموس freq من قبل أم لا وذلك عن طريق التابع ContainsKey. فإن كان غير موجود من قبل، سنعمل على إضافته في السطر 31، أمّا إذا كان موجود من قبل فسنعمل على إضافة التكرار الخاص به بمقدار واحد وذلك في السطر 35:


freq[t] = ++freq[t];

لاحظ أنّ التعبير freq[t] يسمح لنا بالوصول إلى قيمة المفتاح t الموجودة في القاموس freq. بعد ذلك سنمسح محتويات مخطّط الشبكة في السطر 40، ثم سننتقل إلى حلقة foreach أخرى (الأسطر من 43 حتى 49) تسمح لنا بحساب التكرار الأكبر من بين المحارف الموجودة ضمن القاموس freq. في السطر 52 سنقوم بإجراء عمليّة حسابيّة بسيطة، حيث سنعمل على حساب واحدة القياس الخاصّة بالأعمدة (عناصر BoxView)، وبشكل أدق، واحدة القياس الخاصّة بارتفاع كل عنصر BoxView. وذلك من خلال تقسيم ارتفاع مخطّط الشبكة grdLettersHistogram على التكرار الأكبر maxFreq الذي حسبناه قبل قليل. سنخزّن واحدة القياس هذه ضمن المتغيّر measureUnit كما هو واضح.

سندخل أخيرًا ضمن حلقة foreach أخرى (الأسطر من 54 حتى 67) وظيفتها إنشاء عناصر BoxView كلّ منها يمثّل محرف مختلف موجود ضمن القاموس freq، وبارتفاع يتناسب مع تكرار هذا المحرف ضمن النص المُدخل. ستعمل هذه الحلقة على إضافة كل عنصر BoxView إلى القائمة bars (السطر 66). تجدر الملاحظة أنّنا في السطر 61 قد أسندنا نصًّا توضيحيًّا إلى الخاصيّة StyleId لعنصر BoxView. يحتوي هذا النص على الحرف وعلى تكراره ضمن النص المُدخَل. في الواقع يمكننا دومًا استخدام الخاصيّة StyleId مع أيّ عنصر يرث من الصنف View لمثل هذه الأغراض.

في النهاية سنعمل على إضافة الأعمدة (عناصر BoxView) الموجودة ضمن القائمة bars دفعةً واحدةً إلى مخطّط الشبكة من خلال العبارة التالي الموجودة في السطر 69:


grdLettersHistogram.Children.AddHorizontal(bars);

يعمل التابع AddHorizontal من الخاصّية Children لمخطّط الشبكة على إضافة عنصر أو مجموعة من العناصر (كما في مثالنا) إلى مخطّط الشبكة على أن تكون على نفس السطر (أفقيًّا horizontal) وذلك دفعةً واحدةً. يوجد في الواقع تابع آخر مماثل لهذا التابع وهو التابع AddVertical، ولكنّه يضيف العناصر على نفس العمود. من الممكن تحديد السطر أو العمود المراد الإضافة إليه عن طريق توابع ساكنة static مثل Grid.SetRow و Grid.SetColumn ولكنّنا لن نحتاج إلى مثل هذه التوابع لأنّنا سنترك الإعدادات الافتراضيّة كما هي. بقي أن نتحدّث عن معالج حدث النقر TapGesture_Tapped. في الحقيقة الشيء الوحيد الذي يفعله هذا الحدث هو إظهار رسالة تنبيه للمستخدم (السطر 74) عند نقره لأحد الأعمدة ضمن المخطّط (عنصر BoxView) تحتوي هذه الرسالة على النص الذي مرّرناه مسبقًا لعنصر BoxView ضمن الخاصيّة StyleId له.
انتقل الآن إلى الملف App.cs واحرص على أن تكون بانية الصنف App على الشكل التالي:

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

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

الخلاصة

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





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


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



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

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

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


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

تسجيل الدخول

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


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