


آموزش Expandable RecyclerView در اندروید
آموزش 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) باز خواهد شد.
امیدوارم این پست براتون مفید بوده باشه و ازش استفاده کنید. از طریق راه های ارتباطی زیر میتونید با ما برای درخواست آموزش های اختصاصی و انواع پروژه ها در ارتباط باشید.