상세 컨텐츠

본문 제목

[Kotlin] RecyclerView

카테고리 없음

by 클리엘 클리엘 2020. 12. 21. 10:16

본문

728x90

RecyclerView는 데이터 목록을 보여주기 위해 사용되는 것으로서 일련의 사용 과정을 간략히 알아보고자 합니다.

 

1. RecyclerView 배치하기

 

처음 RecyclerView를 사용하려면 아래와 같이 오른쪽에 아래화살표가 표시되어 있음을 볼 수 있습니다. 이는 기본적으로 포함되어 있는 컨테이너가 아니므로 화살표를 눌러 라이브러리를 내려받아 사용해야 함을 의미합니다. 화살표를 눌러 라이브러리를 내려받습니다.

 

내려받기를 하면 IDE아래쪽에 진행과정이 표시되고 곧이어 완료되었음을 알려줍니다. 동시에 내려받기 위한 화살표도 사라지게 됩니다.

 

이제 RecyclerView를 아래와 같이 화면에 배치합니다.

 

위에서 보듯이 RecyclerView에서 보여지는 항목은 하나의 Item으로 표시됩니다. 물론 Spinner처럼 단일 내용을 하나씩 뿌려줄 수도 있지만 그렇게 하려면 차라리 그냥 Spinner을 사용하는 것이 편합니다. RecyclerView를 사용하려는 이유는 여러 가지 내용을 하나의 아이템에 표시하기 위함이고 그렇게 하기 위한 준비가 Spinner보다는 더 많으므로 용도에 따라 적절한 것을 사용해야 합니다.

 

2. 새로운 layout 추가하기

 

이번에 살펴볼 예제는 하나의 item에 번호, 이름, 전화번호 이렇게 3가지의 내용을 보여줄 것입니다. 그렇게 하기 위해 3가지의 내용을 표시할 수 있는 새로운 레이아웃을 생성해야 합니다.

 

Android Studio에서 res -> layout 폴더를 선택해 마우스 오른쪽 버튼을 눌러 new -> Layout resource file 을 선택합니다.

 

위와 같은 창이 열리면 File name에 lyaout의 이름을 입력하고, Root element에 표시할 형태에 따라 적절한 layout을 지정합니다. 예제에서는 recycler_item_layout을 이름으로 하고 Root element에 LinearLayout을 지정하도록 하겠습니다.

 

새로운 Layout이 만들어 지면 여기에 3개의 TextView를 이용해 순서대로 번호, 이름, 전화번호를 표시할 수 있게 배치합니다. 우선 TextView가 가로로 나열되어야 하므로 layout의 orientation 속성을 horizontal로 변경한 뒤 TextView를 Layout에 올려두면 다음과 같이 될 것입니다.

 

각 TextView의 ID를 관리하기 편하게 textView1, textView2, textView3으로 지정하고 layout_weight를 조정해 각 textView가 차지하는 영역을 조절합니다. 마지막으로 추가된 Layout의 layout_height를 실제 item이 표시될 때의 크기로 조정합니다. 예제에서는 20dp정도로 설정했으며 이 크기가 실제 item항목의 크기라고 판단하면 됩니다.

 

3. 데이터 정의하기

 

위에서도 언급했듯이 단순한 문자열이 아니라 3가지의 내용을 한꺼번에 표시하는 형태이므로 이에 맞게끔 데이터를 구조화해야 합니다. 이를 위해 새로운 클래스를 생성하여 데이터를 만들도록 합니다.

 

java디렉터리 아래에 패키지를 마우스 오른쪽 버튼으로 눌러 new -> Kotlin File/Class를 선택합니다. 이어지는 창에서 Class를 선택하고 이름을 PhoneBook이라고 입력합니다.

 

Class를 추가하면 코드 창으로 Class의 코드가 열리게 되는데 여기에서 다음과 같이 기존의 Class를 데이터를 담을 데이터 클래스로 변경합니다.

data class PhoneBook(var no: Int, var name: String, var phone: String) {
    
}

4. 어댑터와 홀더 생성하기

 

어댑터를 생성하기 위해 조금 전 Class를 추가할 때와 동일하게 java폴더 아래에 패키지를 마우스 오른쪽 버튼으로 눌러 new -> Kotlin file/class를 선택합니다. 이어지는 창에서 Class를 선택한 뒤 Name에 RecyclerAdapter를 입력합니다.

 

 

Adapter 클래스가 추가되면 여기에 RecyclerView.ViewHolder를 상속받는 Holder 클래스를 추가합니다.

package com.example.myapplication

import android.view.View
import androidx.appcompat.view.menu.ActionMenuItemView
import androidx.recyclerview.widget.RecyclerView

class RecyclerAdapter {
}

class Holder(itemView: View): RecyclerView.ViewHolder(itemView) {
    
}

Holder의 역할은 item을 화면에 표시할 때 item수가 화면 밖을 벗어나면 표시를 중지했다가 사용자가 더 많은 항목을 보기 위해 스크롤을 내리면 다른 item을 자동적으로 표시해주는 역할을 수행합니다. 앱이 효과적으로 동작하는데 중요한 역활을 해주는 것입니다.

 

Holder를 보면 생성자로 View를 넘겨주고 있는데 여기서 View가 item으로 표시되는 Layout에 해당합니다. Holder를 작성하고 나면 이제 RecyclerAdapter를 다음과 같이 수정합니다.

class RecyclerAdapter: RecyclerView.Adapter<Holder>() {
    
}

그리고 {와 } 사이 코드 블록 안에서 Ctrl + I 키를 눌러 필요한 인터페이스를 구현합니다.

class RecyclerAdapter: RecyclerView.Adapter<Holder>() {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
        TODO("Not yet implemented")
    }

    override fun onBindViewHolder(holder: Holder, position: Int) {
        TODO("Not yet implemented")
    }

    override fun getItemCount(): Int {
        TODO("Not yet implemented")
    }
}

TODO는 모두 지워줍니다. 그리고 RecyclerAdapter 메서드 맨 위에 item을 표시할 데이터 변수를 선언하고 getItemCount에서 데이터 변수의 크기를 반환하는 코드를 작성합니다.

class RecyclerAdapter: RecyclerView.Adapter<Holder>() {
    var items = mutableListOf<PhoneBook>()
    
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
        
    }

    override fun onBindViewHolder(holder: Holder, position: Int) {
        
    }

    override fun getItemCount(): Int {
        return items.size
    }
}

이어서 onCrateViewHolder 메서드를 다음과 같이 수정합니다.

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
    return Holder(LayoutInflater.from(parent.context).inflate(R.layout.recycler_item_layout, parent, false))
}

앱이 실행되면 화면에 표시할 item 수만큼 onCreateViewHolder를 호출하게 되고 이때 onCreateViewHolder 메서드는 표시할 item을 Holder를 통해 반환하게 됩니다.

 

마지막으로 onBindViewHolder 메서드를 수정합니다. 이 메서드는 화면이 스크롤될 때 다음에 표시할 item을 Holder에 담아 반환하는 역할을 수행합니다.

override fun onBindViewHolder(holder: Holder, position: Int) {
    val pb = items.get(position)
    holder.pbData(pb)
}

위에서 작성된 Holder의 pbData는 Holder안에 따로 함수를 구현합니다.

class Holder(itemView: View): RecyclerView.ViewHolder(itemView) {
    fun pbData(phoneBook: PhoneBook) {
        itemView.textView1.text = "${phoneBook.no}"
        itemView.textView2.text = "${phoneBook.name}"
        itemView.textView3.text = "${phoneBook.phone}"
    }
}

5. 데이터 만들고 표시하기

 

이제 끝으로 MainActiviy로 돌아와 위에서 생성한 PhoneBook클래스에 데이터를 채우는 과정을 거쳐야 합니다. 이를 위해 loadPhoneBook이라는 메서드를 만들어 임의의 데이터를 생성해 넣도록 하겠습니다.

fun loadPhoneBook(): MutableList<PhoneBook> {
    var data:MutableList<PhoneBook> = mutableListOf()

    data.add(PhoneBook(1, "홍길동", "010-0000-0000"))
    data.add(PhoneBook(2, "홍길순", "010-1111-0000"))
    data.add(PhoneBook(3, "홍길남", "010-2222-0000"))
    data.add(PhoneBook(4, "홍길석", "010-3333-0000"))
    data.add(PhoneBook(5, "홍길영", "010-4444-0000"))

    return data
}

그다음 MainActivity의 onCreate안에서 위에서 생성한 데이터를 가져와 adapter에 넘겨준 뒤

val data:MutableList<PhoneBook> = loadPhoneBook()
var adapter = RecyclerAdapter()
adapter.items = data

adapter를 이전에 배치해둔 Recycler에 연결하고 화면 표시를 위한 레이아웃을 지정합니다.

myRecycler.adapter = adapter
myRecycler.layoutManager = LinearLayoutManager(this)

예제에서 LayoutManager를 사용했는데 데이터를 어떤 형태로 보여주느냐에 따라 GridLayoutManager, StaggeredGridLayoutManager를 사용할 수 있습니다.

 

LinearLayoutManager 기본적으로 데이터를 1개씩 세로로 나열합니다. 하지만 LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false) 처럼 두번째 매개변수를 통해 가로냐열을 지정할 수도 있습니다.
GridLayoutManager 다수의 데이터를 나열할 수 있습니다. 이때 GridLayoutManager(this, 2) 처럼 몇개를 나열할지 숫자로 지정합니다.
StaggeredGridLayoutManager StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.HORIZONTAL) 처럼 표시할 갯수와 방향을 지정하여 가로 혹은 세로 방향으로 다수의 데이터를 나열합니다.

 

여기까지 완료되었으면 이제 앱을 실행해 보도록 하겠습니다.

 

6. 데이터 선택 처리

 

목록을 나열한 후 사용자가 특정 목록을 선택할 수 있는 상황이라면 Holder에서 init를 구현해 itemView의 리스너를 추가하면 됩니다.

class Holder(itemView: View): RecyclerView.ViewHolder(itemView) {
    init {
        itemView.setOnClickListener {
            Toast.makeText(itemView?.context, "선택 : ${itemView.textView1}번 ${itemView.textView2}", Toast.LENGTH_LONG).show()
        }
    }

    fun pbData(phoneBook: PhoneBook) {
        itemView.textView1.text = "${phoneBook.no}"
        itemView.textView2.text = "${phoneBook.name}"
        itemView.textView3.text = "${phoneBook.phone}"
    }
}

728x90

댓글 영역