Android RecyclerView، مثال دراسي على Android CardView

Android RecyclerView و Android CardView تمت إضافتهما في Android Lollipop مع Material Design. بالنسبة لأولئك الذين لا يعرفون ما هو Material Design، فإنه دليل شامل لعناصر واجهة المستخدم التي تم تقديمها منذ Android 5.0، ويعمل على تحسين جاذبية التطبيقات بصرياً.

Android RecyclerView

Android RecyclerView هو إصدار متقدم وقوي ومرن لل ListView. يشبه Android RecyclerView ListView باستثناء أنه يجبرنا على استخدام فئة RecyclerView.ViewHolder لحمل العناصر وهو ليس إجباريًا في ListView. كما يوحي الاسم، يتم استخدام Android RecyclerView لإعادة استخدام الخلايا عند التمرير لأعلى ولأسفل من خلال إعادة استخدام العناصر في القائمة. تحسين آخر في RecyclerView هو أنه يسمح لنا بتعيين LayoutManagers بشكل ديناميكي أثناء التشغيل، على عكس ListView الذي كان متاحًا فقط في قائمة التمرير العمودية. يسمح RecyclerView لنا بتعيين أنواع الشرائح التالية أثناء التشغيل.

  • LinearLayoutManager: يدعم كل من القوائم العمودية والأفقية
  • StaggeredLayoutManager : يدعم قوائم متقطعة
  • GridLayoutManager : يدعم عرض الشبكات كما هو موضح في GalleryView سابقًا

فئات Android RecyclerView

  • فئة RecyclerView.ItemAnimator توفر دعمًا أفضل لتحريك العروض بشكلٍ أفضل من ListViews
  • فئة RecyclerView.ItemDecorator توفر دعمًا أفضل عندما يتعلق الأمر بإضافة حدود وفواصل مما يعطي لنا تحكمًا هائلاً

لذلك ، فإن RecyclerView أكثر قابلية للتخصيص بالمقارنة مع ListView وتمنح المستخدمين تحكمًا أكبر. يتوفر RecyclerView في مكتبة الدعم. لذا علينا تعديل نص الجرادل لإضافة التبعيات التالية.

dependencies {
       compile 'com.android.support:recyclerview-v7:21.0.0-rc1'
 }

Android CardView

تظهر مكون واجهة المستخدم لـ Android CardView المعلومات داخل بطاقات. يتم استخدام هذا المكون عادةً لعرض معلومات الاتصال. هذا المكون متوفر في مكتبة دعم أخرى لذا علينا إضافة تبعيتها أيضًا.

dependencies {
        compile 'com.android.support:cardview-v7:21.0.0-rc1'
        compile 'com.android.support:recyclerview-v7:21.0.0-rc1'
 }

تُسَمِح واجهة البرمجة لـ Android CardView بالتحكم في لون الخلفية، الظل، نصف قطر الزاوية، والارتفاع، وما إلى ذلك. لاستخدام السمات المخصصة في XML، نحتاج إلى إضافة الإعلان التالي لمساحة الأسماء إلى تخطيط الوالدين. الإعلان التالي يحتوي على بعض السمات من مشروعنا.

<android.support.v7.widget.CardView
        android:id="@+id/card_view"
        xmlns:card_view="https://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        card_view:cardBackgroundColor="@color/grey_300"
        card_view:cardCornerRadius="10dp"
        card_view:cardElevation="5dp"
        card_view:cardUseCompatPadding="true">

السمات المهمة المستخدمة أعلاه هي:

  • card_view:cardCornerRadius: يُستخدم لتعيين نصف قطر الزاوية في تخطيطاتنا
  • card_view:cardBackgroundColor: يُستخدم لتعيين لون خلفية العرض

في مشروعنا التوضيحي، سنُضيف RecyclerView لعرض قائمة من CardViews تحتوي على أسماء وأرقام إصدارات Android مع شعار عينة. تم برمجة تفعيل CardView onclick لإزالة تلك البطاقة من القائمة. لقد أضفنا خيارًا في ActionBar لإضافة البطاقات المزالة مرة أخرى بالترتيب. ملاحظة: تم اختيار صور الشعار بشكل عشوائي من Google، لذا الأحجام قد تتنوع.

مثال على Android RecyclerView وCardView

تتكون المشروع من فئة MainActivity التي تعرض RecyclerView. يتم إضافة CardView إلى RecyclerView من فئة CustomAdapter. يتم استخدام DataModel لاسترداد البيانات لكل CardView من خلال getters. تحتفظ فئة MyData بمصفوفات النصوص والرسومات بالإضافة إلى هويتها.

مثال على رمز Android RecyclerView و CardView

تحتوي activity_main.xml على RecyclerView داخل RelativeLayout كما هو موضح أدناه. كود activity_main.xml:

<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"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity"
    android:background="@color/grey_300"
    >

    <android.support.v7.widget.RecyclerView
        android:id="@+id/my_recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical"
        />

</RelativeLayout>

تم تعريف تخطيط Android CardView أدناه: كود cards_layout.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:tag="cards main container">

    <android.support.v7.widget.CardView
        android:id="@+id/card_view"
        xmlns:card_view="https://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        card_view:cardBackgroundColor="@color/color_white"
        card_view:cardCornerRadius="10dp"
        card_view:cardElevation="5dp"
        card_view:cardUseCompatPadding="true">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            >

            <ImageView
                android:id="@+id/imageView"
                android:tag="image_tag"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_margin="5dp"
                android:layout_weight="1"
                android:src="@drawable/ic_launcher"/>

            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginTop="12dp"
                android:layout_weight="2"
                android:orientation="vertical"
                >

                <TextView
                    android:id="@+id/textViewName"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center_horizontal"
                    android:layout_marginTop="10dp"
                    android:text="Android Name"
                    android:textAppearance="?android:attr/textAppearanceLarge"/>

                <TextView
                    android:id="@+id/textViewVersion"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center_horizontal"
                    android:layout_marginTop="10dp"

                    android:text="Android Version"
                    android:textAppearance="?android:attr/textAppearanceMedium"/>

            </LinearLayout>
        </LinearLayout>

    </android.support.v7.widget.CardView>

</LinearLayout>

يحتوي Android CardView على ImageView مع TextViews اثنين في تخطيط خطي متداخل. تحتوي menu_main.xml على عنصر واحد لإضافة البطاقات المزالة. كود menu_main.xml:

<menu xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    xmlns:tools="https://schemas.android.com/tools"
    tools:context=".MainActivity">
    <item android:id="@+id/add_item"
        android:title="Add"
        android:orderInCategory="100"
        app:showAsAction="always"/>
</menu>

تم تعريف فئة MainActivity.java كما هو موضح أدناه:

package com.journaldev.recyclerviewcardview;

import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    private static RecyclerView.Adapter adapter;
    private RecyclerView.LayoutManager layoutManager;
    private static RecyclerView recyclerView;
    private static ArrayList data;
    static View.OnClickListener myOnClickListener;
    private static ArrayList removedItems;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        myOnClickListener = new MyOnClickListener(this);

        recyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
        recyclerView.setHasFixedSize(true);

        layoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(layoutManager);
        recyclerView.setItemAnimator(new DefaultItemAnimator());

        data = new ArrayList();
        for (int i = 0; i < MyData.nameArray.length; i++) {
            data.add(new DataModel(
                    MyData.nameArray[i],
                    MyData.versionArray[i],
                    MyData.id_[i],
                    MyData.drawableArray[i]
            ));
        }

        removedItems = new ArrayList();

        adapter = new CustomAdapter(data);
        recyclerView.setAdapter(adapter);
    }


    private static class MyOnClickListener implements View.OnClickListener {

        private final Context context;

        private MyOnClickListener(Context context) {
            this.context = context;
        }

        @Override
        public void onClick(View v) {
            removeItem(v);
        }

        private void removeItem(View v) {
            int selectedItemPosition = recyclerView.getChildPosition(v);
            RecyclerView.ViewHolder viewHolder
                    = recyclerView.findViewHolderForPosition(selectedItemPosition);
            TextView textViewName
                    = (TextView) viewHolder.itemView.findViewById(R.id.textViewName);
            String selectedName = (String) textViewName.getText();
            int selectedItemId = -1;
            for (int i = 0; i < MyData.nameArray.length; i++) {
                if (selectedName.equals(MyData.nameArray[i])) {
                    selectedItemId = MyData.id_[i];
                }
            }
            removedItems.add(selectedItemId);
            data.remove(selectedItemPosition);
            adapter.notifyItemRemoved(selectedItemPosition);
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        super.onOptionsItemSelected(item);
        if (item.getItemId() == R.id.add_item) {
           // التحقق مما إذا كان هناك عناصر لإضافتها
            if (removedItems.size() != 0) {
                addRemovedItemToList();
            } else {
                Toast.makeText(this, "Nothing to add", Toast.LENGTH_SHORT).show();
            }
        }
        return true;
    }

    private void addRemovedItemToList() {
        int addItemAtListPosition = 3;
        data.add(addItemAtListPosition, new DataModel(
                MyData.nameArray[removedItems.get(0)],
                MyData.versionArray[removedItems.get(0)],
                MyData.id_[removedItems.get(0)],
                MyData.drawableArray[removedItems.get(0)]
        ));
        adapter.notifyItemInserted(addItemAtListPosition);
        removedItems.remove(0);
    }
}

تُستدعى طريقة `removeItems()` من طريقة المستمع لإزالة CardView التي تم النقر عليها. يتم تخزين معرفها المقابل في مصفوفة للاسترجاع لاحقًا. لإضافة العرض لاحقًا، قمنا بتنفيذ طريقة أخرى تُسمى `addRemovedItemToList()`، حيث نُضيف هذا العرض في موضع محدد مسبقًا في القائمة ونقوم بإزالة معرفه من مصفوفة `removedItems`. يتم إعلام CustomAdapter في كلتا الحالتين. يتم تعريف فئة CustomeAdapter.java أدناه :

package com.journaldev.recyclerviewcardview;

import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.ArrayList;

public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.MyViewHolder> {

    private ArrayList<DataModel> dataSet;

    public static class MyViewHolder extends RecyclerView.ViewHolder {

        TextView textViewName;
        TextView textViewVersion;
        ImageView imageViewIcon;

        public MyViewHolder(View itemView) {
            super(itemView);
            this.textViewName = (TextView) itemView.findViewById(R.id.textViewName);
            this.textViewVersion = (TextView) itemView.findViewById(R.id.textViewVersion);
            this.imageViewIcon = (ImageView) itemView.findViewById(R.id.imageView);
        }
    }

    public CustomAdapter(ArrayList<DataModel> data) {
        this.dataSet = data;
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent,
                                           int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.cards_layout, parent, false);

        view.setOnClickListener(MainActivity.myOnClickListener);

        MyViewHolder myViewHolder = new MyViewHolder(view);
        return myViewHolder;
    }

    @Override
    public void onBindViewHolder(final MyViewHolder holder, final int listPosition) {

        TextView textViewName = holder.textViewName;
        TextView textViewVersion = holder.textViewVersion;
        ImageView imageView = holder.imageViewIcon;

        textViewName.setText(dataSet.get(listPosition).getName());
        textViewVersion.setText(dataSet.get(listPosition).getVersion());
        imageView.setImageResource(dataSet.get(listPosition).getImage());
    }

    @Override
    public int getItemCount() {
        return dataSet.size();
    }
}

في الشفرة أعلاه، قمنا بتنفيذ ViewHolder الخاص بنا عن طريق تمديد `RecyclerView.ViewHolder`. يتم نفخ العرض من `cards_layout.xml` الذي قمنا بتعريفه في دليل الشرائط. يتم إرفاق OnClickListener في MainActivity بهذا العرض في المقطع أدناه.

view.setOnClickListener(MainActivity.myOnClickListener);

تخزن ArrayList جميع البيانات على شكل كائن فئة DataModel في ArrayList وتضيفها إلى البطاقات المعنية في القائمة. تعريف فئة DataModel.java وفئة MyData.java التي تحتوي على البيانات المحددة لهذا التطبيق موضحة أدناه :

package com.journaldev.recyclerviewcardview;

public class DataModel {

    String name;
    String version;
    int id_;
    int image;

    public DataModel(String name, String version, int id_, int image) {
        this.name = name;
        this.version = version;
        this.id_ = id_;
        this.image=image;
    }

    public String getName() {
        return name;
    }

    public String getVersion() {
        return version;
    }

    public int getImage() {
        return image;
    }

    public int getId() {
        return id_;
    }
}
package com.journaldev.recyclerviewcardview;

public class MyData {

    static String[] nameArray = {"Cupcake", "Donut", "Eclair", "Froyo", "Gingerbread", "Honeycomb", "Ice Cream Sandwich","JellyBean", "Kitkat", "Lollipop", "Marshmallow"};
    static String[] versionArray = {"1.5", "1.6", "2.0-2.1", "2.2-2.2.3", "2.3-2.3.7", "3.0-3.2.6", "4.0-4.0.4", "4.1-4.3.1", "4.4-4.4.4", "5.0-5.1.1","6.0-6.0.1"};

    static Integer[] drawableArray = {R.drawable.cupcake, R.drawable.donut, R.drawable.eclair,
            R.drawable.froyo, R.drawable.gingerbread, R.drawable.honeycomb, R.drawable.ics,
            R.drawable.jellybean, R.drawable.kitkat, R.drawable.lollipop,R.drawable.marsh};

    static Integer[] id_ = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
}

أدناه هو الإخراج الذي أنتجه تطبيقنا مثال RecyclerView و CardView على نظام Android. كما يمكنك رؤية أن العنصر المزال دائمًا يتم إضافته في المؤشر الثالث (المركز الرابع في القائمة). هذا يضع نهاية لهذا البرنامج التعليمي حول RecyclerView و CardView على نظام Android. يمكنك تنزيل مشروع مثال Android RecyclerView CardView من الرابط أدناه.

تحميل مشروع مثال Android RecyclerView CardView

Source:
https://www.digitalocean.com/community/tutorials/android-recyclerview-android-cardview-example-tutorial