مستوى المقال: مبتدئ

بسم الله الرحمن الرحيم 

لإرسال البيانات بين two activities يوجد عدة طرق, كـ shared Preference و database, لكن الطريقة المباشرة للإرسال هي عن طريق Intent بإستخدام الدوال الخاصة بها putExtra() والتي تتعامل مع جميع الـ Primitive types بطريقة مباشرة, لكن في حال أردنا أن نرسل object أو list of objects سنضطر لإستخدام ما يسمى بـ parcelable.

بداية ما هو الـ parcelable, هو interface يعمل كعمل الـ Serializable في جافا لكن مخصص لأندرويد, يعمل على تحويل الـ object إلى byte واستعادته كـ object مرة أخرى وفي هذه المقالة سنتكلم عنه وعن الية التعامل معه.

 

لنفرض أن لدينا Student class 

public class Student {
    private String name;
    private int id;

    public Student(String name, int id) {
        this.name = name;
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

}

ولدينا two Activities  الأولى MainActivity والثانية ShowStudentActivity, سنحاول نقل object واحد ومن ثم نقل list of object.

سنقسم هذه المقالة إلى ثلاثة أقسام:

  1. نقوم بعمل implementation لـ Parcelable
  2. نقوم بتمرير object
  3. نقوم بتمرير list of object 

لنبدأ بعمل implementation لـ Parcelable 

public class Student implements Parcelable {
    private String name;
    private int id;

    public Student(String name, int id) {
        this.name = name;
        this.id = id;
    }


    protected Student(Parcel in) {
        name = in.readString();
        id = in.readInt();
    }

    public static final Creator<Student> CREATOR = new Creator<Student>() {
        @Override
        public Student createFromParcel(Parcel in) {
            return new Student(in);
        }

        @Override
        public Student[] newArray(int size) {
            return new Student[size];
        }
    };

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }


    @Override
    public int describeContents() {
        return hashCode();
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeInt(id);
    }
}

بداية قمنا بعمل implementation لـ Parcelable, ثم override لدالتين, الأولى  describeContents والتي ترجع hash code الخاص بـ class, والثانية writeToParcel تحول data members إلى Parcel class, فنستخدم دوال Parcel class لتخزين بياناتنا, وهنا ينبغي الانتباه لترتيب data members عند الكتابة والقراءة حيث أنه في حال ما حدث تغيير في الترتيب قد يكون هناك crash لتطبيق.

 @Override
    public int describeContents() {
        return hashCode();
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeInt(id);
    }

بعد ذلك قمنا ببناء static field يسمى CREATOR وهو يستخدم من النظام لاستعادة البيانات - unparcel - , بداخلة دالتين أخرتين الأولى createFromParcel وهي تستخدم لإعادة بناء الـ object, ونلاحظ بداخلها استدعاء لـ constructor يستقبل parcel object لذلك سنقوم ببناء هذا constructor لاحقا, الدالة الثانية newArray وهي تستدعى في حال كان لديك مصفوفة - array - من الـ Parcelable ثم تستدعي الدالة الأولى createFromParcel عند بناء كل عنصر داخل هذه المصفوفة.

public static final Creator<Student> CREATOR = new Creator<Student>() {
        @Override
        public Student createFromParcel(Parcel in) {
            return new Student(in);
        }

        @Override
        public Student[] newArray(int size) {
            return new Student[size];
        }
    };

كما ذكرنا أن دالة createFromParcel تستدعي constructor يستقبل parcel object, ولهذا سنقوم ببنائه, وكما ذكرنا أيضا أن الترتيب مطلوب كما, قمنا بكتابة name أولا ثم id, سنقرأ بنفس الترتيب كالتالي

    protected Student(Parcel in) {
        name = in.readString();
        id = in.readInt();
    }

وبهذا يكون لدينا class نستطيع نقول بياناته بين الـ activity بشكل بسيط وسلس

سنقوم الان بنقل object واحد من هذا  class إلى Activity أخرى, كما هو معلوم التنقل بين الـ activites يكون دائما بإستخدام Intent, ولنقل البيانات نستخدم الدوال المساعدة في عملية النقل putExtra, هذه الدالة تستيع التعامل مع عدة انواع من المتغيرات - overloaded method - منها ال Parcelable, كما نلاحظ من داخل Intent class 

public @NonNull Intent putExtra(String name, Parcelable value) {
        if (mExtras == null) {
            mExtras = new Bundle();
        }
        mExtras.putParcelable(name, value);
        return this;
    }

لذا فيمكنا إرسال object كالتالي 

		Student student = new Student("Kream",16);
		Intent i = new Intent(this,ShowStudentActivity.class);
        i.putExtra("Key",student);
        startActivity(i);

واستقباله في  ShowStudentActivity بهذه الطريقة 

public class ShowStudentActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_show_student);
        Student student = getIntent().getParcelableExtra("Key");
	}
}

سنقوم الان بنقل ArrayList من نفس الـ class بنفس الطريقة

ArrayList<Student> students = new ArrayList<>();
        students.add(new Student("Ali",18));
        students.add(new Student("Ahmed",19));
        students.add(new Student("Adel",20));
        students.add(new Student("Khaled",23));
        students.add(new Student("Kream",16));

        Intent i = new Intent(this,ShowStudentActivity.class);
        i.putParcelableArrayListExtra("students", students);
        startActivity(i);

ثم استعادتها في الـ Activity الأخرى 

public class ShowStudentActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_show_student);
        ArrayList<Student> students = getIntent().getParcelableArrayListExtra("students");
    }
}

 

أندرويد ستديو له عدد من الإختصارات تسهل عمل implementation لـ Parcelable استعرضتها في هذا الفيديو: 

 

إلى هنا نكون قد وصلنا إلى نهاية هذه المقالة, والسلام عليكم ورحمة الله وبركاته 

 

المصادر: 

https://www.survivingwithandroid.com/2012/09/passing-data-between-activities-2.html

https://stackoverflow.com/questions/22037801/parcelable-what-is-newarray-for

https://developer.android.com/reference/android/os/Parcel.html

 

3zcs

1


اراء المستخدمين


لاتوجد تعليقات لعرضها .



انشئ حساب جديد او قم بتسجيل دخولك لتتمكن من اضافه تعليق جديد

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

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

سجل حسابك الجديد لدينا في الموقع بمنتهي السهولة .


سجل حساب جديد

تسجيل الدخول

هل تمتلك حساب بالفعل ؟ سجل دخولك من هنا.


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

Ads Belongs To 3zcs

  • بسم الله الرحمن الرحيم
     
    في إستمرارنا في الحديث عن أهم وأحدث مكتبات منصة الأندرويد سنتحدث اليوم عن واحدة من أشهر المكاتب المستخدمة مؤخراً في بعض التطبيقات المشهورة ومن ضمنها تطبيق 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); }  
    والنتيجة ..

     
    إلى هنا نكون قد وصلنا إلى نهاية الشرح الخاص بالمكتبة. وسنقوم في المواضيع القادمة بإستعراض المزيد من المكتبات الخاصة بمنصة الأندرويد بإذن الله.
    مستوى المقال: مبتدئ

    بواسطه Abdulrahman Hasan Agha , في

  • بسم الله الرحمن الرحيم
    السلام عليكم ورحمة الله وبركاته
     
    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]); }  
    المخرج النهائي 

     
    مستوى المقال: متوسط

    بواسطه Abdulrahman Hasan Agha , في

  • من أكثر الأسباب التي تؤدي الى زيادة معدل تقييم تطبيقك في متجر التطبيقات هو عن طريق سؤال المستخدم من داخل التطبيق !
     
    هناك طرق كثيره ومكتبات متنوعه لفعل ذلك ووظيفتها تكمن في سؤال المستخدم ما اذا اعجبهم التطبيق ويريد كتابة مراجعة له ، فيتم تحويلهم الى متجر التطبيقات
     
    ولكن اليوم سوف نتحدث عن API آبل اضافته في تحديث iOS 10.3
    يطلق عليه SKStoreReviewController
     
    الفكرة في هذا الـ API هو سؤال المستخدم من داخل التطبيق بدون الحاجة الى تحويلهم الى متجر التطبيقات !
    لكن العيب الوحيد هو عدم المقدرة على جعلهم يكتبوا مراجعة للتطبيق ، فقط يستطيعوا تقييم التطبيق عن طريق النجوم .
     
    وطبيعة الحال هناك عدة شروط :
     
         يجب الا يستخدم الـ API بداخل ViewDidLoad  أو ViewWillApper او غيرهم بدون وجود Logic ، فانت لا تريد أن يقيم المستخدم التطبيق بمجرد فتحه لأول مره وهو لم يستخدمه بعد !     يجب ان لا يستخدم الـ API بداخل Button او يتطلب أي Action من المستخدم ، قد يسبب ذلك في رفض تطبيقك اذا وضعته بداخل Action !    تذكر بأن كل شيء يتم تلقائيا اعتماداً على النظام ، من حيث ظهور رسالة طلب التقييم من عدمها ! لهذا السبب وجد الشرط السابق    أهم شرط هو رسالة التقييم سوف تظهر فقط 3 مرات في السنه لكل مستخدم .    اثناء فترة التطوير الرسالة سوف تظهر بشكل دائم     عند ارسال تطبيقك كنسخه تجربيه باستخدام TestFlight الرسالة لن تظهر ! ، بمعنى أخرى سوف تظهر فقط عند نشر تطبيقك في متجر التطبيقات .  
    ومن النصائح اذا اردت تقييم إيجابي للتطبيقك ، اطلب ظهور رسالة التقييم اذا فعل المستخدم امراً إيجابياً !
    ولا تقاطع المستخدم عند عمل مهمه معينه !! ، ولكن اسأله بعد الانتهاء من مهمة محددة
     
    لذا من الأمثله :
     اذا التطبيق متجر الكتروني اطلب رسالة التقييم بعد إتمام الشراء. اذا كان التطبيق لعبة ، اطلب رسالة التقييم بعد تحقيق سكور عالي في اللعبة أو بعد انتهاء من مرحلة او عدة مراحل في اللعبة . اسأله بعدد عدد مرات معينه من استخدام التطبيق وليس من أول مره !
    بحيث تسأل المستخدم لتقييم تطبيقك في وقت يكون في مزاج جيد !  
    اذا كيف يتم استخدامه ؟
     
    كما ذكرت سابقا الأمر بيد المطور
     
    بصورة مختصرة :
     
    الذي يتطلبه هو فقط استدعاء
    import StoreKit  
    في داخل الـ ViewController الذي يريد ظهور الرسالة عليه
     
    ومن ثم استخدام السطر التالي ، في أي مكان يرده المطور
    SKStoreReviewController.requestReview()  
    بصورة افضل هو حساب عدد مرات فتح التطبيق وإظهار الرسالة بعد عدد مرات محدده
    من دراسة قراءتها بأن افضل وقت هو بعد 5 مرات ويفضل بعد 10 مرات
    ولكن ليس أقل من ذلك ، بهذا الوقت بعد 5 مرات او 10 مرات تكون متأكد بأن المستخدم يستخدم التطبيق باستمرار .
     
    اذا كيف تحسب وقت استخدام المستخدم للتطبيق ؟
     
    عن طريق الاستفادة من UserDefaults
     
    اذا لا تعلم ماهو UserDefaults
    فهو طريقة لحفظ البيانات بشكل دائم ويستخدم مع البيانات البسيطة
    كحفظ رقم Integer مثلا
    او حفظ حالة الـ Switch اذا مفعل او مغلق الخ
     
     
    وبالتالي الفكرة هيا بوضع عدد محدد كمتغير وفي هذا المثال سوف نضع المدة 4
    لأننا نريد أن تظهر رسالة التقييم بعد خامس مره ، ولأنه العد يبدأ من 0
    فالرقم 4 = خامس مره
    0 , 1 , 2 , 3 , 4
     
    ونضع متغير اخر يحسب عدد مرات فتح التطبيق (ViewController)
    ومن ثم نعمل مقارنه اذا وصلت عدد المرات الى 5 مرات تظهر الرسالة
    اذا لم يوصل نزود قيمة المتغير ونحفظه في UserDefaults
     
     
     
    الكود يصبح بالشكل التالي :

     
    import UIKit import StoreKit class ViewController: UIViewController {     let minimumRunCount = 4     let userDefaultsKey = "minimumRunCountUserDefaultsKey"     override func viewDidLoad() {         super.viewDidLoad()         if ShowStoreReview() {             SKStoreReviewController.requestReview()         }     }        func ShowStoreReview() -> Bool {         let count = UserDefaults.standard.integer(forKey: userDefaultsKey)         if count >= minimumRunCount {             return true         } else {                        UserDefaults.standard.set((count + 1), forKey: userDefaultsKey)         }         return false     } }  
    اذا كما ذكرنا سابقا
     
    في البداية عرفنا الحد الادنى قبل ظهور الرسالة في  minimumRunCount  
    عرفنا الـ Key للـ userDefault
    يمكنك كتابة أي شي تريده
     
    بالنسبة الى UserDefaults.standard.integer
    القيمة سوف تكون 0 بشكل تلقائي عند استخدامه
    وهذا ما نريده
    لذا قيمة count سوف تكون 0  في اول مره
     
    ومن ثم عملنا مقارنة اذا كانت قيمة count اكبر او يساوي قيمة minimumRunCount  
    تظهر ترجع قيمة true وبالتالي تظهر الرسالة
     
    اذا لم تكون تساوي او اكبر من قيمة  minimumRunCount  
     
    نزود واحد على قيمة count ونحفظها في userDefault
     
    لذا في المره الثانية يفتح فيها التطبيق تكون قيمة count بـ 1
    عوضاً عن 0
     
    وهكذا
     
    وعندما توصل لقيمة 4
    تظهر الرسالة
     
    صورة الرسالة :

     

     
    هذه من الأمثلة التي تستطيع الاستفادة منها
    تستطيع تحسينها حسب احتياجك
     
    اريد أن أوضح نقطة اخيره وهيا الـ SKStoreReviewController
    فقط يظهر لمستخدم نظام 10.3 فأعلى
     
    لذا تستطيع إضافة السطر التالي في ViewDidLoad
     

     
            if #available(iOS 10.3, *) {             SKStoreReviewController.requestReview()                     } else {  وهنا ضع الطريقة القديمة لتحويله الى متجر التطبيقات//         }  
    فيصبح الكود  ViewDidLoad النهائي بالشكل التالي :

     
        override func viewDidLoad() {         super.viewDidLoad()         if ShowStoreReview() {             if #available(iOS 10.3, *) {                 SKStoreReviewController.requestReview()                          } else {             }         }     }  
     
    واخيراً اريد أن اذكر بأن هناك مطور جرب استخدام SKStoreReviewController
    في تطبيقه ، وادى ذلك الي حصوله على 200 تقييم في خلال 8 أيام !
     
    لذا ينصح بشده باستخدام SKStoreReviewController في تطبيقاتكم
     
    فهناك عدد كبير من التطبيقات في المتجر لا تملك أي تقييم !
     
    لذلك آبل إضافة هذا الـ API في نظامها
     
     
    مستوى المقال: محترف

    بواسطه باسل العمودي , في

  • السلام عليكم ورحمة الله وبركاته
    الدرس الثامن : بناء جهاز لقياس درجة الحرارة والرطوبة .

     
    نبذة عن الدرس :
    من خلال هذا الدرس سوف نقوم بعمل جهاز قادر على قراءة درجة الحرارة وايضا نسبة الرطوبة وذلك من خلال مستشعر الحرارة والرطوبة , ويتم عرضها على الشاشة الكريستالية , المقاومة المتغيرة لتغيير سطوع الشاشة .
     
    متطلبات المشروع :
    أردوينو اونو (Arduino Uno)
    مستشعر الحرارة والرطوبة (DHT11 or DHT22)
    مقاومة متغيرة (variable resistor)
    الشاشة كريستالية(lcd 2*16 )
    أسلاك توصيل (Jumper wire) .
    لوحة تجارب (Breadborad)
     
    الدائرة الكهربائية :
     

     
    آلية عمل حساس الرطوبة والحرارة (DHT11) :
    لدى هذا الحساس القدرة على قراءة درجة الحرارة والرطوبة في البيئة المحيطة به .
     
    الفرق بين DHT11 و DHT22 :

     
    معلومات عن الشاشة الكربستالية :
    يوجد العديد من مقاسات الشاشات الكريستالية مثل 2*16 و 4*20  وفي هذا الدرس استخدمنا مقاس 2*16   وتعني إن الشاشة قادرة على عرض سطرين وكل سطر يحتوي على 16 حرف أو رمز.
     
    الكود البرمجي :
    في البداية نحتاج إلى إضافة مكتبة DHT  والتي تسهل علينا التعامل مع حساس الرطوبة والحرارة.
    أولا : نقوم بتحميل الملف المضغوط  .
    DHT library.zip
    ثانيا :نقوم بتشغيل برنامج Arduino IDE .
    ثالثا :نذهب إلى الشيفرة البرمجية ---< إدراج مكتبة  ---< أضف مكتبة ---< قم بإختيار الملف المضغوط .
    رابعا : قم بإغلاق البرنامج ثم إعد تشغيله .
     
    #include <LiquidCrystal.h> #include "DHT.h" LiquidCrystal lcd(8, 9, 10, 11, 12, 13); int DHTPIN =3; #define DHTTYPE DHT11 DHT dht(DHTPIN, DHTTYPE); void setup() { dht.begin(); lcd.begin(16, 2); } void loop() { int hum = dht.readHumidity(); int tem = dht.readTemperature(); lcd.setCursor(0, 0); lcd.print("Humidity: "); lcd.print(hum); lcd.setCursor(0, 1); lcd.print("Temperature: "); lcd.print(tem); delay(500); }  
    بعد رفع الكود للاردوينو أضف أحد مصادر الطاقة المذكورة في الدرس السابق لتنقل بها إلى أي مكان .

     
    شرح الكود البرمجي :
    #include <LiquidCrystal.h> تعريف بالمكتبة الخاصة بالشاشة الكريستالية
    #include "DHT.h" المكتبة الخاصة لمستشعر الحرارة والرطوبة
     
    LiquidCrystal lcd(8, 9, 10, 11, 12, 13); دالة تحمل أرقام المنافذ الاردوينو المتصلة بالشاشة الكريستالية .
     
    #define DHTTYPE DHT11 تعريف نوع مستشعر الحرارة والرطوبة المستخدم في درسنا وهو DHT11.
    لو كنت تستخدم نوع DHT22  فقط قم بإستبدال النوع في الكود السابق .
    DHT dht(DHTPIN, DHTTYPE); دالة لتهيئة الحساس وتحمل متغيرين متغير يحمل رقم المنفذ واخر نوع المستشعر .
     
    lcd.begin(16, 2); تعريف بمقاس الشاشة المستخدمة وهو 2*16 .
     
    int h = dht.readHumidity(); متغير لقراءة نسبة الرطوبة من المستشعر .
    int t = dht.readTemperature(); متغير لقراءة درجة الحرارة من المستشعر .
    lcd.setCursor(0, 0); هذه الدالة لجعل المؤشر في بداية السطر الاول.
    lcd.setCursor(0, 1); هذه الدالة لجعل المؤشر في بداية السطر الثاني.
     
    حل تحدي السابق :
    التحدي مذكور في هذا المقال

    يمكنك تحميل حل التحدي من هنا :
    waduino_3.ino
     
    تحدي بسيط :
    قم بجعل الشاشة تعرض فقط درجة الحرارة ثم تعرض درجة الرطوبة فقط لمدة 500 ملي ثانية لكل منهما .
    مستوى المقال: مبتدئ

    بواسطه وضاح العوني , في

  • بسم الله الرحمن الرحيم
    في هذا الموضوع سنقوم بمناقشة المواضيع التالية في التعامل مع صفوف الجداول :
    طريقة إخفاء الصفوف الفارغة في الجدول . طريقة إظهار الصفوف المطلوبة و إخفاء بعض الصفوف ( عن طريق تغيير ارتفاع الصف) .  
    ١- طريقة إخفاء الصفوف الفارغة في الجدول :
    سنقوم بعمل جدول وإضافة بعض القيم فيه ( بافتراض بأن لديك معرفه مسبقة لطريقة عمل الجداول) وهذا الجدول الذي قمت بعمله :

     
    نلاحظ بالجدول السابق وجود صفوف فارغة وقد تكون مزعجة نوعاً ما وقد نرغب في إخفائها :
     
    وحتى تقوم بإخفائها كل ماعليك هو اتباع إحدى الطرق التالية : 
    (لنفترض بأن TableView لدينا اسمه ColorTable)
     
    الطريقة الأولى : إخفاء الـ separator :
    باستخدام السطر التالي بداخل دالة viewDidLoad :
    ColorTable.separatorStyle = .none  
    أو من خلال Interface Builder للجدول ColorTable :

     
    وستكون النتيجة كالتالي :
     

     
    بالنسبة لهذه الطريقة فكما نلاحظ فقد تم إخفاء ال Separetor للجدول كامل .
     
    الطريقة الثانية : بإضافة Footer وجعل الارتفاع له يساوي 0 :
    وذلك بإضافة السطر التالي بداخل داله  viewDidLoad :
    ColorTable.tableFooterView = UIView(frame: .zero)  
    وستكون النتيجة كالتالي :
     

    الطريقة الثالثة : بإضافة UIView كـ Footer وذلك كالتالي :
     
    ١- قم بإضافة UIView أسفل الجدول كما في الصوره ( قمت بتغيير لون UIview للون الأزرق حتى تتضح لكم الفكرة) :


     
    ٢- بعد إضافة ال UIview قم بتغيير الارتفاع له بحيث يكون مساوياً لـ 0 وذلك من خلال Interface Builder:
     

     
    والنتيجة كالتالي :
     

     
    ٢- طريقة إظهار الصفوف المطلوبة و إخفاء بعض الصفوف :
    هذه الطريقة نستطيع الاستفاده منها في حاله أردنا إخفاء بعض الصفوف بشكل مؤقت ( وليست حذف للصفوف) وذلك عند تنفيذ أو حدوث أمر معين ، مثال على ذلك لو أردت إنشاء تقويم Calendar
    وتحت هذا التقويم ترغب بعرض الأحداث Events لذلك الشهر وإخفاءها في الشهور الأخرى :

     
    الفكرة هنا تكون من خلال تغيير الارتفاع للصف المطلوب وجعله مساوياً لل 0 ، ويتم عمل ذلك من خلال استخدام ال function التالية :
    public func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { } سنقوم بتطبيق الفكره على جدولنا ColorTable  ونقوم بـ (إخفاء) اللون الأصفر من الجدول :
    النقاط المهمة لذلك هي :
    ١- انشاء الدالة التالية للتعامل مع ارتفاع صفوف الجدول :
    public func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return } ٢- تعريف متغير من نوع CGFloat و تكون قيمته = 0.0 و هو يدل على قيمة الارتفاع للصف :
    var rowHeight:CGFloat = 0.0  
    ٣- نستخدم ال if - else وذلك للتحقق من الشرط الذي سيتم إخفاء الصفوف المطلوبة عند حدوثه وتحقق الشرط ففي حاله تحقق الشرط ستكون قيمة المتغير الذي تم تعريفه = 0.0
    وإذا لم يتحقق سنجعل ارتفاع الصفوف مثلاً = 60.0 :
     
    let RC = "Yellow" let cell1 = ColorTable.dequeueReusableCell(withIdentifier: "cell") cell1?.textLabel?.text = list[indexPath.row] if (RC == cell1?.textLabel?.text) { rowHeight = 0.0 }else { rowHeight = 60.0 } return rowHeight  
    الكود النهائي بداخل الدالة السابقة سيكون كالتالي :
     
    public func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { var rowHeight:CGFloat = 0.0 let RC = "Yellow" let cell1 = ColorTable.dequeueReusableCell(withIdentifier: "cell") cell1?.textLabel?.text = list[indexPath.row] if (RC == cell1?.textLabel?.text) { rowHeight = 0.0 }else { rowHeight = 60.0 } return rowHeight }  
    وبذلك سيتم (إخفاء) اللون Yellow من القائمة :

     
    هذا مثال بسيط جداً لإخفاء الصفوف بشكل مؤقت دون حذفها فقط حتى تصل الفكرة لكم ..
     
    * خلاصة ماتعلمناه اليوم :
    طريقة إخفاء الصفوف الفارغة في الجدول : من خلال ٣ طرق :
    ١- إخفاء ال Separator :
    ColorTable.separatorStyle = .none ٢-  إضافة Footer وجعل الارتفاع له = 0 :
    ColorTable.tableFooterView = UIView(frame: .zero) ٣- إضافة UIView كـ Footer وجعل ارتفاعه =0 .
     
    طريقة إظهار الصفوف المطلوبة و إخفاء بعض الصفوف ( عن طريق تغيير ارتفاع الصف) . وذلك باستخدام الدالة التالية التي من خلالها نستطيع التحكم بقيمة ارتفاع الصفوف :
    public func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { }  
    وبذلك نكون قد انتهينا اتمنى أن أكون قد وفقت في الشرح وتبسيط المعلومة لكم
    وأرحب بأي مناقشه وأي سؤال يخص الموضوع 🌹
    مستوى المقال: مبتدئ

    بواسطه ساره علي , في

  • Ads Belongs To 3zcs

    عالم البرمجة

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