آموزش Expandable RecyclerView در اندروید با کاتلین ، در این پست آموزشی ما یکی از پرکاربردترین ابزارهای اندروید یعنی RecyclerView رو داریم که میخوایم اون رو بصورت Expandable یا قابل گسترش دربیاریم !
کتابخونه هایی هستند که این کا ررو برای ما انجام میدن اما خب چکاریه زمانی که میتونیم خودمون اون رو پیاده و ایجاد کنیم بریم و کتایخونه ای به پروژه ایمپورت کنیم که حجم رو بالا میبره و هم ممکنه مشکلات دیگری از قبیل تداخب کتابخونه ها برامون بوجود بیاره(البته این مشکلات در خیلی استفاده از خیلی از کتابخانه ها بوجود نمیاد اما همیشه پیشگیری بهتر از درمانه 😀 ).
ما تو این آموزش لیست قابل گسترش یا Expandable رو با کاستومایز کردن اداپتر و دیتا مدل مربوط به آیتم های لیست بصورت زیر ایجاد میکنیم.
خب بریم سراغ کدزدن 😉
اولین کاری که باید انجام بدیم این است که یک کلاس مدل برای دیتاهایی که میخوایم داخل لیست نمایش بدیم ایجاد کنیم که در کاتلین با کلمه کلیدی data model ایجاد میشه
نکته جالب درباره کلاس های کاتلین این است که دیگه خبری از getter , setter ها (یا اصطلاحا boilerplate code ها ) نیست بلکه کافیه از خود پراپرتی ها استفاده کنیم و فقط درصورتی که مثلا بخواهیم ادیتی در getter دهیم آنرا ایجاد میکنیم.
package dn.marjan.bosscompanysample data class Users(var id: Int,var name: String,var family: String , var age: Int,var city: String ,var isSubFalg: Boolean)
<?xml version="1.0" encoding="utf-8"?> <resources> <color name="colorPrimary">#801515</color> <color name="colorPrimaryDark">#550000</color> <color name="colorAccent">#D46A6A</color> <color name="white">#ffffff</color> </resources>
من یک فایل استایل برا ردیوس دار کردن کانسترینت لیوت آیتم لیست نیز ایجاد کردم که بصورت زیر هستش
<?xml version="1.0" encoding="UTF-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"> <!--<solid android:color="#FFFFFF"/>--> <!--<stroke android:width="3dp" android:color="#B1BCBE" />--> <corners android:radius="10dp"/> <padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" /> </shape>
در ادامه این فایل را با پراپرتی background به کانسرینت لیوت اختصاص دادم.
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <android.support.v4.widget.NestedScrollView android:layout_width="match_parent" android:layout_height="match_parent" android:fillViewport="true"> <android.support.constraint.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v7.widget.RecyclerView android:id="@+id/rcv_pms" android:layout_width="match_parent" android:layout_height="0dp" android:layout_marginTop="8dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </android.support.constraint.ConstraintLayout> </android.support.v4.widget.NestedScrollView> </android.support.constraint.ConstraintLayout>
ما در این لایه دو ConstraintLayout داخلی و یک ConstraintLayout روت داریم که آن دو کانسترینت داخلی اهداف ما هستند و در اداپتر با آنها سروکار داریم.
بدین صورت که یکی از آنها که نام layout_sub_header بطور پیشفرض نمایش داده نمیشود بلکه زمانی که کاربر روی کانسترینت با نام layout_header کلیک کرد ، آن لایه نمایش داده میشود که این روند در اداپتر با استفاده از فیلد isSubFlag انجام میپذیرد (در ادامه خواهیم دید)
<?xml version="1.0" encoding="utf-8"?> <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="10dp" app:cardCornerRadius="15dp"> <android.support.constraint.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/radios_style" > <android.support.constraint.ConstraintLayout android:id="@+id/layout_header" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/colorAccent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> <TextView android:id="@+id/textView8" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginLeft="16dp" android:layout_marginTop="16dp" android:layout_marginEnd="16dp" android:layout_marginRight="16dp" android:text="شناسه :" android:textColor="@color/white" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toEndOf="@+id/txv_id" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/txv_id" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="TextView" android:textColor="@color/white" app:layout_constraintBottom_toBottomOf="@+id/textView8" app:layout_constraintEnd_toStartOf="@+id/textView8" app:layout_constraintHorizontal_bias="1.0" app:layout_constraintHorizontal_chainStyle="packed" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="@+id/textView8" /> <TextView android:id="@+id/textView10" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginLeft="16dp" android:layout_marginTop="16dp" android:layout_marginEnd="16dp" android:layout_marginRight="16dp" android:text="نام :" android:textColor="@color/white" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toEndOf="@+id/txv_name" app:layout_constraintTop_toBottomOf="@+id/textView8" /> <TextView android:id="@+id/txv_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="TextView" android:textColor="@color/white" app:layout_constraintBottom_toBottomOf="@+id/textView10" app:layout_constraintEnd_toStartOf="@+id/textView10" app:layout_constraintHorizontal_bias="1.0" app:layout_constraintHorizontal_chainStyle="packed" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="@+id/textView10" /> <TextView android:id="@+id/textView12" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginLeft="16dp" android:layout_marginTop="16dp" android:layout_marginEnd="16dp" android:layout_marginRight="16dp" android:layout_marginBottom="16dp" android:text="نام خانوادگی :" android:textColor="@color/white" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toEndOf="@+id/txv_family" app:layout_constraintTop_toBottomOf="@+id/textView10" /> <TextView android:id="@+id/txv_family" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="TextView" android:textColor="@color/white" app:layout_constraintBottom_toBottomOf="@+id/textView12" app:layout_constraintEnd_toStartOf="@+id/textView12" app:layout_constraintHorizontal_bias="1.0" app:layout_constraintHorizontal_chainStyle="packed" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="@+id/textView12" /> </android.support.constraint.ConstraintLayout> <android.support.constraint.ConstraintLayout android:id="@+id/layout_sub_header" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="13dp" android:visibility="visible" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/layout_header" app:layout_constraintVertical_bias="0.0"> <TextView android:id="@+id/textView16" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginLeft="16dp" android:layout_marginTop="16dp" android:layout_marginEnd="16dp" android:layout_marginRight="16dp" android:text="سن :" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toEndOf="@+id/txv_age" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/txv_age" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="TextView" app:layout_constraintBottom_toBottomOf="@+id/textView16" app:layout_constraintEnd_toStartOf="@+id/textView16" app:layout_constraintHorizontal_bias="1.0" app:layout_constraintHorizontal_chainStyle="packed" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="@+id/textView16" /> <TextView android:id="@+id/textView20" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginLeft="16dp" android:layout_marginTop="16dp" android:layout_marginEnd="16dp" android:layout_marginRight="16dp" android:layout_marginBottom="8dp" android:text="شهر :" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toEndOf="@+id/txv_city" app:layout_constraintTop_toBottomOf="@+id/textView16" /> <TextView android:id="@+id/txv_city" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="TextView" app:layout_constraintBottom_toBottomOf="@+id/textView20" app:layout_constraintEnd_toStartOf="@+id/textView20" app:layout_constraintHorizontal_bias="1.0" app:layout_constraintHorizontal_chainStyle="packed" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="@+id/textView20" /> </android.support.constraint.ConstraintLayout> </android.support.constraint.ConstraintLayout> </android.support.v7.widget.CardView>
حالا فقط کافیه که کلاس اداپتر مربوط به ریسایکلرویو رو بسازیم
نکته خیلی مهم در این کلاس استفاه از متغیر isSubFlag دیتامدل هست که ما دراداپتر براساس این متغیر تصمیم میگیریم که یک آیتم کلیک شده(لایه layout_sub_header باز شده) یا خیر که پیشفرض برای تمام آیتم ها False به معنی بسته خواهد بود
package dn.marjan.bosscompanysample import android.support.v7.widget.RecyclerView import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import kotlinx.android.synthetic.main.item_user.view.* class UsersAdapter(val usersList: ArrayList<Users>): RecyclerView.Adapter<UsersAdapter.MyViewHolder>() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { val view = LayoutInflater.from(parent.context) .inflate(R.layout.item_user,parent,false) return MyViewHolder(view) } override fun getItemCount(): Int = usersList.size override fun onBindViewHolder(holder: MyViewHolder, position: Int) { val user = usersList[position] holder.setItems(user) //decide that sub item should show or hidden if (user.isSubFalg) holder.itemView.layout_header.visibility = View.VISIBLE else holder.itemView.layout_sub_header.visibility =View.GONE //when click on item to show or hidden sub item holder.view.layout_header.setOnClickListener { if (user.isSubFalg){ holder.itemView.layout_sub_header.visibility = View.GONE user.isSubFalg = false }else{ holder.itemView.layout_sub_header.visibility =View.VISIBLE user.isSubFalg = true } } } class MyViewHolder(val view: View): RecyclerView.ViewHolder(view) { fun setItems(user: Users) { view.txv_age.text = user.age.toString() view.txv_name.text = user.name view.txv_family.text = user.family view.txv_id.text = user.id.toString() view.txv_city.text = user.city } } }
و در انتها نیزفقط کلاس اکتیویتی اصلی مونده که در اون فقط آرایه ای برای نمایش در لیست ایجاد و مقدار دهی میکنیم
package dn.marjan.bosscompanysample import android.support.v7.app.AppCompatActivity import android.os.Bundle import android.support.v7.widget.LinearLayoutManager import kotlinx.android.synthetic.main.layout_base.* class BaseActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.layout_base) rcv_pms.layoutManager = LinearLayoutManager(this) setData() } private fun setData() { val list = ArrayList<Users>() list.add(Users(1, "ali", "asadi", 32, "shiraz",false)) list.add(Users(1, "reza", "shahedi", 12, "esfahan",false)) list.add(Users(1, "saeed", "hamedi", 24, "tehran",false)) list.add(Users(1, "hamid", "jolan", 33, "tabriz",false)) list.add(Users(1, "kave", "javadi", 32, "yaz",false)) list.add(Users(1, "javad", "saeed", 21, "gilan",false)) list.add(Users(1, "kian", "ilani", 22, "zanjan",false)) val adapter = UsersAdapter(list) rcv_pms.adapter = adapter } }
در انتها برنامه رو ران کنید وبا کلیک روی هرآیتم (layout_header) خواهید دید لایه زیرین آن (layout_sub_header) باز خواهد شد.
امیدوارم این پست براتون مفید بوده باشه و ازش استفاده کنید. از طریق راه های ارتباطی زیر میتونید با ما برای درخواست آموزش های اختصاصی و انواع پروژه ها در ارتباط باشید.