استخدام ArrayAdapter مخصصة مع الـ ListView في الأندرويد

استخدام ArrayAdapter مخصصة مع الـ ListView في الأندرويد

م. خديجة باحويرثمنذ 6 سنوات

 

السلام عليكم و رحمة الله

في الدرس السابق,  قمنا بعمل قائمة على ListView و ذلك باستخدام الـArray adapter , و ArrayList كمصدر للبيانات في القائمة. كانت القائمة بسيطة جدا تتكون من عناصر كل عنصر هو عبارة عن TextView أي نص واحد فقط.

بيئة الأندرويد وفرت عدداً من الـ Layouts المناسبة للتطبيق الخاص بك, أحد أسرع و أسهل الأدوات لإظهار سلسلة أو قائمة من البيانات للمستخدم هي الـ ListView كما تم تطبيق ذلك في الدرس السابق, هذه الأداة تقوم ببناء مساحة بسيطة قابلة للتنقل للأعلى و الأسفل لمشاهدة كافة العناصر.

ماذا لو أردت تصميم قوائم معقدة أكثر حيث تحتوي مثلا على صور و Buttons  داخل العناصر؟ أو تحتوي على أكثر من TextView؟. شاهد الصور التالية التي توضح معنى ذلك:

  

الشيئ الجيد أن الـ ListView  يمكن تخصيصها لعمل قوائم مشابهة للصور في الأعلى. على سبيل المثال تستطيع عرض صورة في الجزء الأيسر و في الجزء الأيمن Text و هذا يتم عمله لكل عنصر. إمكانية تخصيص الـ ListViwe تتم بواسطة بناء ما يسمى بـ Custom Array Adapter .

في هذا الدرس ستتعلم:

بناء Custom Array Adapter ثم ربطها مع  ListView, و نتيجة لذلك ستحصل على قائمة تضم عناصر بشكل فريد و مميز!

 

خطوات عمل Custom ArrayAdapter لعمل قائمة مخصصة

سنقوم بعدد من الخطوات لنحصل في النهاية على الشكل التالي للقائمة ListView الخاصة بنا. سنحتاج لعمل مايلي:

1- مصدر للبيانات: هي قائمة من الـ objects, قد يكون مصدر البيانات مثلاً ArrayList تضم العناصر.

2- ArrayAdapter مخصصة: و التي تحدد كيف استخلاص الداتا من مصدر البيانات و طريقة عرضها على ListView للمستخدم.

3- ListView: هي الـ UI التي ستستخدم الـ ArrayAdapter لعرض القائمة النهائية.

 و لتتذكر العلاقة بين العناصر الثلاث, راجع الصورة التالية:

بناء Custom ListView لتطبيق خاص بالكتب

بنهاية هذا الدرس ستتمكن من إنشاء Custom ListView و التي سوف نستعرض فيها صور و أسماء الكتب. الصورة التالية توضح النتيجة المتوقعة للتطبيق :).

إنشاء المشروع

أنشيء مشروع جديد على الـ Android Studio, و بعد اضافة Empty Activity سيتم فتح التطبيق الفارغ, بداخله بالتأكيد كلاس  واحدة هي MainActivity.java بسيطة و ملف الـ XML الذي يقابلها activity_main.xml هذا كل ما تحتاجه لكي تبدأ !!.

 

تحديد شكل العنصر في القائمة (XML file)

نحتاج الآن لتعريف XML layout  الذي يحدد طريقة ظهور البيانات في القائمة, و أيضً ملف الـ XML هذا سيتم استخدامه لاحقاً بواسطة Custom ArrayAdapte للحصول على القائمة بشكلها النهائي.

حدد شكل العنصر في القائمة برسم يدوي مبسط, مثلا نريد أن تكون صورة الكتاب في الجزء الأيسر, و من ناحية اليمين يكون اسم الكتاب. الشكل التالي يوضح تصور بسيط للتصميم.

لعمل ذلك من نافذة المشروع بداخل مجلد res ثم layout قم بإنشاء new layout و تسميتها بإسم list_item كالتالي:

و من ثم انسخ التالي إلى ملف layout الخاص بك, مع ملاحظة أن الـ TextViewو ImageView لابد أن نضع لها ID  حتى نتمكن من الوصول إليها من الـ Custom ArrayAdapter  لاحقاً.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" 
android:layout_width="match_parent" 
android:layout_height="100dp" 
android:padding="5dp">
 
<ImageView android:id="@+id/book_image" 
android:layout_width="0dp" 
android:layout_height="wrap_content" 
android:layout_centerHorizontal="true" 
android:layout_weight="1" />
 
<TextView android:id="@+id/book_name" 
android:layout_width="0dp" 
android:layout_height="wrap_content" 
android:fontFamily="sans-serif-medium" 
android:textAppearance="?android:textAppearanceMedium" 
android:layout_weight="3" 
android:layout_gravity="center" 
android:textStyle="bold"/>
</LinearLayout>

تعديل الـ Layout الخاص بـ activity_main.xml

بداخل ملف الـ activity_main.xml قم باستبدال TextView بـ ListView  كالتالي:

<?xml version="1.0" encoding="utf-8"?>
<ListView android:layout_width="match_parent" 
android:layout_height="match_parent" 
android:id="@+id/listView" 
xmlns:android="http://schemas.android.com/apk/res/android">
</ListView>

تذكر أن الـ ListView  لديها اسم قمنا بتخصيصه من خلال android:id="@+id/listView".

بناء قالب (Class) للبيانات

قبل إنشاء الـ ListView  نحتاج إلى الداتا المراد تعبئتها داخل القائمة. و بما أن كل كتاب لديه مجموعة من المعلومات الخاصة به و بما أننا سنقوم بتعبئة ListView بمعلومات عن الكتاب, اذن من الجيد عمل كلاس يضم خصائص الكتاب, و هي اسم الكتاب و صورة الكتاب, أيضا بداخل الكلاس نضع get methods  لكل خاصية.

لعمل الكلاس نافذة المشروع , بالزر الأيمن على اسم المشروع كالتالي:

الكود التالي كامل للكلاس Book:

public class Book {
//Each Book Properties
private String mBookName;
private Integer mImageID;
 
//Constructor
public Book(String name, int imageID)
{
mBookName = name;
mImageID = imageID;
}
//getter to retrieve book properties.
public String getBookName(){return mBookName;}
public Integer getImageID(){return mImageID;}
}

الآن تم بناء Structure  للكلاس السابق عن طريق اضافة حقول Private  لتخزن معلومات مثل اسم الكتاب, صورة الكتاب.

بعدها قمنا بتخصيص الـ Constructor  و الذي سيتم استخدامه في كل مرة نريد اضافة كتاب جديد, و أخيرا تعريف دوال الـ get  لكل حقل و التي سنستخدمها لاحقاً لاستخراج المعلومات التي نريدها. getBookName تقوم باسترجاع اسم الكتاب, و getImageIDتقوم باسترجاع رقم id للصورة الخاصة بالكتاب نفسه.

 

إضافة صور الكتب للمشروع

بما أن كل كتاب لديه صوره خاصة, إذن يجب إضافة الصور إلى الأندرويد ستديو مثلا لو أردنا عرض 4 كتب سيكون لدينا 4 صور و هكذا. قم بإضافة عدد من الصور بنسخ جميع الصور و لصقها في ملف drawable بالشكل التالي:

 

إنشاء ArrayAdapter مخصّصة (java class)

الـ ArrayAdapter الأساسية الموجودة في الأندرويد تستطيع إظهار فقط TextView واحدة فقط لكل عنصر في القائمة ListView كما في الدرس السابق هنا, و بما أننا قررنا إنشاء شكل خاص لكل عنصر في القائمة إذن لابد أن نقوم بعمل ArrayAdapter خاصة بنا, أي سنقوم بعمل override لل ArrayAdapter.

لعمل ذلك, أنشيء كلاس جديدة في المشروع من خلال الضغط على اسم المشروع newClass:

 

و بداخل الكلاس BookAdapter.java انسخ الكود التالي:

import android.app.Activity;
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;

/**
 * Created by khadijah on 3/13/2018.
 */

public class BookAdapter extends ArrayAdapter<Book> {

    public BookAdapter(Activity context, ArrayList<Book> book) {
        // Here, we initialize the ArrayAdapter's internal storage for the context and the list.
        // the second argument is used when the ArrayAdapter is populating a single TextView.
        // Because this is a custom adapter for two TextViews and an ImageView, the adapter is not
        // going to use this second argument, so it can be any value. Here, we used 0.
        super(context, 0, book);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        // Check if the existing view is being reused, otherwise inflate the view
        View listItemView = convertView;
        if(listItemView == null) {
            listItemView = LayoutInflater.from(getContext()).inflate(
                    R.layout.list_item, parent, false);
        }

        // Get the Book object located at this position in the list
        Book currentBook = getItem(position);

        // Find the TextView in the list_item.xml layout with the ID book_name
        TextView bookNameTextView = (TextView) listItemView.findViewById(R.id.book_name);

        // Get the Book name from the current Book object and
        // set this text on the TextView
        bookNameTextView.setText(currentBook.getBookName());

        // Find the TextView in the list_item.xml layout with the ID book_image
        ImageView imageTextView = (ImageView) listItemView.findViewById(R.id.book_image);

        // Get the book image from the current Book object and
        // set this text on the ImageView
        imageTextView.setImageResource(currentBook.getImageID());

        // Return the whole list item layout (containing 1 TextViews  and 1 imageView)
        // so that it can be shown in the ListView
        return listItemView;
    }
}

بالأعلى تم إنشاء Adapter  جديدة و مخصصة "Custom" تم تسميتها بـ BookAdapter Class, و التي ترث "Extend" الـ ArrayAdapter .

ثم تم تعريف الـ Constructor بحيث يستقبل 3 بارمترات;

super(context, 0, words);

الأول context, و الثاني resource و هو يستخدم في حالة الـ ArrayAdapter الأساسية و لأننا قمنا ببناء Custom Array Adapter سنجعل هذا البارمتر قيمته صفر لأننا لسنا بحاجة له الآن في حالتنا, و الثالث object و هو عبارة عن الـ ArrayList التي تضم العناصر أي objects of Book.

قبل أن نكمل شرح الكود, أو توضيح العلاقة بين الـ BookAdapter.java class ,Book.java class, و list_item.xml

فالـ BookAdapter تستخدم  Book class  لكي ترتب ظهور البيانات في الأخير على الـ list_item.xml.

الدالة getView

 الدالة getView  وظيفتها استدعاء الـ Views وهي (TextView & ImageView)من داخل الـ list_item.xml و تعبئتها بالقيم. تذكر أنه في كل مرة تقوم الـ ListView  باستدعاء عنصر جديد فإنه سيتم مباشرة مناداة الدالة getView  من جديد. يتم استدعاء عناصر القائمة صفاً صفاً أو عنصراً عنصراً تلو الآخر و يمكن الوصول لموقع العنصر من القائمة من خلال القيمة position  الموجود في تعريف الدالة.

// Get the Book object located at this position in the list
   Book currentBook = getItem(position);

بعدها يتم الحصول على كائن من كلاس الـ Book حسب موقع العنصر. الآن و بعد الحصول على الكائن أي الكتاب الحالي, ثم يتم الوصول إلى العناصر الموجودة في list_item.xml عن طريق الid الخاص بكل عنصر,

عندها نستطيع الوصول إلى خصائص الكتاب و هي BookName, ImageID باستخدام دوال الـ get التي قمنا بإنشاءها داخل الكلاس Book.java.

استدعاء BookName  و ترتيب ظهورها على عنصر في القائمة

// Find the TextView in the list_item.xml layout with the ID book_name
   TextView bookNameTextView = (TextView) listItemView.findViewById(R.id.book_nam

ثم استرجاع خاصية اسم الكتاب للكتاب الحالي currentBook و عرضها للمستخدم على الـ TextView كالتالي:

// Get the Book name from the current Book object and
// set this text on the TextView
   bookNameTextView.setText(currentBook.getBookName());

 

استدعاء صورة الكتاب و ترتيب ظهورها على عنصر في القائمة

كما فعلنا في الخطوة السابقة نصل إلى imageView الموجودة على list_item.xml عن طريق الـ id الخاص بها كالتالي:

// Find the TextView in the list_item.xml layout with the ID book_image
 ImageView imageTextView = (ImageView) listItemView.findViewById(R.id.book_image);

ثم استرجاع رقم صورة الكتاب للكتاب الحالي currentBook و عرضها للمستخدم على الـ ImageView كالتالي:

// Get the book image from the current Book object and
// set this text on the ImageView
   imageTextView.setImageResource(currentBook.getImageID());

و بانتهاء السطر التالي يكون الـ ArrayAdapter قد انتهى من تخصيص عنصر واحد فقط في القائمة, و بعدها يقوم بتكرار الخطوات حتى الانتهاء من جميع عناصر القائمة.

return listItemView;

 

الخطوة الأخيرة من داخل الـ MainActivity.java:

إضافة البيانات التي نريد استخدامها

الأن قمنا بعمل أغلب الخطوات :

  • بناء ملف الـ XML  للعناصر في القائمة list_item.xml

  • بناء كلاس لبيانات كل عنصر و هي Book.java class  و التي تمثل هيكلة لبيانات الكتاب

  • بناء ArrayAdapter مخصصة و تم تسميتها  BookAdapter.java

الآن ننتقل إلى المرحلة و التالية و النهائية و هي إضافة البيانات المراد ظهورها في القائمة, قم بفتح الـ MainActivity.java و عرف الـ ArrayList بالشكل التالي (تذكر أن الـ ArrayList  هي مصدر البيانات للـ ArrayAdapter)

إنشاء الـ ArrayList  من النوع Book  اي تخزن عناصر عبارة عن كائنات من الكلاس Book:

private ArrayList<Book> booksItems = new ArrayList<Book>();

ثم تعبئة بيانات الكتاب الأول كالتالي, يجب أن تكون القيم الاسم ثم رقم مصدر صورة الكتاب اجباري بهذا الترتيب, و السبب أننا سابقا بداخل الـ Book.java class عرفنا الـ Constructor  بنفس هذا الترتيب بحث يستقبل القيمة الأولى string و الثانية Integer.

booksItems.add(new Book("android for dummies!!", R.drawable.book_1));

تعبئة بقية الكتب بنفس الطريقة.

  • استخدام الـ Bookadapter الذي قمنا ببناءه مسبقاً

الآن بعد أن تم تعبئة الداتا بقي استخدام الـ BookAdapter  و ربطها مع مصدر البيانات  ArrayAdapter  الذي قمنا بتسميته  bookItems.

BookAdapter items = new BookAdapter(this, booksItems);
ListView list = (ListView) findViewById(R.id.listView);
 list.setAdapter(items);

كود  الـ MainActivity.java كامل هنا :

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.ListView;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    private ArrayList<Book> booksItems = new ArrayList<Book>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        booksItems.add(new Book("Android for dummies!!", R.drawable.book_1));
        booksItems.add(new Book("Programming android", R.drawable.book_2));
        booksItems.add(new Book("Android Games", R.drawable.book_3));
        booksItems.add(new Book("Hellow android", R.drawable.book_4));
        booksItems.add(new Book("Android Threading!!", R.drawable.book_5));

        BookAdapter items = new BookAdapter(this, booksItems);
        ListView list = (ListView) findViewById(R.id.listView);
        list.setAdapter(items);

    }
}

مبروك !! بذلك أنت أتممت التطبيق بنجاح.

أخيرا موضوع الـ Custom ArrayAdapter ليس بالصعب و لكن خطواته كثيرة و متشابكة, حاولت في هذا الدرس تلخيص النقاط و تبسيطها , إذا واجهتك أي صعوبة اترك تعليقاً بالأسفل.

كود التطبيق كامل يمكن تحميله و مشاهدته من GitHup هنا.

 

References:

1- https://www.sitepoint.com/custom-data-layouts-with-your-own-android-arrayadapter/

2- https://github.com/codepath/android_guides/wiki/Using-an-ArrayAdapter-with-ListView

3- Udacity.com

كلمات دليلية: android java
1
إعجاب
6215
مشاهدات
1
مشاركة
1
متابع

التعليقات (1)

Sofy Abbas:

very helpfull thank u

لايوجد لديك حساب في عالم البرمجة؟

تحب تنضم لعالم البرمجة؟ وتنشئ عالمك الخاص، تنشر المقالات، الدورات، تشارك المبرمجين وتساعد الآخرين، اشترك الآن بخطوات يسيرة !