Android/Kotlin

[Kotlin]RecyclerView 작성하기

망고키위 2023. 1. 22. 22:18

블로그 주인은 이제 안드로이드에 대해 공부를 시작하는 수준이다.

공부하면서 알았던 것들을 잊어버리지 않기 위해 작성한다.

RecyclerView 에 대해 적어보려고 한다.

RecyclerView는 Layout의 종류로 데이터 집합들을 각각의 개별 아이템 단위로 구성하여 화면에 출력해주는 뷰 그룹이며, 수 많은 데이터를 스크롤 가능한 리스트 형태로 표시해주는 위젯을 의미한다.

ListView와 동일한 형식이지만 RecyclerView는 ViewHolder 패턴을 이용하며 ItemLayout에서 가로,Grid형식을 지원하고

Decoration을 할때 RecyclerView.ItemDecoration 객체를 이용해서 구분선을 설정한다.

두 레이아웃의 가장 큰 차이점은 RecyclerView는 재사용성이 훨씬 좋다.

 

RecyclerView 주요 클래스

ViewHolder - 각각의 뷰를 보관하는 Holder 객체

LayoutManager - 아이템의 배치를 담당 (LinearLayoutManager, GridLayoutManager, StaggeredGridLayoutManager)

Adapter - ListView와 동일한 Adapter의 개념으로 View를 생성하는 기능을 담당

ItemAnimation - RecyclerView의 아이템을 꾸미는 역할을 한다. 

 

작성하는 법

우선 Activity를 생성하고 해당 Activity의 Layout에 RecyclerView를 추가한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:id="@+id/linearLayoutCompat"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".TowerlistActivity" >
 
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            aapp:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
            tools:listitem="@layout/tower_list" />
 
</LinearLayout>
cs

 

배치할 아이템 뷰를 만든다.

 

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
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/gamebtn1"
    android:paddingRight="10dp"
    android:paddingLeft="10dp">
 
    <TextView
        android:id="@+id/tv_tower"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="타워이름"
        android:textSize="15sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.042"
        app:layout_constraintStart_toEndOf="@+id/iv_tower"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.555" />
 
    <ImageView
        android:id="@+id/iv_tower"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@drawable/bonewormblack" />
</androidx.constraintlayout.widget.ConstraintLayout>
cs

 

어댑터를 작성하기위한 클래스를 추가하고 다음과 같이 ViewHolder를 만든다.

클릭이벤트를 위해 setOnClickListener를 작성하고 item의 data를 putExtra 하였다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class TowerViewHolder(v : View) : RecyclerView.ViewHolder(v){
    private  val profile: ImageView = itemView.findViewById(R.id.iv_tower)
    private  val text: TextView = itemView.findViewById(R.id.tv_tower)
 
    fun bind(item:Data){  //바인드 함수를 집어넣어서 저장된 요소와 뷰와 바인딩을 하였다.
        text?.text = item.name
        profile?.setImageResource(item.profile)
        itemView.setOnClickListener {
            val intent = Intent(itemView.context, TowerprofileActivity::class.java)
            itemView.context.startActivity(intent)
            intent.putExtra("data", item)
            itemView.context.startActivity(intent)
            println("넘어가기")
        }
        println("바인드함수")
 
 
    }
}
cs

 

Data 클래스 , 클래스를 putExtra 하기에 Serializable 함

 

1
2
 
class Data(val profile:Int, val name:String) : Serializable
cs

 

Adapter class를 만든다

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class TowerAdapter(val DataList:ArrayList<Data>) : RecyclerView.Adapter<TowerViewHolder> (){
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TowerViewHolder { // viewType 형태의 아이템 뷰를 위한 뷰홀더 객체 생성
        val cellForRow = LayoutInflater.from(parent.context).inflate(R.layout.tower_list,parent,false)
        println("온크리트뷰홀더")
        return TowerViewHolder(cellForRow)
    }
 
    override fun onBindViewHolder(holder: TowerViewHolder, position: Int) { //position에 해당하는 데이터를 뷰홀더의 아이템 뷰에 표시 얘는 내용을 수정하는 역할
        holder.bind(DataList[position])
        println("바인드뷰홀더")
    }
    override fun getItemCount(): Int { //전체 아이템 개수 리턴.
        return DataList.size
    }
}
cs

 

Activity에 아이템목록인 arrayList의 DataList를 만들었다. DataList를 adapter로 받아 구현함

 

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
class TowerlistActivity : AppCompatActivity() {
 
    val DataList = arrayListOf(
        Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"),
        Data(R.drawable.bonewormgreen, "초록지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"),
        Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"),
        Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"),
        Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"),
        Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"),
        Data(R.drawable.bonewormgreen, "초록지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"),
        Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"),
        Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"),
        Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"),
        Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"),
        Data(R.drawable.bonewormgreen, "초록지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"),
        Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"),
        Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"),
        Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"),
        Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"),
        Data(R.drawable.bonewormgreen, "초록지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"),
        Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"),
        Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"),
        Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"),
        Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"),
        Data(R.drawable.bonewormgreen, "초록지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"),
        Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"),
        Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"),
        Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"),
        Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"),
    )
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_towerlist)
 
        val recyclerView : RecyclerView = findViewById(R.id.recyclerView)
        val gridLayoutManager = GridLayoutManager(applicationContext, 2// Grid형태의 레이아웃 1줄당 2개 씩 배치
        recyclerView.layoutManager = gridLayoutManager
        recyclerView.adapter = TowerAdapter(DataList)
        recyclerView.addItemDecoration(VerticalItemDecorator(5)) // 줄간 데코 간격을 5씩 주었다.
    }
 
cs

줄간 간격을 위한 Decorator class

 

1
2
3
4
5
6
7
8
9
10
11
12
13
class VerticalItemDecorator(private val Height : Int) : RecyclerView.ItemDecoration(){
    override fun getItemOffsets(
        outRect: Rect,
        view: View,
        parent: RecyclerView,
        state: RecyclerView.State
    ) {
        super.getItemOffsets(outRect, view, parent, state)
        outRect.top = Height
        outRect.bottom = Height
    }
}
 
cs

 

클릭이벤트로 클릭했을때 넘어가는 액티비티를 추가하였다.

해당 액티비티의 레이아웃을 만들었다 액티비티 종료를 위한 버튼 만듬

 

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
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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"
    tools:context=".TowerprofileActivity">
 
    <ImageView
        android:id="@+id/imageView2"
        android:layout_width="132dp"
        android:layout_height="126dp"
        android:layout_marginStart="139dp"
        android:layout_marginTop="168dp"
        android:layout_marginEnd="140dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@drawable/bonewormblack" />
 
    <TextView
        android:id="@+id/textView2"
        android:layout_width="103dp"
        android:layout_height="38dp"
        android:layout_marginTop="48dp"
        android:background="#E3A5A5"
        android:text="지렁이이름"
        android:textSize="15dp"
        android:textStyle="italic"
        android:gravity="center"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/imageView2" />
 
    <Button
        android:id="@+id/button"
        android:layout_width="103dp"
        android:layout_height="38dp"
        android:layout_marginTop="32dp"
        android:background="#ABCBDA"
        android:text="닫기"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView2" />
</androidx.constraintlayout.widget.ConstraintLayout>
cs

 

위 레이아웃에 해당하는 액티비티 null에러가 떠서 어플이 강제종료 되길래 저렇게 입력했다

 

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
 
class TowerprofileActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_towerprofile)
 
        var datas = intent.getSerializableExtra("data") as Data?
 
        val profiledetail: ImageView = findViewById(R.id.imageView2)
        val textdetail: TextView = findViewById(R.id.textView2)
        val btn : Button = findViewById(R.id.button)
        if(datas != null) {
            profiledetail.setImageResource(datas.profile)
            textdetail.text = datas.name
        }else{
            finish()
            println("null 에러")
        }
        btn.setOnClickListener(){
            finish()
            println("프로필 종료")
        }
 
    }
}
 
cs

 

결과 화면

 

로그를 찍어서 recyclerview의 생성 과정을 살펴보았다.

onCreateViewHolder로 생성된 이후 onBindViewHolder 로 객체들과 View들을 하나씩 Bind하였다.

 

 

위 혹은 아래로 드래그시 화면에 표시되지않은 recyclerview의 view들이 onCreateViewHolder로 생성된 이후 Bind 되는것을 알수있었다. View들이 전부 생성된 이후로는 위아래로 드래그하면 onBindViewHolder 함수가 계속 작동하는것을 볼수 있었다.