کار با RecyclerView و CardView متریال دیزاین

در طراحی و توسعه برنامه‌های موبایلی و بخصوص اپلیکیشن‌های اندرویدی، استفاده از لیست‌ها کاربرد فراوانی برای نمایش اطلاعات و محتوا دارد. مانند نمایش لیست تراکنش‌های حساب شخص در یک برنامه بانکی یا نمایش لیست شهرها در یک برنامه ویژه گردشگری. در این بخش به نحوه پیاده سازی لیست ها با استفاده از RecyclerView و CardView در اندروید می پردازیم که شامل مباحث زیر می باشد:

آنچه در این آموزش می‌خوانید:

  • معرفی کامپوننت RecyclerView و بررسی مزایای آن نسبت به ListView
  • آشنایی با کلاس Model
  • آشنایی با ViewHolder
  • آشنایی با Adapter در RecyclerView و متدهای آن
  • ساخت یک لیست و نمایش آن در قالب آیتم‌های یک RecyclerView
  • کار با متدهای LinearLayoutManager، GridLayoutManager و StaggeredGridLayoutManager جهت تعیین نحوه چیدمان آیتم‌ها
  • آشنایی با کامپوننت CardView و نحوه استفاده از آن در پروژه و طراحی لایه ها

این مبحث در قالب PDF و در ۴۸ صفحه تهیه شده که در ادامه چند صفحه‌ی ابتدایی آن را مشاهده می کنید:

معرفی RecyclerView:

به نام خدا. در فصل پنجم با کامپوننت/ویجت ListView آشنا شدیم. از ListView برای نمایش آیتم ها در قالب یک لیست استفاده کردیم. با معرفی متریال دیزاین در اندروید، ViewGroup یا ویجت RecyclerView نیز به عنوان یک جایگزین حرفه ای برای ListView و GridView معرفی شد. RecyclerView مزیت هایی نسبت به ListView دارد از جمله:
– بهینه است و حافظه بهتر مدیریت می شود. در RecyclerView فقط آیتم هایی که در صفحه نمایش قرار گرفته اند (به همراه چند آیتم قبل و بعد آنها) روی حافظه بارگزاری می شود نه همه آیتم های لیست.
– مدیریت RecyclerView ساده تر است و به راحتی می توان نحوه چینش آیتم ها (افقی یا عمودی) و همچنین در صورت نیاز به ستون بندی، تعداد ستونها را تعیین کرد.
– در RecyclerView امکان پیاده سازی انیمیشن برای زیباتر شدن نحوه اضافه و یا حذف شدن آیتم ها فراهم شده است.

RecyclerView لیستی از آیتم ها را نمایش می دهد که قابل Scroll بوده و محدودیتی در تعداد آیتم ها ندارد. از نمونه های کاربردی می توان به پیام رسان ها، لیست مخاطبین تلفن همراه، لیست ایمیل‌ها و یا لیست شهرها به همراه اطلاعات آب و هوایی مربوط به آنها در یک اپلیکیشن هواشناسی اشاره کرد.

نمایش لیست ایمیل توسط RecyclerView

تصویر بالا یک اپ مدیریت ایمیل است که هر آیتم از لیست شامل عنوان، نام فرستنده و چند کلمه ابتدای متن ایمیل می باشد. در سمت راست نیز زمان دریافت و آیکون ستاره جهت علامت زدن موارد مهم قرار دارد.

نمایش لیست ایمیل توسط RecyclerView

تصویر بالا مربوط به پیام رسان WhatsApp است. اینجا هم مانند مثال قبل، آیتم های لیست دارای عناصر مشترکی هستند. یک تصویر پروفایل، نام شخص و چند کلمه ابتدای پیغام دریافتی. در سمت راست نیز زمان دریافت پیغام مشخص شده است.
برای ساخت و نمایش لیست ها در RecyclerView مانند ListView ابتدا باید یک قالب کلی برای نمایش یک آیتم طراحی کنیم. سپس اطلاعاتی که قصد نمایش آنها در قالب لیست داریم را یک به یک به قالب از قبل طراحی شده می‌فرستیم تا همگی با یک فرمت و ظاهر مشخص و یکسان نمایش داده شوند.
بنابراین در RecyclerView امکان شخصی سازی را داریم و آیتم ها از ساده ترین حالت یعنی یک text تا یک آیتم پیچیده (ترکیبی از تصویر، متن، لینک و…) قابل پیاده سازی هستند. داده ها از منابعی مانند آرایه ها،دیتابیس، سرور و… به RecyclerView منتقل و نمایش داده می شوند.
توصیه می کنم قبل از مطالعه این مبحث حتما یکبار مبحث ListView را نیز مطالعه و تمرین کنید تا تفاوت بین این دو ویجت بهتر برایتان ملموس شود. ضمن اینکه برخی از جزئیات که در هردو مبحث مشترک است را در اینجا بطور خلاصه بیان می کنم.
در اندروید استودیو یک پروژه جدید با نام RecyclerView و یک Empty Activity ایجاد می کنم. برای استفاده از RecyclerView باید کتابخانه آنرا به پروژه اضافه کنیم که از کتابخانه های متعلق به support است. مطابق مباحث گذشته، کتابخانه را به دو صورت اضافه کردن دستی به فایل build.gradle و یا مسیر:

File > Project Structure > Modules (app) > Dependencies > + > Library Dependency

به پروژه اضافه می کنم:

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'com.android.support:appcompat-v7:27.0.2'
    implementation 'com.android.support:recyclerview-v7:27.0.2'
}

پس از سینک شدن موفق پروژه، یک ویجت/کامپوننت از نوع RecyclerView در لایه اکتیویتی اصلی پروژه یعنی activity_main.xml تعریف می کنم:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="ir.android_studio.recyclerview.MainActivity">

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

</RelativeLayout>

سپس RecyclerView را به فایل جاوا اکتیویتی اضافه می کنم:

MainActivity.java:

package ir.android_studio.recyclerview;

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

public class MainActivity extends AppCompatActivity {

    RecyclerView myRecycler;

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

        myRecycler = findViewById(R.id.recycler_view);

    }
}
نکته: در نسخه ۳ اندروید استودیو و به بعد، عمل Casting در تعریف کردن view ها در کلاس جاوا الزامی نیست. با اینحال چنانچه با ارور مواجه شدید لازم است Casting را انجام دهید. یعنی در اینجا باید (RecyclerView) به قبل از findViewById اضافه شود:

myRecycler = (RecyclerView) findViewById(R.id.recycler_view);

مانند مبحث ListView برای مدیریت ویجت / view ها و ارتباط آنها با عملگرهای جاوا، از کلاسی استفاده می کنیم که اصطلاحا Model نامیده می شود. استفاده از چنین کلاسی الزامی نیست اما باعث می شود مدیریت بهتری روی پروژه داشته باشیم. در کلاس Model از متدهایی که اصطلاحا Getter و Setter نامیده می شوند برای مقداردهی به متغیرها و یا مقدارگیری از آنها استفاده می شود.
برای ساخت Model، یک کلاس با نام دلخواه Item به پکیج اصلی پروژه اضافه می کنم:

اضافه کردن کلاس Model به پروژه اندروید

package ir.android_studio.recyclerview;

public class Item {

    private int uAvatar;
    private String uName;
    private String uMessage;
    private String uTime;

}

می خواهم هر یک از آیتم های RecyclerView حاوی یک تصویر پروفایل، نام، متن پیغام و تاریخ ارسال باشد.
چهار متغیر در کلاس تعریف کردم که یکی برای نمایش تصویر شخص و سه مورد دیگر برای نمایش نام، متن پیغام و تاریخ استفاده خواهد شد. در ادامه متدهای سازنده (Constructor) و همچنین Getter & Setter را به کلاس مدل اضافه می کنم:

package ir.android_studio.recyclerview;

public class Item {

    private int uAvatar;
    private String uName;
    private String uMessage;
    private String uTime;

    public Item(int uAvatar, String uName, String uMessage, String uTime) {
        this.uAvatar = uAvatar;
        this.uName = uName;
        this.uMessage = uMessage;
        this.uTime = uTime;
    }

    public int getuAvatar() {
        return uAvatar;
    }

    public void setuAvatar(int uAvatar) {
        this.uAvatar = uAvatar;
    }

    public String getuName() {
        return uName;
    }

    public void setuName(String uName) {
        this.uName = uName;
    }

    public String getuMessage() {
        return uMessage;
    }

    public void setuMessage(String uMessage) {
        this.uMessage = uMessage;
    }

    public String getuTime() {
        return uTime;
    }

    public void setuTime(String uTime) {
        this.uTime = uTime;
    }
    
}

جزئیات مربوط به نحوه افزودن این متدها به کلاس را در مبحث ListView توضیح داده ام.
در ابتدای مبحث گفتیم برای نمایش آیتم های لیست از یک قالب مشترک استفاده می شود که هر آیتم با همان فرمت و چینش طراحی به RecyclerView فرستاده می شود. من دقیقا همان قالبی که در مبحث ListView (لیست سفارشی) طراحی کرده بودم را در این پروژه استفاده می کنم. یک فایل با نام list_item به دایرکتوری layout پروژه اضافه می کنم:

ساخت layout نمایش آیتم RecyclerView

<?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="100dp"
    android:padding="5dp"
    android:orientation="horizontal">

    <ImageView
        android:id="@+id/img_avatar"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="0.2"
        android:src="@drawable/avatar_1" />

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="0.8"
        android:orientation="vertical">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="0.5"
            android:orientation="horizontal">

            <TextView
                android:id="@+id/txt_name"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="bottom"
                android:paddingLeft="5dp"
                android:text="Name" />

            <TextView
                android:id="@+id/txt_time"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="bottom|right"
                android:text="Time" />
        </LinearLayout>

        <TextView
            android:id="@+id/txt_message"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_marginTop="5dp"
            android:layout_weight="0.5"
            android:paddingLeft="5dp"
            android:text="Message"
            android:textColor="#161111" />

    </LinearLayout>

</LinearLayout>

این لایه شامل یک ImageView و سه TextView است. یک تصویر هم در دایرکتوری drawable برای نمایش مکان آواتار در قسمت Preview اندروید استودیو قرار دادم.خروجی list_item.xml در بخش پیش نمایش به اینصورت است:

پیش نمایش لایه آیتم RecyclerView

در مرحله بعد برای مدیریت view ها و ارسال داده های دریافتی (شامل آدرس تصویر پروفایل، نام، متن پیغام و تاریخ) به آنها، نیاز به ساخت یک کلاس Adapter داریم. بنابراین یک کلاس دیگر با نام دلخواه ItemAdapter به پکیج اصلی پروژه اضافه می کنم:

ساخت کلاس Adapter برای RecyclerView

اگر بخاطر داشته باشید در ListView برای بهینه تر شدن برنامه، یک ViewHolder تعریف کردیم. در RecyclerView هم این مورد درون کتابخانه تعبیه شده و برای تعریف view ها باید از ViewHolder استفاده کنیم. داخل کلاس آداپتر قبل از هرکار ابتدا یک کلاس داخلی (inner class) برای ViewHolder ایجاد و آنرا تکمیل می کنم:

package ir.android_studio.recyclerview;

import android.support.v7.widget.RecyclerView;

public class ItemAdapter {

    public class MyViewHolder extends RecyclerView.ViewHolder {}

}

یک کلاس داخلی با نام دلخواه MyViewHolder به کلاس ItemAdapter اضافه کردم. این کلاس باید از کلاس ViewHolder ارث بری شود و با توجه به اینکه ViewHolder در کتابخانه RecyclerView تعریف شده، کلاس را باید از RecyclerView.ViewHolder ارث بری کنیم.
View ها را داخل MyViewHolder تعریف می کنم:

public class MyViewHolder extends RecyclerView.ViewHolder {

    public ImageView aAvatar;
    public TextView aName;
    public TextView aMessage;
    public TextView aTime;

}

در بدنه کلاس MyViewHolder خطایی مشاهده می شود که با قراردادن نشانگر روی آن و زدن alt+Enter از ما می خواهد متد سازنده را به کلاس اضافه کنیم:

متد سازنده ViewHolder

public class MyViewHolder extends RecyclerView.ViewHolder {

    public ImageView aAvatar;
    public TextView aName;
    public TextView aMessage;
    public TextView aTime;


    public MyViewHolder(View itemView) {
        super(itemView);
    }
}

حالا view ها را درون متد سازنده به متغیر مربوط به خودشان متصل می کنم:

public class MyViewHolder extends RecyclerView.ViewHolder {

    public ImageView aAvatar;
    public TextView aName;
    public TextView aMessage;
    public TextView aTime;


    public MyViewHolder(View itemView) {
        super(itemView);
        
        aAvatar = itemView.findViewById(R.id.img_avatar);
        aName = itemView.findViewById(R.id.txt_name);
        aMessage = itemView.findViewById(R.id.txt_message);
        aTime = itemView.findViewById(R.id.txt_time);
    }
}

ملاحظه می کنید در اینجا قبل از findViewById به واسطه itemView که از جنس View است تعریف شده و به تنهایی نمی توان استفاده کرد. در واقع itemView شامل هریک از آیتم های RecyclerView می باشد. فعلا با MyViewHolder کاری ندارم.
پس از ساخت ViewHolder، لازم است کلاس ItemAdapter از RecyclerView.Adapter ارث بری شود:

ارث بری کلاس Adapter از RecyclerView.Adapter

مشاهده می کنید در لیست پیشنهادی، در مقابل Adapter عبارت VH (مخفف ViewHolder) قید شده به این معنی که در اینجا باید کلاسی که از ViewHolder اصطلاحا کاستوم کردیم را وارد کنیم:

ارث بری کلاس Adapter از RecyclerView.Adapter

در نهایت کلاس به اینصورت تکمیل می شود:

package ir.android_studio.recyclerview;

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

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

    public class MyViewHolder extends RecyclerView.ViewHolder {

        public ImageView aAvatar;
        public TextView aName;
        public TextView aMessage;
        public TextView aTime;


        public MyViewHolder(View itemView) {
            super(itemView);

            aAvatar = itemView.findViewById(R.id.img_avatar);
            aName = itemView.findViewById(R.id.txt_name);
            aMessage = itemView.findViewById(R.id.txt_message);
            aTime = itemView.findViewById(R.id.txt_time);
        }
    }

}

مجدد در بدنه کلاس اصلی هم خطا میگیرم که با alt+Enter متدهای ضروری Adapter را اضافه می کنم:

متدهای Adapter

متدهای Adapter

package ir.android_studio.recyclerview;

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

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

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return null;
    }

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

    }

    @Override
    public int getItemCount() {
        return 0;
    }

    public class MyViewHolder extends RecyclerView.ViewHolder {

        public ImageView aAvatar;
        public TextView aName;
        public TextView aMessage;
        public TextView aTime;


        public MyViewHolder(View itemView) {
            super(itemView);

            aAvatar = itemView.findViewById(R.id.img_avatar);
            aName = itemView.findViewById(R.id.txt_name);
            aMessage = itemView.findViewById(R.id.txt_message);
            aTime = itemView.findViewById(R.id.txt_time);
        }
    }

}

جهت مطالعه ادامه آموزش، فایل PDF را دانلود نمائید

توجه : سورس پروژه داخل پوشه Exercises قرار داده شده است.

با توجه به اینکه آموزشهای پایه با قیمت پایین در اختیار کاربر قرار گرفته و درآمد حاصل صرف تامین هزینه های وب سایت و تهیه آموزش های آتی می شود، به اشتراک گذاری این فایل با دیگران خلاف اخلاق خواهد بود.

دانلود نسخه کامل این آموزش به همراه سورس پروژه
تعداد صفحات : ۴۸
حجم : ۴ مگابایت
قیمت : ۱۸ هزار تومان
توجه: صرفا در صورتی از درگاه پشتیبان استفاده کنید که قادر به پرداخت از طریق سبد دانلود نباشید.
افزودن به سبد دانلود درگاه پشتیبان
این مطلب چقدر برایتان مفید بود؟ لطفا امتیاز دهید
3.6/5 - (14 امتیاز)
پرسش‌ها و دیدگاه‌های کاربران
دوره آموزش برنامه نویسی اندروید
دوره آموزش برنامه نویسی اندروید

با دریافت این دوره به تمامی آموزش‌های غیر رایگان و رایگان موجود در وب سایت دسترسی دارید که تخفیفی برای آموزش‌های غیر رایگان نیز درنظر گرفته شده. این پکیج به دو صورت دانلودی و ارسال پستی ارائه می‌گردد.
آموزش‌های اندروید استودیو در دو دسته «پایه» و «تکمیلی» منتشر می‌شوند.
آموزش‌های پایه شامل مباحث اصلی و ضروری و آموزش‌های تکمیلی مطالبی است که می‌بایست در کنار مطالب اصلی بررسی شود.
با خرید این دوره، به تمامی آموزش‌های غیر رایگانی که در آینده منتشر می‌شود نیز به صورت رایگان دسترسی خواهید داشت!

یک دیدگاه بنویسید

پرسش‌های زیر تایید و پاسخ داده نـــخواهند شد:
۱: جزء موارد مطرح شده در صفحات مشکلات و پرسش‌های رایج و بروزرسانی‌های محتوای آموزشی باشد
۲: سوال قبلا توسط کاربران در دیدگاه‌ها مطرح و پاسخ داده شده باشد
۳: پرسش خارج از مبحث آموزشی موجود در این صفحه باشد