Abdulrahman Hasan Agha

Members
  • عدد منشوراتي

    6
  • تاريخ الإنضمام

  • تاريخ اخر زياره

السمعه بالموقع

2 Neutral

عن العضو Abdulrahman Hasan Agha

  • الرتبه
    مبدع جديد
  • تاريخ الميلاد 07/11/96

معلومات عامة

  • الجنس
    ذكر
  • السكن
    Saudi Arabia
  1. بسم الله الرحمن الرحيم في إستمرارنا في الحديث عن أهم وأحدث مكتبات منصة الأندرويد سنتحدث اليوم عن واحدة من أشهر المكاتب المستخدمة مؤخراً في بعض التطبيقات المشهورة ومن ضمنها تطبيق Jodel الشهير. هذه المكتبة هي مكتبة Crouton. لنتعرف معاً على هذه المكتبة وما أهميتها وكيف تعمل. مكتبة Crouton هي مكتبة تتيح لك تنبيه المستخدم وإظهار بعض الإشعارات. تشبه في عملها مكتبة Toast الشهيرة ولكنها تختلف عنها بأنها تحل بعض المشكلات المتعلقة بـ Toast. واحدة من أهم مشاكل الـ Toast هي مشكلة out of context وهي بأن Toast تعمل وتظهر بغض النظر عن الـ context أو المضمون. قد تظهر في سياق مختلف تماماً عن المتوقع أي أنه عند الإنتقال لـ Activity أخرى سيستمر إشعار الـ Toast بالظهور كما أنها غير قابلة للتعديل وموحدة الشكل. كل هذه المشاكل من الممكن حلها بمكاتب مختلفة ولكن من أسهل هذه المكاتب هي Crouton. ولكن السؤال .. كيف تعمل ؟ مكتبة Crouton تتيح لك التحكم الكامل بشكل الإشعارات ولونها وخصائصها بما تتناسب مع تطبيقك. تعطيك كبداية 3 أشكال ثابتة إذا أردت الإبقاء على أشكال Crouton دون أي تغيير. هذه الأشكال هي : 1- Alert Notification : باللون الأحمر والخط الأبيض ولمدة 3 ثواني تقريباً 2- Info Notification : باللون الأزرق والخط الأبيض ولمدة 3 ثواني تقريباً 3- Confirm Notification : باللون الأخضر والخط الأبيض ولمدة 3 ثواني تقريباً لنبدأ التطبيق ونرى كيفية إظهار هذه الأشكال. قبل بداية التطبيق وكما جرت العادة سنحتاج إلى إضافة Dependency لملف build.gradle ولكن هذه المرة سنحتاج لتعديل ملفي الـ gradle. بالنسبة لملف build.gradle "project" سنضيف mavenCentral() داخل Block الـ repositories الموجود في buildscript بالنسبة لملف build.gradle "module" سنضيف compile 'de.keyboardsurfer.android.widget:crouton:[email protected]' نقوم بعمل Sync من الأعلى حتى تضاف المكتبة ونستطيع بعدها البدء في العمل. نقوم بإنشاء Layout بسيطة توضح عمل هذه المكتبة. تحتوي هذه الـ Layout على 3 أزرار خاصة بكل style وأيضاً على زر Toast وزر آخر لتوضيح الـ Custom Notification. <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:paddingTop="50dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <Button android:id="@+id/info" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="INFO" /> <Button android:id="@+id/alert" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="Alert" /> <Button android:id="@+id/succ" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="Success" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <Button android:id="@+id/toast" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="Toast" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <Button android:id="@+id/custom" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="Custom" /> </LinearLayout> </LinearLayout> صورة توضح الشكل النهائي للـ Layout نذهب الآن للجزء الأهم وهو ملف الـ Java. ملاحظة : سنقوم بتطبيق مفاهيم الـ ButterKnife. لمزيد من المعلومات نرجو زيارة الموضوع التالي: دليلك لأفضل مكاتب الأندرويد - الجزء الثاني - ButterKnife أولاً : سنقوم في البداية بتعريف المتغيرات والـ Buttons @BindView(R.id.alert) Button alert; @BindView(R.id.info) Button info; @BindView(R.id.succ) Button succ; @BindView(R.id.toast) Button toast; @BindView(R.id.custom) Button custom; ثانياً : سنقوم بعمل Bind للمتغيرات داخل onCreate method ButterKnife.bind(this); ثالثاً : سنقوم بإختيار كل زر للقيام بوظيفة معينة. نلاحظ عندما نريد إظهار Crouton أو إشعار لا نحتاج لتعريف أي متغيرات فهي تعمل بنفس طريقة عمل Toast. لإظهار الإشعارات بالأشكال السابقة التي سبق وتحدثنا عنها سنحتاج لتمرير 3 params فقط وهي الـ Context والنص والشكل سواء كان alert – info – confirm والطريقة تبدو مشابهة تماماً لطريقة الـ Toast. @OnClick(R.id.info) void clicked1() { Crouton.showText(this, "INFO 3alamPro", Style.INFO); } @OnClick(R.id.alert) void clicked2() { Crouton.showText(this, "ALERT 3alamPro", Style.ALERT); } @OnClick(R.id.succ) void clicked3() { Crouton.showText(this, "SUCCESS 3alamPro", Style.CONFIRM); } @OnClick(R.id.toast) void clicked4() { Toast.makeText(getApplicationContext(), "3alamPro", Toast.LENGTH_LONG).show(); } رابعاً : نقوم بإختبار التطبيق ونرى الفرق بين Crouton وبين Toast. وكيف أن Toast تستمر بالظهور حتى عند الخروج من التطبيق ولكن Crouton تختفي وهو المطلوب. ماذا لو أردنا البقاء على هذه الأشكال ولكن نريد تغيير الوقت المحدد مسبقاً. بكل بساطة سنحتاج لعمل Object من كلاس Configuration الموجود مسبقاً داخل مكتبة Crouton. Configuration croutonConfiguration; ومن ثم نعرف الـ Object داخل onCreate method ونعطيه الوقت المطلوب ( الوقت المدخل يقاس بالـ milliseconds والثانية الواحدة تساوي 1000 ميلي ثانية ). croutonConfiguration = new Configuration.Builder().setDuration(1000).build(); // 1 sec الآن سنقوم بتغيير الوقت لواحدة من الإشعارات المعرفة مسبقاً ولكننا سنحتاج لتمرير باراميتر إضافي وهو getTaskId() سنتكلم عليه فيما بعد ولكن في الوقت الراهن قد لا يهمنا كثيراً. بهذا التعديل سيتغير الوقت ليصبح ثانية واحدة فقط بدلاً من 3 ثواني. @OnClick(R.id.info) void clicked1() { Crouton.showText(this, "INFO 3alamPro", Style.INFO,getTaskId(),croutonConfiguration); } نقوم بالتجربة والمقارنة بين INFO وبين ALERT ونستطيع رؤية الفرق في التوقيت. بعد الإنتهاء من أول قسم سنبدأ في تصميم الـ Notification الخاصة بنا. وطبعاً هذا الأمر متاح بكل سهولة مع Crouton. سنقوم في البداية بتغيير لون الخط ولون خلفية الإشعار فقط. نلاحظ بأننا كنا نمرر باراميتر من نوع Style داخل crouton. الباراميتر Style.INFO عبارة عن ستايل جاهز ولتغييره سنحتاج لعمل Style خاص بنا. لذلك سنحتاج إلى عمل Object من كلاس style وإضافة جميع الخصائص بشكل يدوي. Style style; ونقوم بتعريف الـ Object داخل onCreate method. style = new Style.Builder() .setBackgroundColorValue(Color.parseColor("#000000")) // black .setGravity(Gravity.CENTER_HORIZONTAL) .setConfiguration(croutonConfiguration) .setHeight(100) .setTextColorValue(Color.parseColor("#ffffff")).build(); // white للشرح بشكل أعمق للخصائص : setBackgroundColorValue(Color.parseColor()) نستخدمها لإختيار لون خلفية الإشعار. setGravity نستخدمها لإختيار مكان النص سواء كان في المنتصف أو على اليمين أو على اليسار. setConfiguration نستخدمها لإختيار المدة الزمنية لإظهار الإشعار. (يجب إنشاء Object منفصل كالذي تم إنشاؤه في الخطوات السابقة ) setHeight نحدد من خلالها إرتفاع الإشعار. setTextColorValue نحدد من خلالها لون خط النص داخل الإشعار. ومن ثم ننهي التعريف بـ .build نقوم الآن بتعديل واحدة من الإشعارات المعرفة سابقاً وإختيار الـ style الخاص بنا بدلاً من المعرف مسبقاً. @OnClick(R.id.alert) void clicked2() { Crouton.showText(this, "ALERT 3alamPro", style); } ونرى النتيجة. تعمل بشكل مثالي. سنقوم الان بعمل إشعار مختلف تماماً يتضمن صور ونص وليس نص فقط. لتطبيق هذا الأمر سنحتاج إلى إنشاء Layout جديدة. سنقوم داخل هذه الـ Layout بتصميم شكل الإشعار. نقوم بإنشاء Layout جديدة ونسميها crouton_custom على سبيل المثال. نقوم بتصميم Layout بسيطة تتضمن شعار الموقع فقط. نلاحظ بأن إرتفاع الـ Layout يمثل إرتفاع الإشعار نفسه لذلك إختيار الإرتفاع مهم جداً. <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <RelativeLayout android:layout_width="match_parent" android:layout_height="50dp" android:background="#000000"> <ImageView android:layout_width="100dp" android:layout_height="30dp" android:layout_centerInParent="true" android:src="@drawable/pro"/> </RelativeLayout> </RelativeLayout> الشكل النهائي للـ Layout. للاستمرار بذلك يجب علينا تعريف Object جديد من نوع View وعمل Inflate للـ Layout الخاصة بالإشعار. ومن ثم عمل Crouton جديد. @OnClick(R.id.custom) void clicked5() { View customView = getLayoutInflater().inflate(R.layout.crouton_custom, null); Crouton.show(this,customView); } والنتيجة .. إلى هنا نكون قد وصلنا إلى نهاية الشرح الخاص بالمكتبة. وسنقوم في المواضيع القادمة بإستعراض المزيد من المكتبات الخاصة بمنصة الأندرويد بإذن الله.
  2. بسم الله الرحمن الرحيم في إستمرارنا في الحديث عن أهم وأحدث مكتبات منصة الأندرويد سنتحدث اليوم عن واحدة من أشهر المكاتب المستخدمة مؤخراً في بعض التطبيقات المشهورة ومن ضمنها تطبيق Jodel الشهير. هذه المكتبة هي مكتبة Crouton. لنتعرف معاً على هذه المكتبة وما أهميتها وكيف تعمل. مكتبة Crouton هي مكتبة تتيح لك تنبيه المستخدم وإظهار بعض الإشعارات. تشبه في عملها مكتبة Toast الشهيرة ولكنها تختلف عنها بأنها تحل بعض المشكلات المتعلقة بـ Toast. واحدة من أهم مشاكل الـ Toast هي مشكلة out of context وهي بأن Toast تعمل وتظهر بغض النظر عن الـ context أو المضمون. قد تظهر في سياق مختلف تماماً عن المتوقع أي أنه عند الإنتقال لـ Activity أخرى سيستمر إشعار الـ Toast بالظهور كما أنها غير قابلة للتعديل وموحدة الشكل. كل هذه المشاكل من الممكن حلها بمكاتب مختلفة ولكن من أسهل هذه المكاتب هي Crouton. ولكن السؤال .. كيف تعمل ؟ مكتبة Crouton تتيح لك التحكم الكامل بشكل الإشعارات ولونها وخصائصها بما تتناسب مع تطبيقك. تعطيك كبداية 3 أشكال ثابتة إذا أردت الإبقاء على أشكال Crouton دون أي تغيير. هذه الأشكال هي : 1- Alert Notification : باللون الأحمر والخط الأبيض ولمدة 3 ثواني تقريباً 2- Info Notification : باللون الأزرق والخط الأبيض ولمدة 3 ثواني تقريباً 3- Confirm Notification : باللون الأخضر والخط الأبيض ولمدة 3 ثواني تقريباً لنبدأ التطبيق ونرى كيفية إظهار هذه الأشكال. قبل بداية التطبيق وكما جرت العادة سنحتاج إلى إضافة Dependency لملف build.gradle ولكن هذه المرة سنحتاج لتعديل ملفي الـ gradle. بالنسبة لملف build.gradle "project" سنضيف mavenCentral() داخل Block الـ repositories الموجود في buildscript بالنسبة لملف build.gradle "module" سنضيف compile 'de.keyboardsurfer.android.widget:crouton:[email protected]' نقوم بعمل Sync من الأعلى حتى تضاف المكتبة ونستطيع بعدها البدء في العمل. نقوم بإنشاء Layout بسيطة توضح عمل هذه المكتبة. تحتوي هذه الـ Layout على 3 أزرار خاصة بكل style وأيضاً على زر Toast وزر آخر لتوضيح الـ Custom Notification. <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:paddingTop="50dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <Button android:id="@+id/info" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="INFO" /> <Button android:id="@+id/alert" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="Alert" /> <Button android:id="@+id/succ" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="Success" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <Button android:id="@+id/toast" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="Toast" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <Button android:id="@+id/custom" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="Custom" /> </LinearLayout> </LinearLayout> صورة توضح الشكل النهائي للـ Layout نذهب الآن للجزء الأهم وهو ملف الـ Java. ملاحظة : سنقوم بتطبيق مفاهيم الـ ButterKnife. لمزيد من المعلومات نرجو زيارة الموضوع التالي: دليلك لأفضل مكاتب الأندرويد - الجزء الثاني - ButterKnife أولاً : سنقوم في البداية بتعريف المتغيرات والـ Buttons @BindView(R.id.alert) Button alert; @BindView(R.id.info) Button info; @BindView(R.id.succ) Button succ; @BindView(R.id.toast) Button toast; @BindView(R.id.custom) Button custom; ثانياً : سنقوم بعمل Bind للمتغيرات داخل onCreate method ButterKnife.bind(this); ثالثاً : سنقوم بإختيار كل زر للقيام بوظيفة معينة. نلاحظ عندما نريد إظهار Crouton أو إشعار لا نحتاج لتعريف أي متغيرات فهي تعمل بنفس طريقة عمل Toast. لإظهار الإشعارات بالأشكال السابقة التي سبق وتحدثنا عنها سنحتاج لتمرير 3 params فقط وهي الـ Context والنص والشكل سواء كان alert – info – confirm والطريقة تبدو مشابهة تماماً لطريقة الـ Toast. @OnClick(R.id.info) void clicked1() { Crouton.showText(this, "INFO 3alamPro", Style.INFO); } @OnClick(R.id.alert) void clicked2() { Crouton.showText(this, "ALERT 3alamPro", Style.ALERT); } @OnClick(R.id.succ) void clicked3() { Crouton.showText(this, "SUCCESS 3alamPro", Style.CONFIRM); } @OnClick(R.id.toast) void clicked4() { Toast.makeText(getApplicationContext(), "3alamPro", Toast.LENGTH_LONG).show(); } رابعاً : نقوم بإختبار التطبيق ونرى الفرق بين Crouton وبين Toast. وكيف أن Toast تستمر بالظهور حتى عند الخروج من التطبيق ولكن Crouton تختفي وهو المطلوب. ماذا لو أردنا البقاء على هذه الأشكال ولكن نريد تغيير الوقت المحدد مسبقاً. بكل بساطة سنحتاج لعمل Object من كلاس Configuration الموجود مسبقاً داخل مكتبة Crouton. Configuration croutonConfiguration; ومن ثم نعرف الـ Object داخل onCreate method ونعطيه الوقت المطلوب ( الوقت المدخل يقاس بالـ milliseconds والثانية الواحدة تساوي 1000 ميلي ثانية ). croutonConfiguration = new Configuration.Builder().setDuration(1000).build(); // 1 sec الآن سنقوم بتغيير الوقت لواحدة من الإشعارات المعرفة مسبقاً ولكننا سنحتاج لتمرير باراميتر إضافي وهو getTaskId() سنتكلم عليه فيما بعد ولكن في الوقت الراهن قد لا يهمنا كثيراً. بهذا التعديل سيتغير الوقت ليصبح ثانية واحدة فقط بدلاً من 3 ثواني. @OnClick(R.id.info) void clicked1() { Crouton.showText(this, "INFO 3alamPro", Style.INFO,getTaskId(),croutonConfiguration); } نقوم بالتجربة والمقارنة بين INFO وبين ALERT ونستطيع رؤية الفرق في التوقيت. بعد الإنتهاء من أول قسم سنبدأ في تصميم الـ Notification الخاصة بنا. وطبعاً هذا الأمر متاح بكل سهولة مع Crouton. سنقوم في البداية بتغيير لون الخط ولون خلفية الإشعار فقط. نلاحظ بأننا كنا نمرر باراميتر من نوع Style داخل crouton. الباراميتر Style.INFO عبارة عن ستايل جاهز ولتغييره سنحتاج لعمل Style خاص بنا. لذلك سنحتاج إلى عمل Object من كلاس style وإضافة جميع الخصائص بشكل يدوي. Style style; ونقوم بتعريف الـ Object داخل onCreate method. style = new Style.Builder() .setBackgroundColorValue(Color.parseColor("#000000")) // black .setGravity(Gravity.CENTER_HORIZONTAL) .setConfiguration(croutonConfiguration) .setHeight(100) .setTextColorValue(Color.parseColor("#ffffff")).build(); // white للشرح بشكل أعمق للخصائص : setBackgroundColorValue(Color.parseColor()) نستخدمها لإختيار لون خلفية الإشعار. setGravity نستخدمها لإختيار مكان النص سواء كان في المنتصف أو على اليمين أو على اليسار. setConfiguration نستخدمها لإختيار المدة الزمنية لإظهار الإشعار. (يجب إنشاء Object منفصل كالذي تم إنشاؤه في الخطوات السابقة ) setHeight نحدد من خلالها إرتفاع الإشعار. setTextColorValue نحدد من خلالها لون خط النص داخل الإشعار. ومن ثم ننهي التعريف بـ .build نقوم الآن بتعديل واحدة من الإشعارات المعرفة سابقاً وإختيار الـ style الخاص بنا بدلاً من المعرف مسبقاً. @OnClick(R.id.alert) void clicked2() { Crouton.showText(this, "ALERT 3alamPro", style); } ونرى النتيجة. تعمل بشكل مثالي. سنقوم الان بعمل إشعار مختلف تماماً يتضمن صور ونص وليس نص فقط. لتطبيق هذا الأمر سنحتاج إلى إنشاء Layout جديدة. سنقوم داخل هذه الـ Layout بتصميم شكل الإشعار. نقوم بإنشاء Layout جديدة ونسميها crouton_custom على سبيل المثال. نقوم بتصميم Layout بسيطة تتضمن شعار الموقع فقط. نلاحظ بأن إرتفاع الـ Layout يمثل إرتفاع الإشعار نفسه لذلك إختيار الإرتفاع مهم جداً. <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <RelativeLayout android:layout_width="match_parent" android:layout_height="50dp" android:background="#000000"> <ImageView android:layout_width="100dp" android:layout_height="30dp" android:layout_centerInParent="true" android:src="@drawable/pro"/> </RelativeLayout> </RelativeLayout> الشكل النهائي للـ Layout. للاستمرار بذلك يجب علينا تعريف Object جديد من نوع View وعمل Inflate للـ Layout الخاصة بالإشعار. ومن ثم عمل Crouton جديد. @OnClick(R.id.custom) void clicked5() { View customView = getLayoutInflater().inflate(R.layout.crouton_custom, null); Crouton.show(this,customView); } والنتيجة .. إلى هنا نكون قد وصلنا إلى نهاية الشرح الخاص بالمكتبة. وسنقوم في المواضيع القادمة بإستعراض المزيد من المكتبات الخاصة بمنصة الأندرويد بإذن الله.
  3. بسم الله الرحمن الرحيم إستكمالاً لسلسلة مكتبات الأندرويد سنستعرض اليوم مكتبة من أهم المكتبات التي يستخدمها المطورون بشكل أساسي في تطوير التطبيقات على منصة الأندرويد. هذه المكتبة هي مكتبة Butter Knife. ولكن السؤال الأهم هو ما فائدة هذه المكتبة ؟ من المعروف لدى المطورين بأن طريقة ربط الـ View component مع الأوبجكت داخل كود الجافا هو باستخدام الأمر find view by id. ومن ثم ربط الأوبجكت عن طريق R.id.component. هذه الطريقة يوماً بعد يوم تثبت عدم فعاليتها وخصوصاً عند وجود الكثير من الـ components داخل الـ layout التي ستضطر لربطها بشكل يدوي. كما أن الربط العادي ستضطر في البداية إلى إنشاء المتغير في بداية الكلاس أما تعريفه فيجب أن يتم داخل onCreate method. هذه الطريقة باتت تثبت عدم فعاليتها يوماً بعد يوم. ButterKnife هي مكتبة تستطيع من خلالها ربط الأوبجكت مع الـ view component بشكل سريع وسلس وبشكل مباشر. كما تمكنك المكتبة من ربط الـ drawables والـ strings وغيرها وليست مقتصرة فقط على الـ views. كيف تعمل هذه المكتبة ؟ لنبدأ ... أولاً : وكما جرت العادة يجب إضافة هذه المكتبة داخل المشروع كـ dependency إضافية داخل ملف الـ gradle.build. من المهم أيضاً إضافة الـ annotationProcessor كي تعمل المكتبة بشكل صحيح. ومن ثم عمل sync لكي تتم الإضافة. compile 'com.jakewharton:butterknife:8.8.1' annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' ثانياً: بعد عمل sync يمكننا الآن البدء في تصميم الـ layout .. طبعاً سأقوم بتصميم layout بسيطة فقط لتوضيح عمل هذه المكتبة. من الممكن ملاحظة بأن ملف الـ xml لم يتغير بتاتاً ولم نقم بإضافة أي كود إضافي وهذه ميزة إضافية أيضاً لأن هناك الكثير من المكتبات الأخرى التي توفر ميزة الربط ستضطر حين إستخدامها إلى إضافة تعديل بسيط داخل ملف الـ xml. <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="15dp"> <TextView android:id="@+id/key" android:layout_width="100dp" android:layout_height="50dp" android:gravity="center" android:text="WEBSITE" /> <TextView android:id="@+id/value" android:layout_width="100dp" android:layout_height="50dp" android:gravity="center" android:text="3alamPro" android:layout_alignParentEnd="true"/> </RelativeLayout> صورة توضح الشكل النهائي للـ layout ثالثاً: سنبدأ الآن العمل على ملف الـ Java حيث سنتعلم بشكل أكبر طريقة عمل المكتبة. في البداية سنقوم كما جرت العادة بتعريف المتغيرات ولكن هذه المرة وقبل تعريفها سنقوم بكتابة @BindView(R.id.key) ليصبح الشكل النهائي للمتغيرات كالتالي public class MainActivity extends AppCompatActivity { @BindView(R.id.key) TextView key; @BindView(R.id.value) TextView value; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); } بهذه الأسطر البسيطة سنكون قد أنتهينا تماماً من الربط .. طبعاً للمقارنة هذا ما سنقوم بكتابته بدون butterknife .. قد يبدو الفرق بسيط .. ولكن مع وجود الكثير من الـ components التي تحتاج للربط سيكون الفرق واضح. public class MainActivity extends AppCompatActivity { TextView key; TextView value; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); key = (TextView) findviewbyid(R.id.key); value = (TextView) findviewbyid(R.id.value); } رابعاً: لا يجب أن ننسى إضافة بعد تعريف onCreate method. ButterKnife.bind(this); لتصبح onCreate method في شكلها النهائي @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); } هذا كان توضيح بسيط للميزة الأساسية التي تقدمها المكتبة .. ولكنها أيضاً تقدم المزيد من الإختصارات منها مثلاً [email protected] التي توفر لكن عناء ربط الـ strings الموجودة داخل ملف الـ values بدلاً من getResources().getStrings(R.strings.whatever) أيضاً توفر [email protected] وسنستعرض كيفية عملها. سنقوم الآن بإنشاء ملف Drawable عبارة عن إطار سنقوم بوضعه على key textview. نقوم بإنشاء ملف Drawable جديد ونسميه background على سبيل المثال بعد ذلك نقوم بإضافة الكود المرفق ( خلفية صفراء وحواف سوداء ) <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <solid android:color="#eee1"></solid> <stroke android:color="#000" android:width="10dp"></stroke> </shape> بالعودة لملف الجافا سنقوم بإنشاء أوبجكت جديد لملف الـ Drawable بطريقة الـ Bind. @BindDrawable(R.drawable.background) Drawable drawable; ومن ثم وضعه كخلفية للـ textview داخل onCreate method بعد ButterKnife.bind طبعاً key.setBackground(drawable); سنقوم أيضاً بتغيير الـ Text الموجودة داخل الـ textviews للتأكد بأن الربط يعمل بشكل صحيح key.setText("Website Bind"); value.setText("Pro3alam"); نقوم بتشغيل البرنامج والتأكد من النتيجة .. كل شي على ما يرام 👍 هل إنتهينا ؟ ليس بعد .. مازال هناك ميزة قوية تقدمها هذه المكتبة .. وهي الإستغناء التام عن الـ inner-classes الناتجة عن الـ Listener عند عمل setOnClickListener . عن طريق ButterKnife يمكنك بكل بساطة كتابة [email protected] ومن ثم الـ id الخاص بالـ view . على سبيل المثال .. سنقوم بإظهار Toast بسيط عند الضغط على أحد الـ textviews من خلال [email protected] @OnClick(R.id.key) void clicked1() { Toast.makeText(getApplicationContext(), "key", Toast.LENGTH_LONG).show(); } @OnClick(R.id.value) void clicked2() { Toast.makeText(getApplicationContext(), "value", Toast.LENGTH_LONG).show(); } عند تشغيل البرنامج وعند الضغط على الـ textviews سنرى بأن كل شي يعمل بشكل صحيح بهذا نكون قد إنتهينا من موضوعنا بإستعراض واحدة من أهم وأقوى المكاتب وسنقوم بإستعراض المزيد من المكتبات في قادم المواضيع بإذن الله
  4. بسم الله الرحمن الرحيم إستكمالاً لسلسلة مكتبات الأندرويد سنستعرض اليوم مكتبة من أهم المكتبات التي يستخدمها المطورون بشكل أساسي في تطوير التطبيقات على منصة الأندرويد. هذه المكتبة هي مكتبة Butter Knife. ولكن السؤال الأهم هو ما فائدة هذه المكتبة ؟ من المعروف لدى المطورين بأن طريقة ربط الـ View component مع الأوبجكت داخل كود الجافا هو باستخدام الأمر find view by id. ومن ثم ربط الأوبجكت عن طريق R.id.component. هذه الطريقة يوماً بعد يوم تثبت عدم فعاليتها وخصوصاً عند وجود الكثير من الـ components داخل الـ layout التي ستضطر لربطها بشكل يدوي. كما أن الربط العادي ستضطر في البداية إلى إنشاء المتغير في بداية الكلاس أما تعريفه فيجب أن يتم داخل onCreate method. هذه الطريقة باتت تثبت عدم فعاليتها يوماً بعد يوم. ButterKnife هي مكتبة تستطيع من خلالها ربط الأوبجكت مع الـ view component بشكل سريع وسلس وبشكل مباشر. كما تمكنك المكتبة من ربط الـ drawables والـ strings وغيرها وليست مقتصرة فقط على الـ views. كيف تعمل هذه المكتبة ؟ لنبدأ ... أولاً : وكما جرت العادة يجب إضافة هذه المكتبة داخل المشروع كـ dependency إضافية داخل ملف الـ gradle.build. من المهم أيضاً إضافة الـ annotationProcessor كي تعمل المكتبة بشكل صحيح. ومن ثم عمل sync لكي تتم الإضافة. compile 'com.jakewharton:butterknife:8.8.1' annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' ثانياً: بعد عمل sync يمكننا الآن البدء في تصميم الـ layout .. طبعاً سأقوم بتصميم layout بسيطة فقط لتوضيح عمل هذه المكتبة. من الممكن ملاحظة بأن ملف الـ xml لم يتغير بتاتاً ولم نقم بإضافة أي كود إضافي وهذه ميزة إضافية أيضاً لأن هناك الكثير من المكتبات الأخرى التي توفر ميزة الربط ستضطر حين إستخدامها إلى إضافة تعديل بسيط داخل ملف الـ xml. <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="15dp"> <TextView android:id="@+id/key" android:layout_width="100dp" android:layout_height="50dp" android:gravity="center" android:text="WEBSITE" /> <TextView android:id="@+id/value" android:layout_width="100dp" android:layout_height="50dp" android:gravity="center" android:text="3alamPro" android:layout_alignParentEnd="true"/> </RelativeLayout> صورة توضح الشكل النهائي للـ layout ثالثاً: سنبدأ الآن العمل على ملف الـ Java حيث سنتعلم بشكل أكبر طريقة عمل المكتبة. في البداية سنقوم كما جرت العادة بتعريف المتغيرات ولكن هذه المرة وقبل تعريفها سنقوم بكتابة @BindView(R.id.key) ليصبح الشكل النهائي للمتغيرات كالتالي public class MainActivity extends AppCompatActivity { @BindView(R.id.key) TextView key; @BindView(R.id.value) TextView value; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); } بهذه الأسطر البسيطة سنكون قد أنتهينا تماماً من الربط .. طبعاً للمقارنة هذا ما سنقوم بكتابته بدون butterknife .. قد يبدو الفرق بسيط .. ولكن مع وجود الكثير من الـ components التي تحتاج للربط سيكون الفرق واضح. public class MainActivity extends AppCompatActivity { TextView key; TextView value; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); key = (TextView) findviewbyid(R.id.key); value = (TextView) findviewbyid(R.id.value); } رابعاً: لا يجب أن ننسى إضافة بعد تعريف onCreate method. ButterKnife.bind(this); لتصبح onCreate method في شكلها النهائي @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); } هذا كان توضيح بسيط للميزة الأساسية التي تقدمها المكتبة .. ولكنها أيضاً تقدم المزيد من الإختصارات منها مثلاً [email protected] التي توفر لكن عناء ربط الـ strings الموجودة داخل ملف الـ values بدلاً من getResources().getStrings(R.strings.whatever) أيضاً توفر [email protected] وسنستعرض كيفية عملها. سنقوم الآن بإنشاء ملف Drawable عبارة عن إطار سنقوم بوضعه على key textview. نقوم بإنشاء ملف Drawable جديد ونسميه background على سبيل المثال بعد ذلك نقوم بإضافة الكود المرفق ( خلفية صفراء وحواف سوداء ) <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <solid android:color="#eee1"></solid> <stroke android:color="#000" android:width="10dp"></stroke> </shape> بالعودة لملف الجافا سنقوم بإنشاء أوبجكت جديد لملف الـ Drawable بطريقة الـ Bind. @BindDrawable(R.drawable.background) Drawable drawable; ومن ثم وضعه كخلفية للـ textview داخل onCreate method بعد ButterKnife.bind طبعاً key.setBackground(drawable); سنقوم أيضاً بتغيير الـ Text الموجودة داخل الـ textviews للتأكد بأن الربط يعمل بشكل صحيح key.setText("Website Bind"); value.setText("Pro3alam"); نقوم بتشغيل البرنامج والتأكد من النتيجة .. كل شي على ما يرام 👍 هل إنتهينا ؟ ليس بعد .. مازال هناك ميزة قوية تقدمها هذه المكتبة .. وهي الإستغناء التام عن الـ inner-classes الناتجة عن الـ Listener عند عمل setOnClickListener . عن طريق ButterKnife يمكنك بكل بساطة كتابة [email protected] ومن ثم الـ id الخاص بالـ view . على سبيل المثال .. سنقوم بإظهار Toast بسيط عند الضغط على أحد الـ textviews من خلال [email protected] @OnClick(R.id.key) void clicked1() { Toast.makeText(getApplicationContext(), "key", Toast.LENGTH_LONG).show(); } @OnClick(R.id.value) void clicked2() { Toast.makeText(getApplicationContext(), "value", Toast.LENGTH_LONG).show(); } عند تشغيل البرنامج وعند الضغط على الـ textviews سنرى بأن كل شي يعمل بشكل صحيح بهذا نكون قد إنتهينا من موضوعنا بإستعراض واحدة من أهم وأقوى المكاتب وسنقوم بإستعراض المزيد من المكتبات في قادم المواضيع بإذن الله
  5. بسم الله الرحمن الرحيم كثيراً ما يقدم مطورو الأندرويد بعض المكتبات التي تساعد المبرمجين على إنشاء تطبيقاتهم الخاصة وتضيف بعض الميزات التي قد لا تجدها في بيئة البرمجة الأساسية. هذه المكتبات قد تقدم إضافة جمالية لتطبيقك أو ميزة قد تحتاجها. سنحاول في هذه السلسلة بإذن الله تسليط الضوء على بعض المكتبات الممتازة والتي بإمكانك إضافتها لتطبيقك الخاص بكل سهولة. مكتبة اليوم هي مكتبة ستمكنك من إضافة خاصية الـ QR Scanning لتطبيقك الخاص وتعتبر من أبسط المكتبات من حيث سهولة الدمج مع تطبيقك الخاص. أغلب التطبيقات الحالية باتت تستخدم الـ QR Scanner في تطبيقاتها ومن أشهرها تطبيق الـ WhatssApp والـ SnapChat وغيرها ولكن كيف من الممكن دمج مثل هذه الميزة في تطبيقك ؟ هناك الكثير من الـ Libraries المنتشرة لنظام الأندرويد والتي تسمح بإضافة هذه الخاصية إلى تطبيقك ولكن سنختار اليوم واحدة من أسهل هذه المكاتب وأقلها تعقيداً وهي مكتبة ZXingScanner. لنبدأ في فهم طريقة عمل هذه المكتبة. أولاً : يجب عليك إضافة الـ Dependency داخل ملف الـ Build.gradle داخل Block الـ Dependencies ومن ثم عمل Sync للتطبيق من الأعلى كيف تتم إضافة هذه المكتبة. compile 'me.dm7.barcodescanner:zxing:1.9' لتشغيل هذه المكتبة نحتاج إلى Activity خاصة نقوم بالانتقال إليها في كل مرة أراد المستخدم عمل مسح لـ QR Code . طبعاً بالإمكان عمل Activity ومن ثم تشغيل الـ QR Scanner كـ Fragment داخل هذه الـ Activity مثل برنامج الـ Whatsapp ولكن سنستخدم Activity كاملة في هذا الشرح كونها أقل تعقيداً. نلاحظ من الصورة أدناه شكل الـ QR Scanner داخل Fragment من تطبيق الـ Whatsapp. أما في حالة الـ Activity فسيغطي القارئ كل الشاشة. نقوم الآن بإنشاء Activity جديدة داخل التطبيق الذي تريد إضافة هذه الخاصية له ونختار Empty Activity وتختار الإسم الذي يناسبك بالنسبة للـ Layout الخاصة بالـ Activity الجديدة فـ ليس هناك أي داعي لتغيير أي شيء فيها وبإمكانك طبعاً تغيير الـ Parent Layout سواء Linear أو Relative فلا يوجد أي فرق <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> </RelativeLayout> ننتقل بعد ذلك إلى الجزء الأهم وهو كلاس الـ Java الخاص بهذه الـ Activity ونبدأ العمل عليه. في البداية يجب علينا عمل Implementation لـ Interface خاص بالمكتبة التي تم إستيرادها مسبقاً public class <Your Class Name> extends AppCompatActivity implements ZXingScannerView.ResultHandler سنقوم بعد ذلك بعمل Override لـ Method خاصة ستقوم بعمل Handle للنتيجة المقروءة من قبل الـ Scanner. هذه الـ Method موجودة داخل الـ Interface السابق. public void handleResult(Result result){} ومن ثم سنحتاج إلى إضافة View خاص كمتغير من نوع ZXingScannerView لتشغيل الـ Scanner من خلاله داخل الـ Activity وإلى متغير من نوع String كي نستطيع التحقق من الـ Read Value أو النص المقروء من الـ QR Code. private ZXingScannerView mScannerView ; private String QRresult= ""; نقوم بعد ذلك بإضافة بعد الأكواد داخل onCreate method لكي يتم تشغيل الـ Scanner مباشرة بعد الإنتقال للـ Activity. في البداية سنقوم بعمل Instantiate لمتغير الـ ZXingScannerView وإختياره كـ Content View. mScannerView = new ZXingScannerView(this); setContentView(mScannerView); بعد ذلك سيتم إختيار الـ Class الحالي بتولي مهمة الـ Handling للنتيجة المقروءة من قبل الـ Scanner. عن طريق handleResult method. mScannerView.setResultHandler(this); وأخيراً سنقوم بتشغيل الـ Camera للقيام بمسح الـ QR Codes mScannerView.startCamera(); لتصبح onCreate method في شكلها النهائي @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.<your layout name>); mScannerView = new ZXingScannerView(this); setContentView(mScannerView); mScannerView.setResultHandler(this); mScannerView.startCamera(); } بعد قراءة الـ QR Code ستقوم handleResult method بتمرير Object من نوع result ومن أجل التأكد من أن المكتبة تعمل بشكل سليم سنقوم بتجريب QR Code لـ String معين. لكن في البداية يجب أن نقوم بإغلاق الـ Camera كأول أمر داخل handleResult method. mScannerView.stopCamera(); ومن ثم إستخراج النص المقروء من الـ result object وحفظه داخل المتغير المنشأ مسبقاً وعرضه داخل Toast للتأكد ومن ثم إنهاء الـ Activity والعودة للـ Activity السابقة QRresult = result.getText(); Toast.makeText(getApplicationContext(),QRresult,Toast.LENGTH_LONG).show(); finish(); للتأكد من عمل التطبيق بشكل سليم سنحتاج لـ جهاز حقيقي وذلك لأن الكاميرا لا تعمل داخل الـ Virtual Devices ومن ثم بإمكانك إستخدام أي موقع QR Codes Generator وتجربة التطبيق عليه ومن أشهر هذه المواقع موقع QR Code Generator نقوم بإختيار Text وكتابة أي نص ومن ثم الضغط على Create QR code وتجربة التطبيق. وسنواصل الحديث بإذن الله تعالى في المواضيع اللاحقة عن بعض المكتبات الأخرى.
  6. بسم الله الرحمن الرحيم كثيراً ما يقدم مطورو الأندرويد بعض المكتبات التي تساعد المبرمجين على إنشاء تطبيقاتهم الخاصة وتضيف بعض الميزات التي قد لا تجدها في بيئة البرمجة الأساسية. هذه المكتبات قد تقدم إضافة جمالية لتطبيقك أو ميزة قد تحتاجها. سنحاول في هذه السلسلة بإذن الله تسليط الضوء على بعض المكتبات الممتازة والتي بإمكانك إضافتها لتطبيقك الخاص بكل سهولة. مكتبة اليوم هي مكتبة ستمكنك من إضافة خاصية الـ QR Scanning لتطبيقك الخاص وتعتبر من أبسط المكتبات من حيث سهولة الدمج مع تطبيقك الخاص. أغلب التطبيقات الحالية باتت تستخدم الـ QR Scanner في تطبيقاتها ومن أشهرها تطبيق الـ WhatssApp والـ SnapChat وغيرها ولكن كيف من الممكن دمج مثل هذه الميزة في تطبيقك ؟ هناك الكثير من الـ Libraries المنتشرة لنظام الأندرويد والتي تسمح بإضافة هذه الخاصية إلى تطبيقك ولكن سنختار اليوم واحدة من أسهل هذه المكاتب وأقلها تعقيداً وهي مكتبة ZXingScanner. لنبدأ في فهم طريقة عمل هذه المكتبة. أولاً : يجب عليك إضافة الـ Dependency داخل ملف الـ Build.gradle داخل Block الـ Dependencies ومن ثم عمل Sync للتطبيق من الأعلى كيف تتم إضافة هذه المكتبة. compile 'me.dm7.barcodescanner:zxing:1.9' لتشغيل هذه المكتبة نحتاج إلى Activity خاصة نقوم بالانتقال إليها في كل مرة أراد المستخدم عمل مسح لـ QR Code . طبعاً بالإمكان عمل Activity ومن ثم تشغيل الـ QR Scanner كـ Fragment داخل هذه الـ Activity مثل برنامج الـ Whatsapp ولكن سنستخدم Activity كاملة في هذا الشرح كونها أقل تعقيداً. نلاحظ من الصورة أدناه شكل الـ QR Scanner داخل Fragment من تطبيق الـ Whatsapp. أما في حالة الـ Activity فسيغطي القارئ كل الشاشة. نقوم الآن بإنشاء Activity جديدة داخل التطبيق الذي تريد إضافة هذه الخاصية له ونختار Empty Activity وتختار الإسم الذي يناسبك بالنسبة للـ Layout الخاصة بالـ Activity الجديدة فـ ليس هناك أي داعي لتغيير أي شيء فيها وبإمكانك طبعاً تغيير الـ Parent Layout سواء Linear أو Relative فلا يوجد أي فرق <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> </RelativeLayout> ننتقل بعد ذلك إلى الجزء الأهم وهو كلاس الـ Java الخاص بهذه الـ Activity ونبدأ العمل عليه. في البداية يجب علينا عمل Implementation لـ Interface خاص بالمكتبة التي تم إستيرادها مسبقاً public class <Your Class Name> extends AppCompatActivity implements ZXingScannerView.ResultHandler سنقوم بعد ذلك بعمل Override لـ Method خاصة ستقوم بعمل Handle للنتيجة المقروءة من قبل الـ Scanner. هذه الـ Method موجودة داخل الـ Interface السابق. public void handleResult(Result result){} ومن ثم سنحتاج إلى إضافة View خاص كمتغير من نوع ZXingScannerView لتشغيل الـ Scanner من خلاله داخل الـ Activity وإلى متغير من نوع String كي نستطيع التحقق من الـ Read Value أو النص المقروء من الـ QR Code. private ZXingScannerView mScannerView ; private String QRresult= ""; نقوم بعد ذلك بإضافة بعد الأكواد داخل onCreate method لكي يتم تشغيل الـ Scanner مباشرة بعد الإنتقال للـ Activity. في البداية سنقوم بعمل Instantiate لمتغير الـ ZXingScannerView وإختياره كـ Content View. mScannerView = new ZXingScannerView(this); setContentView(mScannerView); بعد ذلك سيتم إختيار الـ Class الحالي بتولي مهمة الـ Handling للنتيجة المقروءة من قبل الـ Scanner. عن طريق handleResult method. mScannerView.setResultHandler(this); وأخيراً سنقوم بتشغيل الـ Camera للقيام بمسح الـ QR Codes mScannerView.startCamera(); لتصبح onCreate method في شكلها النهائي @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.<your layout name>); mScannerView = new ZXingScannerView(this); setContentView(mScannerView); mScannerView.setResultHandler(this); mScannerView.startCamera(); } بعد قراءة الـ QR Code ستقوم handleResult method بتمرير Object من نوع result ومن أجل التأكد من أن المكتبة تعمل بشكل سليم سنقوم بتجريب QR Code لـ String معين. لكن في البداية يجب أن نقوم بإغلاق الـ Camera كأول أمر داخل handleResult method. mScannerView.stopCamera(); ومن ثم إستخراج النص المقروء من الـ result object وحفظه داخل المتغير المنشأ مسبقاً وعرضه داخل Toast للتأكد ومن ثم إنهاء الـ Activity والعودة للـ Activity السابقة QRresult = result.getText(); Toast.makeText(getApplicationContext(),QRresult,Toast.LENGTH_LONG).show(); finish(); للتأكد من عمل التطبيق بشكل سليم سنحتاج لـ جهاز حقيقي وذلك لأن الكاميرا لا تعمل داخل الـ Virtual Devices ومن ثم بإمكانك إستخدام أي موقع QR Codes Generator وتجربة التطبيق عليه ومن أشهر هذه المواقع موقع QR Code Generator نقوم بإختيار Text وكتابة أي نص ومن ثم الضغط على Create QR code وتجربة التطبيق. وسنواصل الحديث بإذن الله تعالى في المواضيع اللاحقة عن بعض المكتبات الأخرى.
  7. بسم الله الرحمن الرحيم السلام عليكم ورحمة الله وبركاته يعاني الكثير من مطوري التطبيقات على منصة الـ Android من مشكلة التعامل مع النصوص في التطبيقات التي يطورونها. سنقوم في هذه المقالة بتسليط الضوء على ملف الـ XML التي يوفره لك Android Studio كحل لكثير من المشاكل المتعلقة بالـ Strings الموجودة داخل التطبيق الخاص بك. ومن هذه المشاكل : 1- احتواء التطبيق الخاص بك على الكثير من النصوص المتشابهة والمتكررة مما يجعل القيام بتغييرها جميعاً أمراً بحاجة إلى الكثير من الوقت. 2- قد ترغب بأن يدعم التطبيق الخاص بك لغات متعددة في آن واحد. الحل بكل بساطة يكمن في ملف الـ strings.xml. فكيف يمكننا التعامل معه ؟ مميزات ملف الـ strings.xml: 1- تخزين جميع النصوص المستخدمة داخل التطبيق واستخدام الملف كمكتبة يتم استيراد النصوص منها بشكل مباشر. 2- تغيير النص داخل ملف الـ strings كفيل بتغيير كافة الـ textViews التي تستخدم هذا النص على سبيل المثال. 3- تخزين جميع اللغات التي ترغب أن يدعمها التطبيق الخاص بك. حيث سيتم عرض النصوص بحسب اللغة المستخدمة داخل الجهاز. لنتعرف معاً كيف يتم التعامل مع هذا الملف. 1- نقوم بإنشاء project جديد ونسميه Strings على سبيل المثال. 2- نقوم بترك اعدادات الأجهزة والنظام كما هي. 3- نقوم باختيار Empty Activity على سبيل المثال. 4- نختار الإسم الخاص بالـ Activity. 5- بعد إنشاء project جديد نقوم بالذهاب إلى قائمة الـ projects على اليسار واسدال قائمة الـ resources ومن ثم قائمة الـ values لنجد ملف الـ strings.xml. اللغة المقترحة أو الـ default عند إنشاء أي تطبيق هي الإنجليزية. 6- عند فتح الملف سنلاحظ وجود Block الـ resources وبداخله Block خاص بالنصوص المستخدمة داخل التطبيق ونلاحظ بأن اسم التطبيق محفوظ بشكل اوتوماتيكي داخل ملف الـ strings. الصيغة التي يتم ادخال أي strings في ملف الـ strings هي: <string name="NAME_OF_THE_VARIABLE">THE_VALUE</string> المقصود بـ NAME OF THE VARIABLE هو اسم النص، والاسم هو الذي سيتم الاشارة إليه مستقبلاً إذا اردت استخدام النص داخل textView على سبيل المثال. وأما THE VALUE تشير إلى النص الذي سيظهر عند تشغيل التطبيق. في الصورة يتبين لنا بأن app_name هو اسم الـ string ونصه Strings. 7- نقوم بإضافة بعض النصوص بنفس الطريقة. 8- نعود للـ activity ونقوم بإضافة textView آخر للتجريب. "نلاحظ وجود كلمة Strings والتي ستتغير بمجرد تغييرك للـ app_name الموجود داخل الـ strings.xml." 9- بالضغط على أول textView سيظهر لنا قائمة الخصائص على يسار الشاشة وإذا لم تظهر يمكنك الذهاب إلى خصائص جميع الـ components الموجودة داخل الـ activity بالضغط على text. 10- قم بتغيير الـ text إلى string/[email protected] وستلاحظ تغير النصوص داخل الـ activity بشكل مباشر إلى نفس النصوص الموجود داخل ملف الـstrings وأي تغيير داخل ملف الـ XML سيقوم بتغييرها. 11- نلاحظ بأننا عندما قمنا بتغيير النص داخل ملف النصوص. تغيرت النصوص بشكل مباشر. تم مناقشة الفائدة الأولى من ملف الـ strings وطريقة استخدامه والآن لنرى كيف نستطيع دعم أكثر من لغة في تطبيق واحد. سنختار العربية على سبيل المثال. 1- سنحتاج إلى إنشاء ملف strings.xml جديد داخل ملف الـ values. 2- اسم الملف يجب أن يكون strings ونقوم بتغيير الـ Directory name إلى values-ar. وهنا رمز الـ ar يشير إلى العربية ولكل لغة الرمز الخاص بها. 3- نلاحظ وجود الملف داخل مجلد الـ strings وبجانبه الرمز الخاص به. 4- نقوم الآن بنسخ جميع النصوص الموجود داخل الملف الأول ولصقها داخل ملف الـ strings.xml الجديد الخاص باللغة العربية وتغيير جميع النصوص إلى اللغة العربية. يلاحظ بأنه يجب عدم تغيير NAME_OF_THE_VARIABLE لأن بتغييرها ستتضطر إلى اختيار إحدى اللغتين للظهور داخل الـ textViews على سبيل المثال. 5- بالعودة إلى الـ activity وبعد الانتهاء من ترجمة النصوص للعربية سنلاحظ بأنه تم إضافة اللغة العربية إلى قائمة الـ Language أعلى الـ activity وبامكانك اختيار اللغة العربية أو الانجليزية لرؤية النتائج. 6- نلاحظ عند تشغيل الـ emulator سيعطينا كافة النصوص باللغة الانجليزية كونها اللغة الافتراضية. 7- لإظهار النصوص باللغة العربية سنضطر لتغيير لغة الجهاز إلى العربية. نذهب للـ settings او الاعدادات. 8- نختار العربية. 9- بالعودة إلى قائمة التطبيقات نجد بأن اسم التطبيق قد تغير بشكل مباشر من Strings إلى النصوص. 10- نجد بأن كافة النصوص تغيرت إلى اللغة العربية بشكل مباشر. بهذه الطريقة ستحل الكثير من المشاكل التي قد تواجه الكثير من المطورين عند التعامل مع النصوص أو اللغات. يجب التنويه إلى أن ملف الـ strings.xml يقبل أيضاً array of strings أو مصفوفة من النصوص وليس نصوص فردية فقط وقد نتطرق إلى هذه الفكرة وكيفية التعامل معها بمقال آخر. إلى هنا نصل إلى نهاية المقال. أتمنى بأني قد وفقت إلى ايصال الفكرة بشكل واضح وشكراً على حسن المتابعة.
  8. بسم الله الرحمن الرحيم السلام عليكم ورحمة الله وبركاته ستحتاج إلى قراءة الموضوع السابق الخاص بـالـ Hashing Algorithm لكي تستطيع المتابعة من خلال الرابط التالي هنا Hashing سنتعرف معاً في هذا المقال على : - طريقة الـ Open Addressing. - طريقة التطبيق في لغة برمجة Java. تكلمنا في المقالة السابقة عن الـ Hashing Algorthim وكيف يمكننا حل مشكلة تكرار الـ Data وتم مناقشة فكرة واحدة وهي الـ Separate Chaining . وللتذكير فإن طريقة الـ Separate Chaining هي طريقة تعتمد على بناء ArrayList بشكل خاص أو أي Structure آخر بشكل عام داخل كل خلية في الـ Hash Table. ومن ثم يتم تخزين القيم اللي تمتلك نفس قيمة الـ Index في هذا الـ Structure. ولكن !!! طريقة الـ Separate Chaining لها العديد من الميزات والعيوب مثل : الميزات : 1 - مطلق الحرية في استخدام الـ Structure الذي يناسبك سواء كان Tree أو ArrayList على سبيل المثال. 2 - سهولة تطبيق فكرة الـ Separate Chaining داخل أي لغة برمجة. العيوب : 1 - ماذا لو أدخل المستخدم 10 أرقام على سبيل المثال والتي تملك نفس الـ Index. فـ على سبيل المثال لنقل بأننا نريد ملئ HashTable بحجم خليتين بالأرقام التالية : 2-4-6-8-10-12. عند تطبيق الدالة Index = number % arrayLength سنجد بأن كل الأرقام ستوضع في خانة 0 في الجدول كما في الصورة التالية : ولكن لحظة !! الـ ArrayList الموجودة بالصورة هي عبارة عن Array عادية موضوعة داخل Index 0 والفكرة الاساسية من الـ Hashing لم يتم تطبيقها للأسف لأننا في حال أردنا البحث عن رقم 12 مثلاً سنضطر للبحث داخل كل الـ Array داخل Index 0. تخيل بأنك أضفت أكثر من الف Record وجميع هذه الـ Records تمتلك نفس الـ Index ستستغرق نفس الفترة الزمنية المستغرقة في حال إضافة هذه الـ Records في Array عادية بدون التطرق لفكرة الـ Hashing. لذلك نستطيع القول بأن فكرة الـ Separate Chaining ليست ممتازة في جميع الأحوال. فما الحل ؟ الحل يتمثل في الطريقة الثانية وهي Open Addressing. الطريقة الثانية : Open Addressing. ميزة هذه الطريقة بأن كل الـ Records سيتم توزيعها على كل الخانات الفارغة داخل الـ Table. وفكرتها تعتمد على إضافة رقم معين يتم نقل الـ Record المتكرر إلى خلية ثانية بمقدار هذا الرقم. هذا الرقم يعتمد على عدة دوال وهي : 1 - Linear probing وهذه الطريقة التي سنناقشها في هذه المقالة. 2 - Quadratic probing وهي زيادة بمقدار مربع i كل مرة. بحيث i = 0 , 1 , 2 , etc بالشكل التالي i^2 +/- . بمعنى آخر سنقوم في البداية بالتحرك بمقدار i إلى اليمين ومن ثم بمقدار i إلى اليسار. 3 - Double hashing وهي زيادة بحسب دالة أخرى. Linear probing أو زيادة خطية بمعنى زيادة عدد معين تختاره أنت من البداية يطلق عليه i على كل Record متكرر. لكي تتوضح الصورة لنأخذ المثال التالي: نريد إضافة هذه الأرقام 5 - 10 - 21 - 31 إلى جدول مكون من 5 خانات. ونريد تحديد الـ i لكي تكون 1 في حالة التكرار. سيكون المخرج النهائي كالتالي : 1 - عند إضافة 5 .. لن يكون هناك أي مشاكل لأن الجدول يفتقر لوجود أي Data وسيتم وضعه في Index 0. 2 - عند إضافة 10 .. سنلاحظ وجود 5 في خانة Index 0 ولذلك لا يمكننا وضع 10 أيضاً فنقوم بزيادة الـ Index بـ قيمة i المختارة سابقاً وهي 1 فيصبح الـ Index هو Index 1. 3 - عند إضافة 21 .. يجب علينا إضافتها إلى Index 1 ولكنه ممتلئ بالقيمة 10 .. فنقوم بالزيادة بـ 1 ونقوم بوضعها بخانة Index 2. 4 - عند إضافة 31 .. يجب علينا إضافتها إلى Index 1 ولكنه ممتلئ بالقيمة 10 .. نقوم بزيادة 1 ونضيف 31 إلى Index 2 ولكنه ممتلئ أيضاً بالقيمة 21 فنقوم بزيادة 1 مرة أخرى ونضع 31 في Index 3. المخرج النهائي : ولكن طريقة الـ Open Addressing فتحت لنا الباب لظهور مشكلة جديدة وهي مشكلة الحذف. ماذا لو أردنا في المثال السابق حذف الرقم 21. ومن ثم نريد إيجاد الرقم 31. سنقوم بالبحث بداية داخل Index 1 ولكننا سنجد 10 فـ سننتقل إلى Index 2 لنفاجئ بأن القيمة هي Null لأننا سبق وحذفنا الرقم 21. وهذا خطأ .. لأننا اتفقنا في البداية بأن الـ Index إذا كان Null "فارغ" يجب علينا ملء هذا الـ Index قبل الانتقال إلى Index جديد. ستخبرني بأنه من الممكن إكمال البحث حتى العودة للـ Index الذي بدأنا عنده للتأكد بأن 31 غير موجودة ولكن ماذا لو كان 31 غير موجود فعلاً و Index 2 فارغ بسبب إنه لم يتم إضافة 31 بعد .. في هذه الحالة ستقوم بتضييع الكثير من الوقت رغم علمك المسبق بأن 31 غير موجود. تبدو الفكرة محيرة فعلاً فما الحل؟ الحل يتمثل بإضافة " حالة " أو Status إلى الجدول الموجود في المثال السابق. سنقوم بتغيير حالة الـ 21 في حالة الحذف إلى D دلالة إلى Deleted بدون حذفها بشكل نهائي. في هذه الحالة لن يتعطل البحث أو يتوقف وستحل المشكلة. في البداية ستكون الـ Status الخاصة بكل الخلايا E دلالة إلى Empty او فارغ. إذا تم وضع عنصر جديد داخلها سيتم تغيير حالتها إلى O دلالة إلى Occupied او ممتلئ. الآن في حالة البحث عن 31 بعد حذف 21. سنقوم بالذهاب إلى Index 1 لنجد 10 فننتقل إلى Index 2 لنجد 21 فننتقل إلى Index 3 فنجد 31. ماذا لو أردنا البحث عن 21 بعد حذفها ؟ لا يمكنك عمل return true في حالة البحث إلا في حالة واحدة فقط إذا كان القيمة الموجودة في الخلية = القيمة المبحوث عنها + الحالة أو الـ Status = O. لو كانت D كما في مثالنا يجب علينا عمل return false. - التطبيق بلغة Java. سنتحتاج إلى عمل 3 كلاسات. الكلاس الأول Cell والكلاس الثاني HashTable والثالث عبارة عن TestClass. كلاس Cell : الخلية هنا تتكون من القيمة وحالتها كما أسلفنا سابقاً بالإضافة إلى بعض الـ Methods المهمة. public class Cell { Object dataObject ; // value String status ; // status E for empty, D for deleted, or O for occupied public Cell(){ status = "E"; // default value for a null cell } public Cell(Object obj){ status = "O"; dataObject = obj ; } public String toString(){ return "Object : "+dataObject.toString()+"\t\t "+status+"\n" ; } public int getHash(){ // we are going to use hashCode() method that exists in Object class return dataObject.hashCode(); } public void setStatus(String s){ status = s ; } } كلاس HashTable : يتكون من كل ماله علاقة بالجدول المراد إنشائه من Methods. أولا: Instance Variables and Constructors private int size ; private Cell [] list ; public HashTable(){ // default size for empty hash table size = 0 ; } public HashTable(int i){ // creating hash table with a desired value list = new Cell[i]; size = i ; for (int j = 0 ; j<i ; j++) // NullPointerException !! list[j] = new Cell(); } ثانياً : Insert method " إضافة " public void insert(Object obj){ Cell tmp = new Cell(obj); int index = obj.hashCode()%size ; if (list[index].status.equals("E") || list[index].status.equals("D")){ // empty or deleted cell list[index] = tmp ; }else{ // status = occupied int counter = 0 ; index++; while(counter <list.length){ if (list[index].status.equals("E")){ // empty cell list[index] = tmp ; list[index].setStatus("O"); break ; } counter++; index = (index+1)%size; } } } ثالثاً : retrieve method " بحث " هذه الميثود تعطيك قيمة الـ Index الموجود فيها العنصر المراد البحث عنه أو -1 إشارة إلى عدم وجوده public int retrieve(Object obj){ // get index of obj int index = obj.hashCode()%size ; if (list[index].status.equals("E")) return -1 ; // not found int counter = 0 ; while(counter < list.length){ // till the end of the table if (list[index].status.equals("D") && list[index].dataObject.equals(obj)) return -1 ; if (list[index].status.equals("O") && list[index].dataObject.equals(obj)) return index; index = (index+1)%size; counter++ ; } return -1; // not found } رابعاً : Delete Method " حذف " public void delete(Object obj){ int index = retrieve(obj); if (index != -1){ // if found list[index].setStatus("D"); } } خامساً وأخيراً : ميثود الطباعة. public String toString(){ String str = ""; System.out.println("Value\tStatus"); for (int i = 0 ; i<list.length ; i++){ if (list[i].dataObject == null){ str = str+" - "; }else str = str+" "+list[i].dataObject.toString(); str = str+"\t "+list[i].status+"\n"; } return str ; } كلاس TestClass وسنقوم بعمل التالي : سنقوم بعمل جدول مكون من 13 خانة ونقوم بعمل التالي: 1 - إضافة 18 - 26 - 35 - 9 من " اليمين إلى اليسار " 2 - البحث عن 15 - 48 " قيم غير موجودة " 3 - حذف 35 4 - البحث عن 9 " الخانة الصحيحة 10 " 5 - إضافة 64 - 47 " من اليمين إلى اليسار " 6 - طباعة الجدول public class test { public static void main(String [] args){ //create new hashTable HashTable hashTable = new HashTable (13); //insert 18 26 35 9 hashTable.insert(new Integer(18)); hashTable.insert(new Integer(26)); hashTable.insert(new Integer(35)); hashTable.insert(new Integer(9)); // get index of element 15 , 48 System.out.println(hashTable.retrieve(new Integer(15))); // -1 System.out.println(hashTable.retrieve(new Integer(48))); // -1 //delete element 35 " change status to D " ; hashTable.delete(new Integer(35)); // get index of element 9 System.out.println(hashTable.retrieve(new Integer(9))); // 10 //insert 64 47 hashTable.insert(new Integer(64)); hashTable.insert(new Integer(47)); //printing System.out.println(hashTable); } } المخرج النهائي :
  9. بسم الله الرحمن الرحيم السلام عليكم ورحمة الله وبركاته ستحتاج إلى قراءة الموضوع السابق الخاص بـالـ Hashing Algorithm لكي تستطيع المتابعة من خلال الرابط التالي هنا Hashing سنتعرف معاً في هذا المقال على : - طريقة الـ Open Addressing. - طريقة التطبيق في لغة برمجة Java. تكلمنا في المقالة السابقة عن الـ Hashing Algorthim وكيف يمكننا حل مشكلة تكرار الـ Data وتم مناقشة فكرة واحدة وهي الـ Separate Chaining . وللتذكير فإن طريقة الـ Separate Chaining هي طريقة تعتمد على بناء ArrayList بشكل خاص أو أي Structure آخر بشكل عام داخل كل خلية في الـ Hash Table. ومن ثم يتم تخزين القيم اللي تمتلك نفس قيمة الـ Index في هذا الـ Structure. ولكن !!! طريقة الـ Separate Chaining لها العديد من الميزات والعيوب مثل : الميزات : 1 - مطلق الحرية في استخدام الـ Structure الذي يناسبك سواء كان Tree أو ArrayList على سبيل المثال. 2 - سهولة تطبيق فكرة الـ Separate Chaining داخل أي لغة برمجة. العيوب : 1 - ماذا لو أدخل المستخدم 10 أرقام على سبيل المثال والتي تملك نفس الـ Index. فـ على سبيل المثال لنقل بأننا نريد ملئ HashTable بحجم خليتين بالأرقام التالية : 2-4-6-8-10-12. عند تطبيق الدالة Index = number % arrayLength سنجد بأن كل الأرقام ستوضع في خانة 0 في الجدول كما في الصورة التالية : ولكن لحظة !! الـ ArrayList الموجودة بالصورة هي عبارة عن Array عادية موضوعة داخل Index 0 والفكرة الاساسية من الـ Hashing لم يتم تطبيقها للأسف لأننا في حال أردنا البحث عن رقم 12 مثلاً سنضطر للبحث داخل كل الـ Array داخل Index 0. تخيل بأنك أضفت أكثر من الف Record وجميع هذه الـ Records تمتلك نفس الـ Index ستستغرق نفس الفترة الزمنية المستغرقة في حال إضافة هذه الـ Records في Array عادية بدون التطرق لفكرة الـ Hashing. لذلك نستطيع القول بأن فكرة الـ Separate Chaining ليست ممتازة في جميع الأحوال. فما الحل ؟ الحل يتمثل في الطريقة الثانية وهي Open Addressing. الطريقة الثانية : Open Addressing. ميزة هذه الطريقة بأن كل الـ Records سيتم توزيعها على كل الخانات الفارغة داخل الـ Table. وفكرتها تعتمد على إضافة رقم معين يتم نقل الـ Record المتكرر إلى خلية ثانية بمقدار هذا الرقم. هذا الرقم يعتمد على عدة دوال وهي : 1 - Linear probing وهذه الطريقة التي سنناقشها في هذه المقالة. 2 - Quadratic probing وهي زيادة بمقدار مربع i كل مرة. بحيث i = 0 , 1 , 2 , etc بالشكل التالي i^2 +/- . بمعنى آخر سنقوم في البداية بالتحرك بمقدار i إلى اليمين ومن ثم بمقدار i إلى اليسار. 3 - Double hashing وهي زيادة بحسب دالة أخرى. Linear probing أو زيادة خطية بمعنى زيادة عدد معين تختاره أنت من البداية يطلق عليه i على كل Record متكرر. لكي تتوضح الصورة لنأخذ المثال التالي: نريد إضافة هذه الأرقام 5 - 10 - 21 - 31 إلى جدول مكون من 5 خانات. ونريد تحديد الـ i لكي تكون 1 في حالة التكرار. سيكون المخرج النهائي كالتالي : 1 - عند إضافة 5 .. لن يكون هناك أي مشاكل لأن الجدول يفتقر لوجود أي Data وسيتم وضعه في Index 0. 2 - عند إضافة 10 .. سنلاحظ وجود 5 في خانة Index 0 ولذلك لا يمكننا وضع 10 أيضاً فنقوم بزيادة الـ Index بـ قيمة i المختارة سابقاً وهي 1 فيصبح الـ Index هو Index 1. 3 - عند إضافة 21 .. يجب علينا إضافتها إلى Index 1 ولكنه ممتلئ بالقيمة 10 .. فنقوم بالزيادة بـ 1 ونقوم بوضعها بخانة Index 2. 4 - عند إضافة 31 .. يجب علينا إضافتها إلى Index 1 ولكنه ممتلئ بالقيمة 10 .. نقوم بزيادة 1 ونضيف 31 إلى Index 2 ولكنه ممتلئ أيضاً بالقيمة 21 فنقوم بزيادة 1 مرة أخرى ونضع 31 في Index 3. المخرج النهائي : ولكن طريقة الـ Open Addressing فتحت لنا الباب لظهور مشكلة جديدة وهي مشكلة الحذف. ماذا لو أردنا في المثال السابق حذف الرقم 21. ومن ثم نريد إيجاد الرقم 31. سنقوم بالبحث بداية داخل Index 1 ولكننا سنجد 10 فـ سننتقل إلى Index 2 لنفاجئ بأن القيمة هي Null لأننا سبق وحذفنا الرقم 21. وهذا خطأ .. لأننا اتفقنا في البداية بأن الـ Index إذا كان Null "فارغ" يجب علينا ملء هذا الـ Index قبل الانتقال إلى Index جديد. ستخبرني بأنه من الممكن إكمال البحث حتى العودة للـ Index الذي بدأنا عنده للتأكد بأن 31 غير موجودة ولكن ماذا لو كان 31 غير موجود فعلاً و Index 2 فارغ بسبب إنه لم يتم إضافة 31 بعد .. في هذه الحالة ستقوم بتضييع الكثير من الوقت رغم علمك المسبق بأن 31 غير موجود. تبدو الفكرة محيرة فعلاً فما الحل؟ الحل يتمثل بإضافة " حالة " أو Status إلى الجدول الموجود في المثال السابق. سنقوم بتغيير حالة الـ 21 في حالة الحذف إلى D دلالة إلى Deleted بدون حذفها بشكل نهائي. في هذه الحالة لن يتعطل البحث أو يتوقف وستحل المشكلة. في البداية ستكون الـ Status الخاصة بكل الخلايا E دلالة إلى Empty او فارغ. إذا تم وضع عنصر جديد داخلها سيتم تغيير حالتها إلى O دلالة إلى Occupied او ممتلئ. الآن في حالة البحث عن 31 بعد حذف 21. سنقوم بالذهاب إلى Index 1 لنجد 10 فننتقل إلى Index 2 لنجد 21 فننتقل إلى Index 3 فنجد 31. ماذا لو أردنا البحث عن 21 بعد حذفها ؟ لا يمكنك عمل return true في حالة البحث إلا في حالة واحدة فقط إذا كان القيمة الموجودة في الخلية = القيمة المبحوث عنها + الحالة أو الـ Status = O. لو كانت D كما في مثالنا يجب علينا عمل return false. - التطبيق بلغة Java. سنتحتاج إلى عمل 3 كلاسات. الكلاس الأول Cell والكلاس الثاني HashTable والثالث عبارة عن TestClass. كلاس Cell : الخلية هنا تتكون من القيمة وحالتها كما أسلفنا سابقاً بالإضافة إلى بعض الـ Methods المهمة. public class Cell { Object dataObject ; // value String status ; // status E for empty, D for deleted, or O for occupied public Cell(){ status = "E"; // default value for a null cell } public Cell(Object obj){ status = "O"; dataObject = obj ; } public String toString(){ return "Object : "+dataObject.toString()+"\t\t "+status+"\n" ; } public int getHash(){ // we are going to use hashCode() method that exists in Object class return dataObject.hashCode(); } public void setStatus(String s){ status = s ; } } كلاس HashTable : يتكون من كل ماله علاقة بالجدول المراد إنشائه من Methods. أولا: Instance Variables and Constructors private int size ; private Cell [] list ; public HashTable(){ // default size for empty hash table size = 0 ; } public HashTable(int i){ // creating hash table with a desired value list = new Cell[i]; size = i ; for (int j = 0 ; j<i ; j++) // NullPointerException !! list[j] = new Cell(); } ثانياً : Insert method " إضافة " public void insert(Object obj){ Cell tmp = new Cell(obj); int index = obj.hashCode()%size ; if (list[index].status.equals("E") || list[index].status.equals("D")){ // empty or deleted cell list[index] = tmp ; }else{ // status = occupied int counter = 0 ; index++; while(counter <list.length){ if (list[index].status.equals("E")){ // empty cell list[index] = tmp ; list[index].setStatus("O"); break ; } counter++; index = (index+1)%size; } } } ثالثاً : retrieve method " بحث " هذه الميثود تعطيك قيمة الـ Index الموجود فيها العنصر المراد البحث عنه أو -1 إشارة إلى عدم وجوده public int retrieve(Object obj){ // get index of obj int index = obj.hashCode()%size ; if (list[index].status.equals("E")) return -1 ; // not found int counter = 0 ; while(counter < list.length){ // till the end of the table if (list[index].status.equals("D") && list[index].dataObject.equals(obj)) return -1 ; if (list[index].status.equals("O") && list[index].dataObject.equals(obj)) return index; index = (index+1)%size; counter++ ; } return -1; // not found } رابعاً : Delete Method " حذف " public void delete(Object obj){ int index = retrieve(obj); if (index != -1){ // if found list[index].setStatus("D"); } } خامساً وأخيراً : ميثود الطباعة. public String toString(){ String str = ""; System.out.println("Value\tStatus"); for (int i = 0 ; i<list.length ; i++){ if (list[i].dataObject == null){ str = str+" - "; }else str = str+" "+list[i].dataObject.toString(); str = str+"\t "+list[i].status+"\n"; } return str ; } كلاس TestClass وسنقوم بعمل التالي : سنقوم بعمل جدول مكون من 13 خانة ونقوم بعمل التالي: 1 - إضافة 18 - 26 - 35 - 9 من " اليمين إلى اليسار " 2 - البحث عن 15 - 48 " قيم غير موجودة " 3 - حذف 35 4 - البحث عن 9 " الخانة الصحيحة 10 " 5 - إضافة 64 - 47 " من اليمين إلى اليسار " 6 - طباعة الجدول public class test { public static void main(String [] args){ //create new hashTable HashTable hashTable = new HashTable (13); //insert 18 26 35 9 hashTable.insert(new Integer(18)); hashTable.insert(new Integer(26)); hashTable.insert(new Integer(35)); hashTable.insert(new Integer(9)); // get index of element 15 , 48 System.out.println(hashTable.retrieve(new Integer(15))); // -1 System.out.println(hashTable.retrieve(new Integer(48))); // -1 //delete element 35 " change status to D " ; hashTable.delete(new Integer(35)); // get index of element 9 System.out.println(hashTable.retrieve(new Integer(9))); // 10 //insert 64 47 hashTable.insert(new Integer(64)); hashTable.insert(new Integer(47)); //printing System.out.println(hashTable); } } المخرج النهائي : تم ترقية هذا الطرح المميز الى صفحة المقالات
  10. بسم الله الرحمن الرحيم السلام عليكم ورحمة الله وبركاته Hashing الكثير من الـ algorithms تستخدم في عملية البحث البسيطة عن Record معين أو Data معينة وسط الآلاف وأحيانا مئات الالاف من الملفات المخزنة في الذاكرة. العامل المهم في عملية اختيار الطريقة الممتازة تعتمد على الوقت وفعالية الجهاز المستخدم في عملية البحث ومدى كفائته. وواحدة من أفضل الطرق المستخدمة حالياً هي الـ Hashing . سنتعرف معاً في هذا المقال على : - تعريف بسيط عن الـ Hashing. - بعض الطرق المستخدمة في التعامل مع الـ Data في حالة التكرار في طريقة الـ Hashing. - طريقة التطبيق في لغة برمجة Java. تعريف بسيط عن الـ Hashing: هي طريقة تستخدم لحفظ الملفات وتخزينها ومن ثم البحث عنها بأسرع طريقة ممكنة. تتميز هذه الطريقة عن غيرها بأنها قادرة على جلب الـ Data بوقت سريع جدا وسط ملفات ومعلومات كبيرة نوعاً ما مقارنة بغيرها من الطرق. هذه الطريقة تتميز بحفظ الـ Data في جدول بحيث يتم إعطاء كل معلومة رقم مميز يتم حفظه داخل جدول من البيانات ومن ثم يتم الوصول إليه مباشرة في حال الحاجة إلى البحث عنه. هذه الطريقة تتميز بقلة الوقت اللازم للقيام بهذه العملية بحيث يمكنك مباشرة معرفة ما إذا كان الملف الجاري البحث عنه موجود أو غير موجود دون الحاجة للبحث عنه داخل الذاكرة بأكملها. مثال : لنفرض بأن لدينا الأرقام التالية : 1-7-4-8-9-5 . إذا قمنا بحفظ هذه الأرقام داخل Array بشكل عشوائي مباشر على سبيل المثال فعند البحث عن رقم 9 ستضطر إلى البحث داخل الـ Array بالكامل لمعرفة ما اذا كان الرقم 9 موجود أو لا. تخيل بأن لديك أكثر من الف رقم او أكثر!! يمكنك تخيل الوقت الطويل الذي ستحتاجه للقيام بعملية البحث هذه. لكن باستخدام Hashing يمكننا مثلا تخزين هذه الأرقام داخل Array ولكن ليس بشكل عشوائي كما في السابق وإنما بالنسبة لباقي قسمة العدد على حجم الـ Array على سبيل المثال. لنتفرض بأننا انشأنا Array بحجم 9 خلايا، الصف الأول يوضح الـ index والصف الثاني يوضح العدد الموجود داخل هذا الـ index 8 7 6 5 4 3 2 1 0 index value عند تعبئة هذه الـ Array سنستخدم العلاقة التالية : index = number % arrayLength. بمعنى آخر سيتم وضع الرقم داخل الخانة المعبرة عن باقي قسمة العدد على حجم الـ Array. فيكون الجدول بعد التعبئة بهذا الشكل 8 7 6 5 4 3 2 1 0 index 8 7 5 4 1 9 value لاحظ بأن الرقم 9 وضع بخانة رقم 0 لان باقي قسمة 9 على حجم الـ Array سيعطيك صفر. الآن بامكانك مباشرة الذهاب حين البحث عن رقم 9 إلى الرقم المستخرج من الدالة index = number % arrayLength وهو صفر لتعرف ما اذا كان الرقم 9 موجود أو لا وبالتالي توفير الكثير من الوقت. بعض الطرق المستخدمة في التعامل مع الـ Data في حالة التكرار في طريقة الـ Hashing: في المثال السابق لم نصادف أي تكرار في قيمة باقي قسمة الارقام على حجم الـ array ولكن كيف من الممكن حل مشكلة تكرار القيم في حال وقوعها ؟ هناك الكثير من الحلول التي من الممكن استخدامها لحل هذه المشكلة ولكن سيتم ذكر حل واحد في هذا المقال وسيتم الحديث عن باقي الحلول في مقالات قادمة إن شاء الله. 1 - Separate Chaining : هذه الطريقة تعتمد على بناء ArrayList بشكل خاص أو أي Structure آخر بشكل عام داخل كل خلية في الـ Hash Table الموضح في الأمثلة السابقة. لنأخذ هذه الأعداد على سبيل المثال : 12 – 17 – 29 – 6 – 30 – 31 – 4 – 8 ولنقم بتوزيعها علىHash Table بحجم 4 خلايا. في هذه الحالة يتم أولا معرفة الـ index المراد البحث داخله ومن ثم البحث داخل الـ ArrayList لمعرفة ما اذا كان الـ record المراد البحث عنه موجود أو غير موجود. طريقة التطبيق في لغة برمجة Java: هناك الكثير من الطرق لتطبيق فكرة الـ Hashing بواسطة الجافا ولكن سنقوم باتباع الطريقة الأسهل والأقل تعقيداً. سنحتاج إلى إنشاء كلاس List : List Class : عبارة عن كلاس سيتم عمل objects منه ويحتوي على الـ ArrayList المراد تخزين الـ Data فيها بالإضافة إلى Constructor. import java.util.ArrayList; public class List { private ArrayList<Integer> list = new ArrayList<Integer>(); //Constructor public List(){ } public ArrayList<Integer> getList(){ return list ; } } نحتاج أيضاً لإنشاء test class للتأكد من صحة الـ HashTable Test Class : في التيست كلاس سنقوم بإنشاء Array من كلاس List. لاحظ بأنه يجب عليك تعريف كل عناصر هذه الـ Array وبدون هذا التعريف سيظهر لك NullPointerException. //hash table List [] hashTable4 = new List [4] ; for (int i = 0 ; i<hashTable4.length ; i++) hashTable4[i] = new List(); ميثود print لطباعة الجدول public static void print(List [] array){ for (int i = 0 ; i<array.length ; i++) System.out.println("index "+i+" : "+array[i].getList().toString()); } ميثود getHashCode لمعرفة الـ index المناسب public static int getHashCode(int num){ return num%4 ; } نقوم بتخزين الأرقام في array لتسهيل عملية التصنيف والإدخال ومن ثم نقوم بإدخالها في object hashTable4 //numbers we want to add int [] listOfNumbers = {12,17,29,30,6,31,4,8}; //add Numbers to the hash table for (int i = 0 ; i<listOfNumbers.length ; i++){ int index = getHashCode(listOfNumbers[i]); hashTable4[index].getList().add(listOfNumbers[i]); } المخرج النهائي
  11. بسم الله الرحمن الرحيم السلام عليكم ورحمة الله وبركاته Hashing الكثير من الـ algorithms تستخدم في عملية البحث البسيطة عن Record معين أو Data معينة وسط الآلاف وأحيانا مئات الالاف من الملفات المخزنة في الذاكرة. العامل المهم في عملية اختيار الطريقة الممتازة تعتمد على الوقت وفعالية الجهاز المستخدم في عملية البحث ومدى كفائته. وواحدة من أفضل الطرق المستخدمة حالياً هي الـ Hashing . سنتعرف معاً في هذا المقال على : - تعريف بسيط عن الـ Hashing. - بعض الطرق المستخدمة في التعامل مع الـ Data في حالة التكرار في طريقة الـ Hashing. - طريقة التطبيق في لغة برمجة Java. تعريف بسيط عن الـ Hashing: هي طريقة تستخدم لحفظ الملفات وتخزينها ومن ثم البحث عنها بأسرع طريقة ممكنة. تتميز هذه الطريقة عن غيرها بأنها قادرة على جلب الـ Data بوقت سريع جدا وسط ملفات ومعلومات كبيرة نوعاً ما مقارنة بغيرها من الطرق. هذه الطريقة تتميز بحفظ الـ Data في جدول بحيث يتم إعطاء كل معلومة رقم مميز يتم حفظه داخل جدول من البيانات ومن ثم يتم الوصول إليه مباشرة في حال الحاجة إلى البحث عنه. هذه الطريقة تتميز بقلة الوقت اللازم للقيام بهذه العملية بحيث يمكنك مباشرة معرفة ما إذا كان الملف الجاري البحث عنه موجود أو غير موجود دون الحاجة للبحث عنه داخل الذاكرة بأكملها. مثال : لنفرض بأن لدينا الأرقام التالية : 1-7-4-8-9-5 . إذا قمنا بحفظ هذه الأرقام داخل Array بشكل عشوائي مباشر على سبيل المثال فعند البحث عن رقم 9 ستضطر إلى البحث داخل الـ Array بالكامل لمعرفة ما اذا كان الرقم 9 موجود أو لا. تخيل بأن لديك أكثر من الف رقم او أكثر!! يمكنك تخيل الوقت الطويل الذي ستحتاجه للقيام بعملية البحث هذه. لكن باستخدام Hashing يمكننا مثلا تخزين هذه الأرقام داخل Array ولكن ليس بشكل عشوائي كما في السابق وإنما بالنسبة لباقي قسمة العدد على حجم الـ Array على سبيل المثال. لنتفرض بأننا انشأنا Array بحجم 9 خلايا، الصف الأول يوضح الـ index والصف الثاني يوضح العدد الموجود داخل هذا الـ index 8 7 6 5 4 3 2 1 0 index value عند تعبئة هذه الـ Array سنستخدم العلاقة التالية : index = number % arrayLength. بمعنى آخر سيتم وضع الرقم داخل الخانة المعبرة عن باقي قسمة العدد على حجم الـ Array. فيكون الجدول بعد التعبئة بهذا الشكل 8 7 6 5 4 3 2 1 0 index 8 7 5 4 1 9 value لاحظ بأن الرقم 9 وضع بخانة رقم 0 لان باقي قسمة 9 على حجم الـ Array سيعطيك صفر. الآن بامكانك مباشرة الذهاب حين البحث عن رقم 9 إلى الرقم المستخرج من الدالة index = number % arrayLength وهو صفر لتعرف ما اذا كان الرقم 9 موجود أو لا وبالتالي توفير الكثير من الوقت. بعض الطرق المستخدمة في التعامل مع الـ Data في حالة التكرار في طريقة الـ Hashing: في المثال السابق لم نصادف أي تكرار في قيمة باقي قسمة الارقام على حجم الـ array ولكن كيف من الممكن حل مشكلة تكرار القيم في حال وقوعها ؟ هناك الكثير من الحلول التي من الممكن استخدامها لحل هذه المشكلة ولكن سيتم ذكر حل واحد في هذا المقال وسيتم الحديث عن باقي الحلول في مقالات قادمة إن شاء الله. 1 - Separate Chaining : هذه الطريقة تعتمد على بناء ArrayList بشكل خاص أو أي Structure آخر بشكل عام داخل كل خلية في الـ Hash Table الموضح في الأمثلة السابقة. لنأخذ هذه الأعداد على سبيل المثال : 12 – 17 – 29 – 6 – 30 – 31 – 4 – 8 ولنقم بتوزيعها علىHash Table بحجم 4 خلايا. في هذه الحالة يتم أولا معرفة الـ index المراد البحث داخله ومن ثم البحث داخل الـ ArrayList لمعرفة ما اذا كان الـ record المراد البحث عنه موجود أو غير موجود. طريقة التطبيق في لغة برمجة Java: هناك الكثير من الطرق لتطبيق فكرة الـ Hashing بواسطة الجافا ولكن سنقوم باتباع الطريقة الأسهل والأقل تعقيداً. سنحتاج إلى إنشاء كلاس List : List Class : عبارة عن كلاس سيتم عمل objects منه ويحتوي على الـ ArrayList المراد تخزين الـ Data فيها بالإضافة إلى Constructor. import java.util.ArrayList; public class List { private ArrayList<Integer> list = new ArrayList<Integer>(); //Constructor public List(){ } public ArrayList<Integer> getList(){ return list ; } } نحتاج أيضاً لإنشاء test class للتأكد من صحة الـ HashTable Test Class : في التيست كلاس سنقوم بإنشاء Array من كلاس List. لاحظ بأنه يجب عليك تعريف كل عناصر هذه الـ Array وبدون هذا التعريف سيظهر لك NullPointerException. //hash table List [] hashTable4 = new List [4] ; for (int i = 0 ; i<hashTable4.length ; i++) hashTable4[i] = new List(); ميثود print لطباعة الجدول public static void print(List [] array){ for (int i = 0 ; i<array.length ; i++) System.out.println("index "+i+" : "+array[i].getList().toString()); } ميثود getHashCode لمعرفة الـ index المناسب public static int getHashCode(int num){ return num%4 ; } نقوم بتخزين الأرقام في array لتسهيل عملية التصنيف والإدخال ومن ثم نقوم بإدخالها في object hashTable4 //numbers we want to add int [] listOfNumbers = {12,17,29,30,6,31,4,8}; //add Numbers to the hash table for (int i = 0 ; i<listOfNumbers.length ; i++){ int index = getHashCode(listOfNumbers[i]); hashTable4[index].getList().add(listOfNumbers[i]); } المخرج النهائي تم ترقية هذا الطرح المميز الى صفحة المقالات

عالم البرمجة

عالم البرمجة مقالات برمجة و دورات مجانية لإحتراف البرمجة هدفنا تبسيط البرمجة ونشرها بيد الكل بشكل ممتع ومتطور ومحدث بإستمرار لمواكبة جديد تطورات البرمجة الحديثة و المتقدمة بدون مقابل