Android ListView مع مثال على محول مخصص

في هذا البرنامج التعليمي، سنستخدم CustomAdapter الذي يملأ الصفوف المخصصة لـ Android ListView باستخدام ArrayList. أيضًا لتحسين تجربة المستخدم، سنقوم بتحريك القائمة أثناء التمرير.

نظرة عامة على محول القائمة المخصصة لـ Android

أبسط محول لملء عرض من ArrayList هو ArrayAdapter. هذا ما سنقوم بتنفيذه في هذا البرنامج التعليمي. هناك محولات أخرى أيضًا، مثل CursorAdapter الذي يرتبط مباشرة بنتيجة من قاعدة بيانات SQLite المحلية ويستخدم مؤشرًا كمصدر للبيانات.

إعادة استخدام الصفوف

عندما يتم إنشاء ListView ويتم ملء الصفوف بحيث يتم ملؤها بالكامل، لا تتم إنشاء عناصر صف جديدة في الذاكرة. عندما يتم التمرير عبر القائمة، يُحتفظ بالعناصر التي تترك الشاشة في الذاكرة للاستخدام في وقت لاحق، ومن ثم يُعيد كل صف جديد يدخل الشاشة استخدام صف أقدم محفوظ في الذاكرة.

إنشاء قالب عرض

لنقم بإنشاء تخطيط XML يقدم العناصر في صف بطريقة مخصصة. row_item.xml

<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="10dp">

    <TextView
        android:id="@+id/name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:text="Marshmallow"
        android:textAppearance="?android:attr/textAppearanceSmall"
        android:textColor="@android:color/black" />


    <TextView
        android:id="@+id/type"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/name"
        android:layout_marginTop="5dp"
        android:text="Android 6.0"
        android:textColor="@android:color/black" />

    <ImageView
        android:id="@+id/item_info"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentEnd="true"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:src="@android:drawable/ic_dialog_info" />


    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true">

        <TextView
            android:id="@+id/version_heading"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="API: "
            android:textColor="@android:color/black"
            android:textStyle="bold" />

        <TextView
            android:id="@+id/version_number"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="23"
            android:textAppearance="?android:attr/textAppearanceButton"
            android:textColor="@android:color/black"
            android:textStyle="bold" />

    </LinearLayout>

</RelativeLayout>

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

هيكل المشروع

الكود

نقوم بإنشاء ListView مخصص باستخدام تركيبة ArrayAdapter مع DataModel ككائن. getView() هو الطريقة التي تعيد العرض الفعلي المستخدم كصف داخل ListView في موضع معين. يحتوي content_main.xml على ListView كما هو موضح أدناه. content_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    tools:context="com.journaldev.customlistview.MainActivity"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:showIn="@layout/activity_main">

    <ListView
        android:id="@+id/list"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />

</RelativeLayout>

يتم عرض نموذج البيانات الذي يتم تضمينه في ArrayList أدناه. DataModel.java

public class DataModel {

    String name;
    String type;
    String version_number;
    String feature;

    public DataModel(String name, String type, String version_number, String feature ) {
        this.name=name;
        this.type=type;
        this.version_number=version_number;
        this.feature=feature;

    }

    public String getName() {
        return name;
    }
    
    public String getType() {
        return type;
    }
    
    public String getVersion_number() {
        return version_number;
    }
    
    public String getFeature() {
        return feature;
    }
    
}

المحول المخصص الذي يملأ نموذج البيانات في ListView موضح أدناه. CustomAdapter.java

public class CustomAdapter extends ArrayAdapter implements View.OnClickListener{

    private ArrayList dataSet;
    Context mContext;

    // ذاكرة التخزين المؤقتة للعرض
    private static class ViewHolder {
        TextView txtName;
        TextView txtType;
        TextView txtVersion;
        ImageView info;
    }

    public CustomAdapter(ArrayList data, Context context) {
        super(context, R.layout.row_item, data);
        this.dataSet = data;
        this.mContext=context;

    }

    @Override
    public void onClick(View v) {

        int position=(Integer) v.getTag();
        Object object= getItem(position);
        DataModel dataModel=(DataModel)object;

        switch (v.getId())
        {
            case R.id.item_info:
                Snackbar.make(v, "Release date " +dataModel.getFeature(), Snackbar.LENGTH_LONG)
                        .setAction("No action", null).show();
                break;
        }
    }

    private int lastPosition = -1;

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // الحصول على عنصر البيانات لهذا الموضع
        DataModel dataModel = getItem(position);
        // التحقق مما إذا كان هناك عرض موجود يُعاد استخدامه، وإلا فنفخ العرض
        ViewHolder viewHolder; // view lookup cache stored in tag

        final View result;

        if (convertView == null) {

            viewHolder = new ViewHolder();
            LayoutInflater inflater = LayoutInflater.from(getContext());
            convertView = inflater.inflate(R.layout.row_item, parent, false);
            viewHolder.txtName = (TextView) convertView.findViewById(R.id.name);
            viewHolder.txtType = (TextView) convertView.findViewById(R.id.type);
            viewHolder.txtVersion = (TextView) convertView.findViewById(R.id.version_number);
            viewHolder.info = (ImageView) convertView.findViewById(R.id.item_info);

            result=convertView;

            convertView.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
            result=convertView;
        }

        Animation animation = AnimationUtils.loadAnimation(mContext, (position > lastPosition) ? R.anim.up_from_bottom : R.anim.down_from_top);
        result.startAnimation(animation);
        lastPosition = position;

        viewHolder.txtName.setText(dataModel.getName());
        viewHolder.txtType.setText(dataModel.getType());
        viewHolder.txtVersion.setText(dataModel.getVersion_number());
        viewHolder.info.setOnClickListener(this);
        viewHolder.info.setTag(position);
        // إرجاع العرض المكتمل للتقديم على الشاشة
        return convertView;
    }
}

في الكود أعلاه، قمنا بإضافة onClickListener إلى ImageView الذي يعرض SnackBar عند النقر عليه مع وصف للصف الذي يتم النقر عليه. كما يتم تحريك صفوف القائمة عند التمرير. يتم تقديم ملفين مورد xml للتحريك أدناه. down_from_top.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="https://schemas.android.com/apk/res/android"
    android:shareInterpolator="@android:anim/decelerate_interpolator">
    <translate
        android:fromXDelta="0%" android:toXDelta="0%"
        android:fromYDelta="-100%" android:toYDelta="0%"
        android:duration="400" />
</set>

up_from_bottom.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="https://schemas.android.com/apk/res/android"
    android:shareInterpolator="@android:anim/decelerate_interpolator">
    <translate
        android:fromXDelta="0%" android:toXDelta="0%"
        android:fromYDelta="100%" android:toYDelta="0%"
        android:duration="400" />
</set>

يتم تعريف MainActivity.java حيث يُعين المحول المخصص على ListView أدناه. بالإضافة إلى ذلك، يتم ملء ArrayList عشوائي من كائنات DataModel. MainActivity.java

public class MainActivity extends AppCompatActivity {

    ArrayList dataModels;
    ListView listView;
    private static CustomAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        listView=(ListView)findViewById(R.id.list);

        dataModels= new ArrayList<>();

        dataModels.add(new DataModel("Apple Pie", "Android 1.0", "1","September 23, 2008"));
        dataModels.add(new DataModel("Banana Bread", "Android 1.1", "2","February 9, 2009"));
        dataModels.add(new DataModel("Cupcake", "Android 1.5", "3","April 27, 2009"));
        dataModels.add(new DataModel("Donut","Android 1.6","4","September 15, 2009"));
        dataModels.add(new DataModel("Eclair", "Android 2.0", "5","October 26, 2009"));
        dataModels.add(new DataModel("Froyo", "Android 2.2", "8","May 20, 2010"));
        dataModels.add(new DataModel("Gingerbread", "Android 2.3", "9","December 6, 2010"));
        dataModels.add(new DataModel("Honeycomb","Android 3.0","11","February 22, 2011"));
        dataModels.add(new DataModel("Ice Cream Sandwich", "Android 4.0", "14","October 18, 2011"));
        dataModels.add(new DataModel("Jelly Bean", "Android 4.2", "16","July 9, 2012"));
        dataModels.add(new DataModel("Kitkat", "Android 4.4", "19","October 31, 2013"));
        dataModels.add(new DataModel("Lollipop","Android 5.0","21","November 12, 2014"));
        dataModels.add(new DataModel("Marshmallow", "Android 6.0", "23","October 5, 2015"));

        adapter= new CustomAdapter(dataModels,getApplicationContext());

        listView.setAdapter(adapter);
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView parent, View view, int position, long id) {

                DataModel dataModel= dataModels.get(position);

                Snackbar.make(view, dataModel.getName()+"\n"+dataModel.getType()+" API: "+dataModel.getVersion_number(), Snackbar.LENGTH_LONG)
                        .setAction("No action", null).show();
            }
        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // نفخ القائمة؛ هذا يضيف عناصر إلى شريط العمل إذا كان موجودًا
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // التعامل مع عناصر شريط العمل هنا. سيتم
        // التعامل تلقائيًا مع نقرات على زر الصفحة الرئيسية/الرجوع، طالما
        // أنك تحدد نشاطًا أصليًا في AndroidManifest.xml
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

الناتج المعروض لتطبيق في عمله يظهر أدناه. يأتي هذا مع نهاية هذا البرنامج التعليمي. يمكنك تحميل المشروع النهائي Android ListView Custom Adapter من الرابط أدناه.

تحميل مشروع Android ListView Custom Adapter

المرجع: API Guide List View

Source:
https://www.digitalocean.com/community/tutorials/android-listview-with-custom-adapter-example-tutorial