المحتوى عن 'قائمة العرض'.



مزيد من الخيارات

  • ابحث بالكلمات المفتاحية

    أضف وسومًا وافصل بينها بفواصل ","
  • ابحث باسم الكاتب

نوع المُحتوى


التصنيفات

  • التخطيط وسير العمل
  • التمويل
  • فريق العمل
  • دراسة حالات
  • نصائح وإرشادات
  • التعامل مع العملاء
  • التعهيد الخارجي
  • التجارة الإلكترونية
  • الإدارة والقيادة
  • مقالات ريادة أعمال عامة

التصنيفات

  • PHP
    • Laravel
    • ووردبريس
  • جافاسكريبت
    • Node.js
    • jQuery
    • AngularJS
    • Cordova
  • HTML5
  • CSS
    • Sass
    • إطار عمل Bootstrap
  • SQL
  • سي شارب #C
    • منصة Xamarin
  • بايثون
    • Flask
    • Django
  • لغة روبي
    • إطار العمل Ruby on Rails
  • لغة Go
  • لغة جافا
  • لغة Kotlin
  • برمجة أندرويد
  • لغة Swift
  • لغة R
  • لغة TypeScript
  • سير العمل
    • Git
  • صناعة الألعاب
    • Unity3D
  • مقالات برمجة عامة

التصنيفات

  • تجربة المستخدم
  • الرسوميات
    • إنكسكيب
    • أدوبي إليستريتور
    • كوريل درو
  • التصميم الجرافيكي
    • أدوبي فوتوشوب
    • أدوبي إن ديزاين
    • جيمب
  • التصميم ثلاثي الأبعاد
    • 3Ds Max
    • Blender
  • مقالات تصميم عامة

التصنيفات

  • خواديم
    • الويب HTTP
    • قواعد البيانات
    • البريد الإلكتروني
    • DNS
    • Samba
  • الحوسبة السّحابية
    • Docker
  • إدارة الإعدادات والنّشر
    • Chef
    • Puppet
    • Ansible
  • لينكس
  • FreeBSD
  • حماية
    • الجدران النارية
    • VPN
    • SSH
  • مقالات DevOps عامة

التصنيفات

  • التسويق بالأداء
    • أدوات تحليل الزوار
  • تهيئة محركات البحث SEO
  • الشبكات الاجتماعية
  • التسويق بالبريد الالكتروني
  • التسويق الضمني
  • استسراع النمو
  • المبيعات

التصنيفات

  • إدارة مالية
  • الإنتاجية
  • تجارب
  • مشاريع جانبية
  • التعامل مع العملاء
  • الحفاظ على الصحة
  • التسويق الذاتي
  • مقالات عمل حر عامة

التصنيفات

  • الإنتاجية وسير العمل
    • مايكروسوفت أوفيس
    • ليبر أوفيس
    • جوجل درايف
    • شيربوينت
    • Evernote
    • Trello
  • تطبيقات الويب
    • ووردبريس
    • ماجنتو
  • أندرويد
  • iOS
  • macOS
  • ويندوز

التصنيفات

  • شهادات سيسكو
    • CCNA
  • شهادات مايكروسوفت
  • شهادات Amazon Web Services
  • شهادات ريدهات
    • RHCSA
  • شهادات CompTIA
  • مقالات عامة

أسئلة وأجوبة

  • الأقسام
    • أسئلة ريادة الأعمال
    • أسئلة العمل الحر
    • أسئلة التسويق والمبيعات
    • أسئلة البرمجة
    • أسئلة التصميم
    • أسئلة DevOps
    • أسئلة البرامج والتطبيقات
    • أسئلة الشهادات المتخصصة

التصنيفات

  • ريادة الأعمال
  • العمل الحر
  • التسويق والمبيعات
  • البرمجة
  • التصميم
  • DevOps

تمّ العثور على 1 نتيجة

  1. لدى برمجة تطبيقات أندرويد، فإنّه وفي العديد من الأحيان نحتاج أن نعرض مجموعة من العناصر معًا في النشاط أمام المستخدم كعرض جهات الاتصال مثلًا أو الرسائل حيث يمكن للمستخدم أن يتصفحها ويتنقل بينها سريعًا، كما يمكنه الضغط على أي منها فيتم عرض المزيد من المعلومات عن هذا العنصر. نستخدم عنصر الواجهة ListView أو قائمة العرض للقيام بما سبق والذي يتيح عرض أكثر من عنصر في قائمة واحدة، ويتم إضافة العناصر إلى قائمة العرض تلقائيًا باستخدام كائن من الصنف Adapter والذي يستطيع جلب المحتوى من مصفوفة من البيانات أو قاعدة بيانات. ويُعتبر الصنف Adapter بمثابة حلقة الوصل بين عنصر الواجهة الخاص بقائمة العرض ومصدر البيانات المستخدم حيث يقوم بتشكيلها بالشكل المطلوب لتُمثل عنصرًا من عناصر القائمة. يوجد عدة أصناف فرعية من الصنف Adapter كي تُستخدم مع أشكال البيانات المختلفة مثل: ArrayAdapter Base Adapter SimpleCursorAdapter قوائم العرض مع كائن من ArrayAdapter تطبيق 1 يُستخدم ArrayAdapter عندما يكون المصدر البيانات هو قائمة أو مصفوفة. سنقوم بصنع تطبيق يعرض مجموعة من النصوص في قائمة العرض، لذا سنقوم بعمل مشروع جديد في Android Studio. ولتكوين قائمة العرض نقوم بالخطوات التالية: نضع عنصر من ListView في ملفات الواجهة ونشير له بـ Id مميز، ثم نربط هذا العنصر بشيفرات الجافا باستخدام التابع ()findViewById. <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <ListView android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/listview"/> </LinearLayout> نصنع ملف واجهة جديد يكون الجذر له عنصر من النوع TextView. ولصنع واجهة استخدام جديدة نضغط على المجلد layout المتواجد داخل res بالزر الأيمن ثم نختار: new > XML > Layout XML File ونسميه row_item. وبداخل هذا الملف نجعل عنصر الجذر من النوع TextView ونغير حجم النص الذي سيُكتب بداخله إلى 20sp ونجعل الخاصية padding لها قيمة 20sp. لاحظ أن الخاصية padding تعني الحشو أي ضع فراغًا مقداره 20sp حول النص المكتوب داخل TextView في كل الاتجاهات. <?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:textSize="20sp" android:padding="20sp"/> في شيفرة الجافا نبني كائنًا جديدًا من الصنف ArrayAdapter ونمرر لدالة البناء الخاصة به ثلاث عناصر: العنصر الأول هو this والذي يعبر عن السياق الخاص بالنشاط المكوّن للقائمة. العنصر الثاني هو TextView الذي سيُعرض فيه كل نص في القائمة. العنصر الأخير هو المصفوفة الخاصة بالنصوص التي سيتم عرضها في قائمة العرض. ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,R.layout.row_item,colorsArray); نستدعي التابع ()setAdapter باستخدام الكائن الخاص بالـ ListView ونمرر له الـ Adapter الذي قمنا بصنعه ليصبح التطبيق النهائي: package apps.noby.listviewexample; import android.app.Activity; import android.os.Bundle; import android.widget.ArrayAdapter; import android.widget.ListView; public class MainActivity extends Activity { private ListView lv; String [] colorsArray = {"Red","Yellow","Blue","Orange","Black","Green","Brown","Grey"}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); lv =(ListView) findViewById(R.id.listview); ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,R.layout.row_item,colorsArray); lv.setAdapter(adapter); } } ثم نقوم بتجربة التطبيق على المحاكي لنتأكد من عمله بالشكل المطلوب. تطبيق 2 في هذا التطبيق بدلًا من استخدام المصفوفة لتخزين البيانات سنقوم باستبدالها بقائمة من النوع ArrayList مع الحفاظ على باقي ملفات الواجهة السابقة لتصبح الشفرة النهائية بعد التعديل: package apps.noby.listviewexample; import android.app.Activity; import android.os.Bundle; import android.widget.ArrayAdapter; import android.widget.ListView; import java.util.ArrayList; public class MainActivity extends Activity { private ListView lv; ArrayList<String> colorsList; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); colorsList = new ArrayList<String>(); colorsList.add("Red"); colorsList.add("Yellow"); colorsList.add("Blue"); colorsList.add("Orange"); colorsList.add("Black"); colorsList.add("Green"); colorsList.add("Brown"); colorsList.add("Grey"); lv =(ListView) findViewById(R.id.listview); ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,R.layout.row_item,colorsList); lv.setAdapter(adapter); } } وبتجربة هذا التطبيق على المحاكي نجد أنه يقوم بنفس الوظيفة السابقة، ولكن استخدامنا للقائمة بدلًا من المصفوفة يتيح لنا القدرة على إضافة أو إزالة العناصر بسهولة وهو ما لا تسمح به المصفوفة. استجابة عناصر القائمة عند الضغط عليها تطبيق 3 سنقوم في هذا التطبيق بجعل العناصر المتواجدة بقائمة العرض تستجيب عند الضغط عليها، فكما ذكرنا سابقًا أننا استخدمنا كائنًا من نوع ArrayList لسهولة إضافة أو إزالة العناصر منه، سنقوم الآن بتطبيق هذا المفهوم حيث سنقوم بإزالة العنصر من القائمة عند الضغط عليه. كما نستخدم التابع ()setOnClickListener مع الزر للاستجابة عند الضغط عليه سنقوم هنا باستخدام التابع ()setOnItemClickListener للاستجابة عند الضغط على عناصر القائمة، وبداخله نجد الدالة ()onItemClick وبها العناصر التالية: parent وهو يُعبر عن الأب الخاص بالعنصر الذي تم الضغط عليه وفي هذا المثال فهو ListView. view ويُمثل العنصر نفسه الذي تم الضغط عليه أي TextView. position ويُعبر عن رقم العنصر في القائمة. Id وهو رقم مميز يتم تحديده لعنصر الواجهة الذي يعرض النص. لتصبح الشفرة النهائية الخاصة بهذا التطبيق: package apps.noby.listviewexample; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.ListView; import java.util.ArrayList; public class MainActivity extends Activity { private ListView lv; ArrayList<String> colorsList; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); colorsList = new ArrayList<String>(); colorsList.add("Red"); colorsList.add("Yellow"); colorsList.add("Blue"); colorsList.add("Orange"); colorsList.add("Black"); colorsList.add("Green"); colorsList.add("Brown"); colorsList.add("Grey"); lv =(ListView) findViewById(R.id.listview); final ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,R.layout.row_item,colorsList); lv.setAdapter(adapter); lv.setOnItemClickListener(new ListView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { String item = (String) parent.getItemAtPosition(position); colorsList.remove(item); adapter.notifyDataSetChanged(); } }); } } بداخل التابع ()onItemClick نستخدم الكائن parent لاستدعاء التابع ()getItemAtPosition وتمرير رقم العنصر له حتى يُعيد لنا النص المخزن في ذلك العنصر. ثم بعد ذلك نقوم بإزالة العنصر من القائمة واستدعاء التابع ()notifyDataSetChanged باستخدام الكائن adapter وذلك لتنفيذ التغيير الذي حدث على عناصر قائمة العرض. الآن نقوم بتجربة التطبيق على المحاكي، نجد أنه عند الضغط على أحد العناصر تختفي في الحال. يمكنك تطوير هذا التطبيق لجعله يستدعي نشاطًا جديدًا عند الضغط على أحد العناصر. البحث داخل عناصر قائمة العرض تطبيق 4 في هذا التطبيق سنقوم بالبحث عن العناصر التي تحتوي على نص معين وعرضها هي فقط وذلك لسهولة الوصول لعنصر محدد. كتطوير على التطبيق السابق سنقوم بإضافة الخاصية Orientation للعنصر LinearLayout في ملف activity_main.xml. android:orientation="vertical" ثم نضيف عنصر EditText قبل ListView وهو العنصر الذي سيقوم المستخدم بالتفاعل معه وكتابة النص الذي سيبحث عنه داخل قائمة العرض. <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:drawableLeft="@drawable/ic_search_black_24dp" android:hint="Search..." android:id="@+id/searchedittext"/> <ListView android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/listview"/> </LinearLayout> ولإضافة صورة يسار العنصر EditText نستخدم الخاصية drawableLeft ثم نضيف الصورة من المجلد drawable. الآن سنربط هذا العنصر بالشيفرة الرئيسية للتطبيق ثم نستدعي التابع ()addTextChangedListener والذي يستجيب لأي تغير يحدث داخل EditText، حيث يوفر ثلاث دوال: searchET.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { adapter.getFilter().filter(s); } @Override public void afterTextChanged(Editable s) { } }); كل دالة تختص بفترة محددة، وهنا نهتم بالدالة ()onTextChanged والتي يتم استدعاؤها لحظة تغير النص المكتوب بداخل EditText. أخيرًا لتنقية القائمة وترك فقط العناصر التي يتم البحث عنها نستدعي التابع ()getFilter باستخدام الكائن الخاص بالـ ArrayAdapter ثم نستدعي بعده مباشرة التابع ()filters ونمرر له المتغير s الذي يحمل بداخله النص الذي قمنا بكتابته في EditText. لتصبح الشيفرة النهائية كما يلي: package apps.noby.listviewexample; import android.app.Activity; import android.os.Bundle; import android.text.Editable; import android.text.TextWatcher; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.EditText; import android.widget.ListView; import java.util.ArrayList; public class MainActivity extends Activity { private ListView lv; private ArrayList<String> colorsList; EditText searchET; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); colorsList = new ArrayList<String>(); colorsList.add("Red"); colorsList.add("Yellow"); colorsList.add("Blue"); colorsList.add("Orange"); colorsList.add("Black"); colorsList.add("Green"); colorsList.add("Brown"); colorsList.add("Grey"); searchET = (EditText) findViewById(R.id.searchedittext); lv =(ListView) findViewById(R.id.listview); final ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,R.layout.row_item,colorsList); lv.setAdapter(adapter); lv.setOnItemClickListener(new ListView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { String item = (String) parent.getItemAtPosition(position); colorsList.remove(item); adapter.notifyDataSetChanged(); } }); searchET.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { adapter.getFilter().filter(s); } @Override public void afterTextChanged(Editable s) { } }); } } بعد ذلك نقوم بتجربة التطبيق على المحاكي لنتأكد من عمله كما ينبغي. تخصيص واجهة قائمة العرض تطبيق 5 تُتيح قائمة العرض ميزة تخصيص العناصر، فيمكن صنع قائمة عرض تتكون من نص وصورة في كل صف بدلًا من نص فقط كالأمثلة السابقة. في هذا التطبيق سنقوم بصنع تطبيق يعرض قائمة كما في الصورة التالية، وعند الضغط على العنصر يعرض نشاطًا جديدًا. من Android Studio نقوم بعمل مشروع جديد، نبدأ أولًا بصنع واجهة المستخدم الرئيسية والمتكونة من ListView. <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <ListView android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/listview"/> </LinearLayout> نصنع ملف واجهة جديد يُعبر عن الصف ويدعى row_item وبداخله نكون الشكل المطلوب للصف في قائمة العرض كما في المخطط التالي: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/blue" android:id="@+id/imgview"/> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Title Example" android:textSize="20sp" android:textStyle="bold" android:id="@+id/titleview"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Description Example" android:textSize="15sp" android:id="@+id/descview"/> </LinearLayout> </LinearLayout> يتكون الصف في قائمة العرض من صورة، نص العنوان ونص الوصف لذا سنصنع صنفًا جديدًا يحتوي بداخله على هذه المعلومات الخاصة بالعنصر ثم نكوّن مصفوفة أو قائمة من هذه الصنف. لصنع صنفًا جديدًا من داخل المجلد java اضغط بالزر الأيمن على اسم الحزمة الخاصة بالمشروع واختر New > Java Class ثم اكتب اسم الصنف الجديد ListItem. بداخل هذا الصنف الجديد نعرّف ثلاث متغيرات، package apps.noby.customlistviewexample; public class ListItem { public int imgSrc; public String title; public String desc; } في ملف MainActivity.java نكتب الشيفرة الخاصة بالمشروع. أولًا نصنع كائنًا من الصنف ArrayList ليكون هو مصدر البيانات التي ستُعرض في قائمة العرض، ويتكون الكائن من مجموعة من الكائنات الأخرى من الصنف ListItem لكل منها صورة خاصة به، نص العنوان ونص الوصف. private ArrayList<ListItem> items; String [] colorsArray = {"Red","Yellow","Blue","Black","Green","Brown","Grey"}; int [] colorsImage = {R.drawable.red,R.drawable.yellow,R.drawable.blue,R.drawable.black,R.drawable.green,R.drawable.brown,R.drawable.grey}; items = new ArrayList<ListItem>(); for(int i=0;i<colorsArray.length;i++){ ListItem item= new ListItem(); item.imgSrc=colorsImage; item.title = colorsArray; item.desc = colorsArray + " Color!"; items.add(item); } ثانيًا بعد ذلك نصنع الكائن الخاص بالـ ArrayAdapter ولكننا لا يمكننا استخدام الصنف الأساسي من ArrayAdapter حيث أنه لا يعمل إلا مع نص واحد فقط ولن يمكننا تغيير الصورة أو النص الثاني، لذا ينبغي علينا صنع الصنف ArrayAdapter الخاص بنا. بنفس الطريقة السابقة نصنع صنف جديد من داخل المجلد Java، اضغط بالزر الأيمن على اسم الحزمة الخاصة بالمشروع واختر New > Java Class ثم اكتب اسم الصنف الجديد CustomArrayAdapter. يجب أن يرث هذا الصنف الفرعي من الصنف الأساسي ArrayAdapter. public class CustomArrayAdapter extends ArrayAdapter ثم نقوم بكتابة دالة البناء الخاصة بهذا الصنف ونستدعى في بدايتها دالة البناء الخاصة بالصنف الأب، وتحتاج دالة البناء لكائن من الصنف Context وهو السياق الذي سيعمل فيه هذا الـ Adapter أي النشاط الذي سيعمل به، كما يحتاج إلى واجهة المستخدم التي تكون شكل الصف، وقائمة البيانات التي سيتم عرضها. private Context con; private ArrayList<ListItem> data; private int resLayout; public CustomArrayAdapter(Context context, int resource, List objects) { super(context, resource,objects); con = context; resLayout = resource; data =(ArrayList) objects; } يوجد تابع داخل ArrayAdapter يُدعى ()getCount ويتم استخدامه بشكل تلقائي عندما يريد الكائن الذي سنصنعه من CustomArrayAdapter معرفة عدد العناصر التي سيقوم بعرضها داخل قائمة العرض، لذا فهو يستدعي التابع ()size باستخدام الكائن الخاص بالبيانات. @Override public int getCount() { return data.size(); } وبداخل الصنف الأب ArrayAdapter يوجد أيضًا تابع يُدعى ()getView، وهو المسؤول عن تكوين الشكل الخاص بالصف المعروض في قائمة العرض لذا كي نصنع الصف الخاص بنا يجب علينا تعديل هذا التابع. ولتعديل التابع نقوم بكتابته بطريقتنا الخاصة كما يلي: @Override public View getView(int position, View convertView, ViewGroup parent) { inflater = (LayoutInflater) con.getSystemService(Context.LAYOUT_INFLATER_SERVICE); View rootView = inflater.inflate(resLayout,null); ImageView img = (ImageView) rootView.findViewById(R.id.imgview); TextView title = (TextView) rootView.findViewById(R.id.titleview); TextView desc = (TextView) rootView.findViewById(R.id.descview); ListItem item = data.get(position); img.setImageResource(item.imgSrc); title.setText(item.title); desc.setText(item.desc); return rootView; } داخل التابع نبدأ أولًا بطلب الكائن LayoutInflater وهو المسؤول عن تحويل ملف XML إلى كائن من النوع View وللحصول عليه نستخدم التابع ()getSystemService والذي يوفر مجموعة مختلفة من الخدمات منها الكائن LayoutInflater، ويتم تحديد نوع الخدمة المطلوبة عن طريق تمرير ثابت محدد وفي هذه الحالة هو Context.LAYOUT_INFLATER_SERVICE. بعد الحصول على هذا الكائن نستدعي التابع ()inflate ونمرر له ملف XML الذي نرغب في تحويله إلى كائن في ملف الجافا. inflater = (LayoutInflater) con.getSystemService(Context.LAYOUT_INFLATER_SERVICE); View rootView = inflater.inflate(resLayout,null); الآن لربط عناصر الواجهة بالشيفرة نستخدم التابع ()findViewById ولكنا هنا لا نستطيع استخدامه مباشرة كما كنا نفعل عند وجودنا داخل شيفرة النشاط، لذا يجب أن نستدعي التابع باستخدام الكائن من الصنف View الذي حصلنا عليه من التابع ()inflate. ImageView img = (ImageView) rootView.findViewById(R.id.imgview); TextView title = (TextView) rootView.findViewById(R.id.titleview); TextView desc = (TextView) rootView.findViewById(R.id.descview); أخيرًا نأتي بالبيانات المناسبة للعنصر من داخل الـ ArrayList ثم نضع بداخل كل عنصر من عناصر الواجهة البيانات الخاصة به. ListItem item = data.get(position); img.setImageResource(item.imgSrc); title.setText(item.title); desc.setText(item.desc); لتصبح الشيفرة النهائية داخل الصنف CustomArrayAdapter: package apps.noby.customlistviewexample; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.TextView; import java.util.ArrayList; import java.util.List; public class CustomArrayAdapter extends ArrayAdapter { private Context con; private ArrayList<ListItem> data; private LayoutInflater inflater; private int resLayout; public CustomArrayAdapter(Context context, int resource, List objects) { super(context, resource,objects); con = context; data =(ArrayList) objects; resLayout = resource; } @Override public int getCount() { return data.size(); } @Override public View getView(int position, View convertView, ViewGroup parent) { inflater = (LayoutInflater) con.getSystemService(Context.LAYOUT_INFLATER_SERVICE); View rootView = inflater.inflate(resLayout,null); ImageView img = (ImageView) rootView.findViewById(R.id.imgview); TextView title = (TextView) rootView.findViewById(R.id.titleview); TextView desc = (TextView) rootView.findViewById(R.id.descview); ListItem item = data.get(position); img.setImageResource(item.imgSrc); title.setText(item.title); desc.setText(item.desc); return rootView; } } نعود مجددًا إلى ملف MainActivity.java لإنشاء كائن من الصنف الذي صنعناه: private CustomArrayAdapter adapter; adapter = new CustomArrayAdapter(this,R.layout.row_item,items); ثالثًا نربط قائمة العرض بالشيفرة ونستدعي التابع ()setAdapter: private ListView customLV; customLV = (ListView) findViewById(R.id.listview); customLV.setAdapter(adapter); بهذا نكون قد انتهينا من تكوين قائمة العرض المطلوبة، يتبقى أن نجعل عناصر قائمة العرض تستجيب عند الضغط عليها وتقوم بفتح نشاط جديد، لذا أولًا نقوم بعمل نشاط جديد بالضغط بالزر اليمن على اسم الحزمة واختيار: New > Activity > Empty Activity ونسميها DescriptionActivity. ولفتح هذا النشاط عند الضغط على عناصر قائمة العرض نقوم باستدعاء التابع ()setOnItemClickListener وبداخله نفتح النشاط الجديد ونرسل إليه اسم العنصر الذي قام بفتحه حتى يعرضه. package apps.noby.customlistviewexample; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.AdapterView; import android.widget.ListView; import java.util.ArrayList; public class MainActivity extends Activity { private ListView customLV; private CustomArrayAdapter adapter; private ArrayList<ListItem> items; String [] colorsArray = {"Red","Yellow","Blue","Black","Green","Brown","Grey"}; int [] colorsImage = {R.drawable.red,R.drawable.yellow,R.drawable.blue,R.drawable.black,R.drawable.green,R.drawable.brown,R.drawable.grey}; public static final String COLOR_KEY = "colorName"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); items = new ArrayList<ListItem>(); for(int i=0;i<colorsArray.length;i++){ ListItem item= new ListItem(); item.imgSrc=colorsImage; item.title = colorsArray; item.desc = colorsArray + " Color!"; items.add(item); } customLV = (ListView) findViewById(R.id.listview); adapter = new CustomArrayAdapter(this,R.layout.row_item,items); customLV.setAdapter(adapter); customLV.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Intent intent = new Intent(MainActivity.this,DescriptionActivity.class); intent.putExtra(COLOR_KEY,colorsArray[position]); startActivity(intent); } }); } } أخيرًا نقوم بتهيئة واجهة المستخدم الخاصة بالنشاط الجديد لتعرض النص المرسل إليها. <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/txt" android:textSize="20sp"/> </LinearLayout> ولعرض النص داخل الـ TextView نقوم في الشيفرة باستدعاء الـ Intent الذي قام بفتح النشاط واستخراج منه البيانات المرسلة وكتابتها على TextView كما يلي: package apps.noby.customlistviewexample; import android.app.Activity; import android.os.Bundle; import android.widget.TextView; public class DescriptionActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_description); String str = getIntent().getStringExtra(MainActivity.COLOR_KEY); TextView tv = (TextView) findViewById(R.id.txt); tv.setText("Welcome to " + str + " Color Home!!"); } } والآن نقوم بتجربة التطبيق على المحاكي حتى نتأكد من عمله بالشكل المطلوب. البحث داخل عناصر قائمة العرض التي تم تخصيصها تطبيق 6 في هذا التطبيق سنكرر ما قمنا به سابقًا من بحث عن نص معين داخل قائمة العرض ولكن الاختلاف هنا سيكون أن قائمة العرض ليست بهذه البساطة وتتكون من عنصر واحد فقط وهو النص، ولكنها تتكون من صور وعدة نصوص مختلفة لذا ينبغي علينا أن نصنع بأنفسنا طريقتنا الخاصة للبحث وتنقية العناصر من القائمة وترك العناصر التي يتم البحث عنها. أولًا نقوم بتغيير ملف الواجهة activity_main.xml وإضافة عنصر EditText. <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:drawableLeft="@drawable/ic_search_black_24dp" android:hint="Search..." android:id="@+id/searchedittext"/> <ListView android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/listview"/> </LinearLayout> ثانيًا نربط عنصر EditText في الشيفرة الأساسية ونستدعي التابع ()addTextChangedListener وبداخل الدالة ()onTextChanged نستدعي التابع الخاص بالتنقية كما سبق. package apps.noby.customlistviewexample; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.text.Editable; import android.text.TextWatcher; import android.view.View; import android.widget.AdapterView; import android.widget.EditText; import android.widget.ListView; import java.util.ArrayList; public class MainActivity extends Activity { private ListView customLV; private CustomArrayAdapter adapter; private ArrayList<ListItem> items; String[] colorsArray = {"Red", "Yellow", "Blue", "Black", "Green", "Brown", "Grey"}; int[] colorsImage = {R.drawable.red, R.drawable.yellow, R.drawable.blue, R.drawable.black, R.drawable.green, R.drawable.brown, R.drawable.grey}; public static final String COLOR_KEY = "colorName"; EditText searchET; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); items = new ArrayList<ListItem>(); for (int i = 0; i < colorsArray.length; i++) { ListItem item = new ListItem(); item.imgSrc = colorsImage; item.title = colorsArray; item.desc = colorsArray + " Color!"; items.add(item); } searchET = (EditText) findViewById(R.id.searchedittext); customLV = (ListView) findViewById(R.id.listview); adapter = new CustomArrayAdapter(this, R.layout.row_item, items); customLV.setAdapter(adapter); customLV.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Intent intent = new Intent(MainActivity.this, DescriptionActivity.class); intent.putExtra(COLOR_KEY, colorsArray[position]); startActivity(intent); } }); searchET.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { adapter.getFilter().filter(s); } @Override public void afterTextChanged(Editable s) { } }); } } أخيرًا يتبقى أن نقوم بكتابة تابع التنقية الخاص بنا، وسنقوم بكتابته داخل الصنف CustomArrayAdapter. داخل CustomArrayAdapter نقوم بإنشاء كائن آخر من ArrayList ونجعله يساوي البيانات التي سنضعها في قائمة العرض، مثله كالكائن data. private ArrayList<ListItem> dataTemp; public CustomArrayAdapter(Context context, int resource, ArrayList<ListItem> objects) { super(context, resource,objects); con = context; data = objects; resLayout = resource; dataTemp = objects; } بعد ذلك نعيد كتابة التابع الخاص بالتنقية والمتواجد أيضًا داخل الأب Arrayadapter لذا سنقوم بعمل Override لهذا التابع. والتابع هو ()getFilter ويعيد كائن من الصنف Filter وبداخل هذا الصنف تظهر لدينا دالتين جديدتين كما يلي: @Override public Filter getFilter() { return new Filter() { @Override protected FilterResults performFiltering(CharSequence constraint) { } @Override protected void publishResults(CharSequence constraint, FilterResults results) { } }; } الأولى ()performFiltering هي المسؤولة عن عملية التنقية ونكتب بداخلها كيف تتم تنقية العناصر وما هي العناصر التي ستظهر عند كتابة نص معين ويتم تخزين النص الذي تم كتابته وعلى أساسه تتم عملية التنقية داخل المتغير constraint. الثانية ()publishResults هي المسؤولة عن تغيير محتوي البيانات التي تعرضها قائمة العرض حيث تأتي النتائج بعد التنقية داخل الكائن results. سنبدأ أولًا بكتابة طريقة التنقية التي سنتبعها وهي، نتأكد أن النص الذي ستتم التنقية على أساسه وممثل في المتغير constraint ليس فارغًا وذلك عن طريق الجملة المنطقية التالية: constraint == null || constraint.length() == 0 وتعني هل المتغير constraint لم يُخزن بداخله أي بيانات أو تم تخزين نص فارغ، لذا سنضع هذه الجملة داخل الجملة الشرطية if وإن تحققت فذلك يعني أنه لا داعي للتنقية وستظل عناصر القائمة كما هي، أما إذا كان خلاف ذلك ويوجد نص فسنقوم بعملية التنقية. FilterResults results = new FilterResults(); if(constraint == null || constraint.length() == 0){ results.values = dataTemp; results.count = dataTemp.size(); } else{ } return results; لاحظ أن الكائن results هو المتغير الذي سنخزن بداخله ناتج التنقية، ويتكون من عنصرين مهمين الأول results.values وهنا يتم تخزين البيانات الجديدة و results.count ويتم تخزين عددهم. طريقة التنقية ستكون كالتالي: نصنع حاوية جديدة لتخزين العناصر الناتجة من التنقية. لكل عنصر من العناصر القديمة في قائمة العرض نقوم بمقارنته بالنص المكتوب لنرى هل يحتوي على حروفه. إن كان يحتوي على أحد حروفه ننقله إلى حاوية التخزين الجديدة التي صنعناها. بعد الانتهاء من كافة العناصر نعيد النتيجة ليتم تحديث قائمة العرض بالعناصر الجديدة. ArrayList<ListItem> filterList = new ArrayList<ListItem>(); for (int i = 0; i < dataTemp.size(); i++) { ListItem item = dataTemp.get(i); if ( (item.title.toLowerCase()).contains(constraint.toString().toLowerCase())) { ListItem filterItem = new ListItem(); filterItem.imgSrc = dataTemp.get(i).imgSrc; filterItem.title = dataTemp.get(i).title; filterItem.desc = dataTemp.get(i).desc; filterList.add(filterItem); } } results.values = filterList; results.count = filterList.size(); لاحظ أننا نقوم بالتنقية على أساس النص المخزن داخل title، وأننا نقوم بتحويل هذا النص إلى حروف صغيرة ومقارنته بالنص الذي نبحث عنه أيضًا بعد تحويله لحروف صغيرة لتوحيد طريقة كتابة الكلمة. ونستخدم التابع ()contains والذي يتأكد هل الكائن الذي قام باستدعاء هذا التابع يحتوي على النص الذي نمرره للتابع أم لا، إن كان يحتويه فسيعيد القيمة المنطقية true أما إذا كان لا يحتويه فسيعيد false. بعد ذلك ننتقل إلى الدالة الأخرى لنشر النتائج الجديدة في قائمة العرض، ونقوم بنقل البيانات المخزنة في results.values إلى المتغير data حتى يتم تحديث قائمة العرض باستدعاء التابع ()getView بعد استدعاء ()notifyDataChanged. data = (ArrayList<ListItem>) results.values; notifyDataSetChanged(); لتصبح الشيفرة النهائية للصنف CustomArrayAdapter بعد التعديل: package apps.noby.customlistviewexample; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.Filter; import android.widget.ImageView; import android.widget.TextView; import java.util.ArrayList; public class CustomArrayAdapter extends ArrayAdapter{ private Context con; private ArrayList<ListItem> data; private ArrayList<ListItem> dataTemp; private LayoutInflater inflater; private int resLayout; public CustomArrayAdapter(Context context, int resource, ArrayList<ListItem> objects) { super(context, resource,objects); con = context; data = objects; resLayout = resource; dataTemp = objects; } @Override public int getCount() { return data.size(); } @Override public View getView(int position, View convertView, ViewGroup parent) { inflater = (LayoutInflater) con.getSystemService(Context.LAYOUT_INFLATER_SERVICE); View rootView = inflater.inflate(resLayout,null); ImageView img = (ImageView) rootView.findViewById(R.id.imgview); TextView title = (TextView) rootView.findViewById(R.id.titleview); TextView desc = (TextView) rootView.findViewById(R.id.descview); ListItem item = data.get(position); img.setImageResource(item.imgSrc); title.setText(item.title); desc.setText(item.desc); return rootView; } @Override public Filter getFilter() { return new Filter() { @Override protected FilterResults performFiltering(CharSequence constraint) { FilterResults results = new FilterResults(); if (constraint == null || constraint.length() == 0) { results.values = dataTemp; results.count = dataTemp.size(); } else{ ArrayList<ListItem> filterList = new ArrayList<ListItem>(); for (int i = 0; i < dataTemp.size(); i++) { ListItem item = dataTemp.get(i); if ( (item.title.toLowerCase()).contains(constraint.toString().toLowerCase())) { ListItem filterItem = new ListItem(); filterItem.imgSrc = dataTemp.get(i).imgSrc; filterItem.title = dataTemp.get(i).title; filterItem.desc = dataTemp.get(i).desc; filterList.add(filterItem); } } results.values = filterList; results.count = filterList.size(); } return results; } @Override protected void publishResults(CharSequence constraint, FilterResults results) { data = (ArrayList<ListItem>) results.values; notifyDataSetChanged(); } }; } } الآن قم بتجربة التطبيق على المحاكي للتأكد انه يعمل كما ينبغي. بهذا نكون قد وصلنا إلى نهاية هذا الدرس، في انتظار تجربتكم وآرائكم.