آموزش Expandable RecyclerView در برنامه نویسی اندروید
سلام دوستان در این سری از آموزش های برنامه نویسی اندروید به آموزش Expandable RecyclerView در برنامه نویسی اندروید می پردازیم همان طور که از نام معلوم است زمانی که بروی Recyclerview کلیک شود گشترش داده می شود همینطور به نام expanding and collapsing (گسترش و سقوط ) نیز معروف هستند شاید کمی گیج شده باشید بهتر از عکس متحرک زیر را مشاهده کنید تا کمی بیشتر با آن آشنا شوید در ادامه با ما همراه باشید.
همانطور که میبینید متریال دیزان نیز هست ! به صورت معمول Expandable RecyclerView ها دکمه ای برای نمایش رو به پایین و در زمان باز شدن دکمه ای برای نمایش رو به بالا ندارند.
ابتدا باید دو کتاب خانه را به پروژه خود اضافه کنیم اولی که همان RecyclerView است و دومی هم Expandable RecyclerView است ما برای متریال دیزان بودن آن از این کتاب خانه استفاده کرده ایم.
پس وارد فایل Build.gradle شده (module) در قسمت dependencies کتاب خانه های زیر را اضافه کنید.
1 2 | compile 'com.android.support:recyclerview-v7:23.1.1' compile 'com.thoughtbot:expandablerecyclerview:1.3' |
سپس پروژه را sync کنید (همگان سازی) ما قبلا علت خطای گریدل را بررسی کرده ایم در صورتی که به خطایی بر خوردید در سایت جستجو کنید.
expandable لیست ها دو نوع view دارند اولی گروه را مشخص می کند و دومی child یا فرزند را مشخص می کند اگر بالا عکس متحرک بالا را نگاه کنید ios , android و windowsphone گروه را تشکیل داده اند و زمانی که بروی آن کلیک می شود child های آن نمایش داده می شود.
پس یک کلاس به نام MobileOS.java که group ما رو تشکیل می دهد ایجاد کنید کد های زیر را در آن قرار دهید.
1 2 3 4 5 6 7 8 9 10 | package ir.programchi; import android.annotation.SuppressLint; import com.thoughtbot.expandablerecyclerview.models.ExpandableGroup; import java.util.List; @SuppressLint("ParcelCreator") public class MobileOS extends ExpandableGroup<Phone> { public MobileOS(String title, List<Phone> items) { super(title, items); } } |
حالا باید یک کلاس دیگر برای child های آنها ایجاد کنیم.
پس یک کلاس به نام Phone.java ایجاد کنید و کدهای زیر را در آن قرار دهید.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | package ir.programchi; import android.os.Parcel; import android.os.Parcelable; public class Phone implements Parcelable{ private String name; public Phone(Parcel in) { name = in.readString(); } public String getName() { return name; } public void setName(String name) { this.name = name; } public Phone(String name) { this.name = name; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(name); } @Override public int describeContents() { return 0; } public static final Creator<Phone> CREATOR = new Creator<Phone>() { @Override public Phone createFromParcel(Parcel in) { return new Phone(in); } @Override public Phone[] newArray(int size) { return new Phone[size]; } }; } |
حالا باید برای کلاس های بالا ViewHolder نگه داریم همانطور که از اسمش معلوم است view های ما را نگه میدارد .
پس یک کلاس به نام OSViewHolder.java ایجاد کرده کدهای زیر را در آن قرار دهید.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | package ir.programchi; import android.util.Log; import android.view.View; import android.widget.TextView; import com.thoughtbot.expandablerecyclerview.models.ExpandableGroup; import com.thoughtbot.expandablerecyclerview.viewholders.GroupViewHolder; import info.devexchanges.expandablerecyclerview.R; public class OSViewHolder extends GroupViewHolder { private TextView osName; public OSViewHolder(View itemView) { super(itemView); osName = (TextView) itemView.findViewById(R.id.mobile_os); } @Override public void expand() { osName.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.down_arrow, 0); Log.i("Adapter", "expand"); } @Override public void collapse() { Log.i("Adapter", "collapse"); osName.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.up_arrow, 0); } public void setGroupName(ExpandableGroup group) { osName.setText(group.getTitle()); } } |
در بالا ما از دو عکس arrow بالا و پایین استفاده کردیم یعنی زمانی که در حالت expand و collapse است دارای عکس های مخصوصی هستند شما می توانید عکس ها را از زیر دریافت کرده و در پوشه drawbale خود قرار دهید (یا عکس های خودتان را استفاده کنید)
حالا باید یک viewholder برای child هامون درست کنیم.
پس یک فایل به نام PhoneViewHolder.java ایجاد کنید کد های زیر را در آن قرار دهید.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | package ir.programchi; import android.view.View; import android.widget.TextView; import com.thoughtbot.expandablerecyclerview.models.ExpandableGroup; import com.thoughtbot.expandablerecyclerview.viewholders.ChildViewHolder; import ir.programchi.R; import ir.programchi.Phone; public class PhoneViewHolder extends ChildViewHolder { private TextView phoneName; public PhoneViewHolder(View itemView) { super(itemView); phoneName = (TextView) itemView.findViewById(R.id.phone_name); } public void onBind(Phone phone, ExpandableGroup group) { phoneName.setText(phone.getName()); if (group.getTitle().equals("Android")) { phoneName.setCompoundDrawablesWithIntrinsicBounds(R.drawable.nexus, 0, 0, 0); } else if (group.getTitle().equals("iOS")) { phoneName.setCompoundDrawablesWithIntrinsicBounds(R.drawable.iphone, 0, 0, 0); } else { phoneName.setCompoundDrawablesWithIntrinsicBounds(R.drawable.window_phone, 0, 0, 0); } } } |
در بالا یکسری عکس قرار دهید (این عکس های برای هر child در نظر گرفته می شود عکس متحرک بالا را که نگاه کنید متوجه می شوید.)
حال باید برای هر کدام از group و child یک فایل xml که view آنها را تشکیل می دهد ایجاد کنیم.
پس یک فایل در پوشه layout خود به نام group_view_holder.xml ایجاد کنید کد های زیر را در آن قرار دهید.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@android:color/black"> <TextView android:id="@+id/mobile_os" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerInParent="true" android:drawablePadding="5dp" android:drawableRight="@drawable/down_arrow" android:gravity="left|center" android:padding="8dp" android:textColor="#e6e600" /> </RelativeLayout> |
حالا یک فایل به نام child_view_holder.xml برای child خودمون درست می کنیم و کد های زیر را در آن قرار می دهیم.
1 2 3 4 5 6 7 8 9 10 11 | <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/phone_name" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerInParent="true" android:padding="10dp" /> </RelativeLayout> |
حالا باید یک آداپتور سفارشی برای RecyclerView درست کنیم فرق این آداپتور با بقیه آداپتور در دوبار به دست آوردن view یا Viewholder است به صورت معمول ما فقط می تونیم یکی از آنها را تنظیم کنیم ولی با کمک کتاب خانه دومی ما RecyclerAdapter را به ExpandableRecyclerViewAdapter گسترش دادیم و مقدار ورودی آن را دوتا کردیم پس باید یک فایل به نام RecyclerAdapter.java ایجاد کنید کد های زیر را در آن قرار دهید.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | package ir.programchi; import android.app.Activity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import com.thoughtbot.expandablerecyclerview.ExpandableRecyclerViewAdapter; import com.thoughtbot.expandablerecyclerview.models.ExpandableGroup; import java.util.List; import ir.programchi.R; import ir.programchi.MobileOS; import ir.programchi.Phone; import ir.programchi.OSViewHolder; import ir.programchi.PhoneViewHolder; public class RecyclerAdapter extends ExpandableRecyclerViewAdapter<OSViewHolder, PhoneViewHolder> { private Activity activity; public RecyclerAdapter(Activity activity, List<? extends ExpandableGroup> groups) { super(groups); this.activity = activity; } @Override public OSViewHolder onCreateGroupViewHolder(ViewGroup parent, int viewType) { LayoutInflater inflater = (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE); View view = inflater.inflate(R.layout.group_view_holder, parent, false); return new OSViewHolder(view); } @Override public PhoneViewHolder onCreateChildViewHolder(ViewGroup parent, final int viewType) { LayoutInflater inflater = (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE); View view = inflater.inflate(R.layout.child_view_holder, parent, false); return new PhoneViewHolder(view); } @Override public void onBindChildViewHolder(PhoneViewHolder holder, int flatPosition, ExpandableGroup group, int childIndex) { final Phone phone = ((MobileOS) group).getItems().get(childIndex); holder.onBind(phone,group); } @Override public void onBindGroupViewHolder(OSViewHolder holder, int flatPosition, ExpandableGroup group) { holder.setGroupName(group); } } |
حالا زمان درست کردن فایل activity_main.xml است پس یک فایل با همین نام ایجاد کرده و RecyclerView را در آن قرار دهید.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <?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" 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="info.devexchanges.expandablerecyclerview.MainActivity"> <android.support.v7.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="wrap_content" /> </RelativeLayout> |
برای اینکه حالت expand/collapse را ذخیره کنیم از onSaveInstanceState()
و onRestoreInstanceState() استفاده کردیم.
حالا یک فایل به نام MainActivity.java ایجاد کرده کد های زیر را در آن قرار دهید.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | package ir.programchi; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import java.util.ArrayList; import ir.programchi.RecyclerAdapter; import ir.programchi.MobileOS; import ir.programchi.Phone; public class MainActivity extends AppCompatActivity { private RecyclerView recyclerView; private ArrayList<MobileOS> mobileOSes; private RecyclerAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); recyclerView = (RecyclerView) findViewById(R.id.recycler_view); mobileOSes = new ArrayList<>(); setData(); LinearLayoutManager layoutManager = new LinearLayoutManager(this); recyclerView.setLayoutManager(layoutManager); adapter = new RecyclerAdapter(this, mobileOSes); recyclerView.setAdapter(adapter); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); adapter.onSaveInstanceState(outState); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); adapter.onRestoreInstanceState(savedInstanceState); } private void setData() { ArrayList<Phone> iphones = new ArrayList<>(); iphones.add(new Phone("iPhone 4")); iphones.add(new Phone("iPhone 4S")); iphones.add(new Phone("iPhone 5")); iphones.add(new Phone("iPhone 5S")); iphones.add(new Phone("iPhone 6")); iphones.add(new Phone("iPhone 6Plus")); iphones.add(new Phone("iPhone 6S")); iphones.add(new Phone("iPhone 6S Plus")); ArrayList<Phone> nexus = new ArrayList<>(); nexus.add(new Phone("Nexus One")); nexus.add(new Phone("Nexus S")); nexus.add(new Phone("Nexus 4")); nexus.add(new Phone("Nexus 5")); nexus.add(new Phone("Nexus 6")); nexus.add(new Phone("Nexus 5X")); nexus.add(new Phone("Nexus 6P")); nexus.add(new Phone("Nexus 7")); ArrayList<Phone> windowPhones = new ArrayList<>(); windowPhones.add(new Phone("Nokia Lumia 800")); windowPhones.add(new Phone("Nokia Lumia 710")); windowPhones.add(new Phone("Nokia Lumia 900")); windowPhones.add(new Phone("Nokia Lumia 610")); windowPhones.add(new Phone("Nokia Lumia 510")); windowPhones.add(new Phone("Nokia Lumia 820")); windowPhones.add(new Phone("Nokia Lumia 920")); mobileOSes.add(new MobileOS("iOS", iphones)); mobileOSes.add(new MobileOS("Android", nexus)); mobileOSes.add(new MobileOS("Window Phone", windowPhones)); } } |
در ابتدا view مرتبط با RecyclerView را به دست آوردیم سپس یک آرایه از model که در قبل ساختیم (mobileOSes) که به group ازش نام بردیم ایجاد کردیم تا داده ها را در آن قرار دهیم همینطور یک آرایه از child هامون درست کردیم و کار void که نامش setData است داده های sample را در model های ما قرار می دهد و در آخر آداپتور رو در Recyclerview ست کردیم.
دوستان در بالا برای هر child کلیک Listener تعریف نشده برای اینکه برای آنها Click Listener تعریف کنید می تونید از کد زیر استفاده کنید.
1 2 3 4 5 6 | adapter.setChildClickListener(new OnCheckChildClickListener() { @Override public void onCheckChildCLick(View v, boolean checked, CheckedExpandableGroup group, int childIndex) { } }); |
این آموزش هم به پایان رسید.
موفق و موید باشید.
سلام میشه لطفن کلیک روی آیتم ها رو هم توضیح بدین setChildClickListener
بله قرار میدهیم.
آیا امکانش هست مثالی از expandable recyclerview از منبع دیتابیس و یا سرور mysql بذارید
قرار خواهیم داد.
برای فردا یا امشب اگر وقت کنم قرار می دهم.
آیا امکانش وجود دارد مثلی از expanadable recyclerview با منبع دیتابیس sqlite بذارید ؟؟؟
من هرچقدر جستجو کردم نتونستم مثالی پیدا کنم
سلام دوست عزیز این اموزش قبلا در سایت قرار گرفته میتونید از لینک زیر استفاده کنید:
https://programchi.ir/2017/07/14/%d8%a2%d9%85%d9%88%d8%b2%d8%b4-%d9%82%d8%b1%d8%a7%d8%b1-%d8%af%d8%a7%d8%af%d9%86-%d8%a7%d8%b7%d9%84%d8%a7%d8%b9%d8%a7%d8%aa-sqlite-%d8%af%d8%b1-recyclerview-%d8%af%d8%b1-%d8%a8%d8%b1%d9%86%d8%a7%d9%85/
دوست عزیز بیزحمت آموزش expandable recyclerView که اطلاعاتاشو از sqlite میگیره رو قرار بدهید…
این آموزشا که همش کپیه و تو github هستش!
اموزش ها از جایی کپی نمی شوند و کد ها هم چیز نیستند که بگید کپی هستند.
این اموزش هم میتونه براتون مفید باشه:
https://programchi.ir/2017/07/14/%d8%a2%d9%85%d9%88%d8%b2%d8%b4-%d9%82%d8%b1%d8%a7%d8%b1-%d8%af%d8%a7%d8%af%d9%86-%d8%a7%d8%b7%d9%84%d8%a7%d8%b9%d8%a7%d8%aa-sqlite-%d8%af%d8%b1-recyclerview-%d8%af%d8%b1-%d8%a8%d8%b1%d9%86%d8%a7%d9%85/
سلام کارهایی که گفتید انجام دادم و ارور زیر رو میده ؟؟
http://uupload.ir/files/u5f2_screenshot_(319).png
سلام خط زیر را
همانند زیر تغییر دهید.
باید خطا رفع شود.
ممنون قسمتی از ارور رفع شد اما هنوز ارور زیر هستش
http://uupload.ir/files/fool_screenshot_(329).png
سلام مجدد شما باید ArrayList در عکس بالا به List تغییر دهید.
سلام ممنون از اموزشتون آیا این لیست امکان ادد و یا دیلیت کردن ایتم بعد از نشون دادن لیست رو داره ؟ میثل خوده ریسایکلر ویو واگر بله چطور؟
ممنون میشم جواب بدید
من به لیستم اضافه میکنم و handler.notifyDataSetChange رو هم صدا میزنم ولی باز ارور میده و نمیتونه اضافه کنه این ارور رو میده
java.lang.ArrayIndexOutOfBoundsException: length=1; index=1
خب آرایه شما کوچکتر از مقدار لیست است یعنی یکدونه کمه
ممنون از وقتیکه میذارید یعنی به همون روش معمول در recycler ها نمیتونیم بهش آیتم اضافه کنیم؟
تست کنید میگم فکر نمی کنم پشتیبانی بکنه چون بیشتر این کتابخانه ها بعد از چند مدت دیگه پشتیبانی نمی شوند.
موفق باشید.
سلام خیر این امکان رو پشتیبانی نمی کند در متدهاش باز نگاه می کنم ولی چیز خاصی مشاهده نکردم
سلام
میشه به زبان کاتلین هم این آموزش رو قرار بدید.
تشکر
سلام و درود
به راحتی می توانید تبدیل کنید.
موفق باشید.