GDSC HUFS 3기/Android with Kotlin Team 3

[3팀] 10. 화면 구성하기_리사이클러뷰

알 수 없는 사용자 2021. 11. 6. 22:20

이 글은 이것이 안드로이드다 with 코틀린(개정판)를 참고하여 작성하였습니다.

작성자 : 심덕수

개발환경은 Windows, Android Studio입니다. 

 

 

1. 리사이클러뷰

 - 많은 수의 데이터 집합을 제한된 영역 내에서 유연하게 표시할 수 있도록 만들어주는 위젯이다.

- 한 화면에 표시되기 힘든 많은 수의 데이터를 스크롤 가능한 리스트로 표시해준다.

 

2. 리사이클러뷰 생성하기 

(1) 레이아웃 폴더에 레이아웃 리소스 파일 생성하기

** 파란 하이라이트 부분을 변경하여 레이아웃 유형을 변경할 수 있다. linear로 변경

 

(2) 레이아웃 리소스 파일의 레이아웃을 horizontal로 설정한 뒤 text view 위젯 3개 추가한다.

** 각 위젯의 layout_weight 1:5:3 비율로 설정한다.

이때, layout_width값이 0dp여야만 설정값만큼의 비율로 나누어진다.

 

(3) data 클래스 생성 : 사용할 데이터 정의

** data class Memo (var no:Int, var title:String, var timestamp:Long)

-> timestamp 1970 1 1 00 00 00초부터 헤아려진 날짜를 숫자로 표현한 것이다.

** val memoList = mutableListOf<memo>()

for(idx in 1..100){

val memo = Memo(idx, "첼로", 123456878)

memoList.add(memo)

}

-> memo 클래스형의 데이터를 리사이클러뷰에 담기 위한 데이터를 생성하기 위한 반복문이다.

memo 클래스 형의 반복문을 통해 생성된 데이터를 담을 리스트를 생성한다.

 

(4) main activity에서 data를 생성하는 로드 데이터 라는 메시지를 생성한다.

 

** fun loadData() : MutableList<Memo>{

val memoList = MutableListOf<Memo>()

for(no in 1..100){

val title = "이것이 안드로이드다 $no"

val date = System.currentTimeMillis()

val memo = Memo(no, title, date)

memoList.add(memo)

}

return memoList

}

-> loadData() 함수를 호출할 경우, memoList 리스트에 인덱스가 1~100까지의 메모형 가상 데이터들이 생성되어 넘겨진다.

 

(5) main activity에서 어댑터를 생성한다.(어댑터 틀 생성하기)

** 

class CustomAdapter : RecyclerView.Adapter<CustomAdapter.Holder>(){

class Holder(binding: ItemRecyclerBinding): RecycleView.ViewHolder(binding.root){ }

}

-> RecyclerView 화면 전체를 관리한다.

-> holder : 화면 내에 이치한 아이템 하나하나를 관리한다. 홀더는 클래스로 미리 만들어놓아야 한다. 이후 RecyclerView에 집어넣는다.

-> 위의 어댑터를 연결하기 위해서는 gradle 파일에 미리 뷰바인딩을 연결해놓아야 한다.

 

-> class CustomAdapter : RecyclerView.Adapter<CustomAdapter.Holder>()

제네릭은 위에서 해당 홀더를 통해 개별 아이템을 관리하여야 하므로 홀더를 설정해놓는다.

CustomAdapter  RecyclerView.Adapter를 상속받는다.

 

-> class Holder(binding: ItemRecyclerBinding): RecycleView.ViewHolder(binding.root)

Holder  RecycleView.ViewHolder를 상속받는다.

Holder의 파라미터를 통해 미리 생성해놓은 ItemRecyclerBinding 레이아웃을 연결하고

viewHolder의 파라미터로는 binding.root를 받는다.

 

(6) main activity에서 어댑터를 생성한다.(어댑터 세부 메서드 구현하기)

** ctrl + i를 통해 인터페이스 내부의 메서드를 구현한다.

class CustomAdapter : RecyclerView.Adapter<CustomAdapter.Holder>(){

class Holder(binding: ItemRecyclerBinding): RecycleView.ViewHolder(binding.root){

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {

val binding = ItemRecyclerBinding.inflate(LayoutInflater.from(parent.context), parent, attatchParent: false)

return Holder(binding)

}

override fun getItemCount(): Int {

return listData.size

}

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

                 val memo = listData.get(position)

                 holder.setMemo(memo)

             }

      class Holder(val binding:ItemRecyclerBinding):recyclerView.ViewHolder(binding.root){

                  fun setMemo(memo:Memo){

                      binding.textNo.text = "${memo.no}"

                      binding.textTitle.text = memo.title

 

                      val sdf = SimpleDateFormet(pattern: "yyyy-MM-dd")

                      val formattedDate = sdf.format(memo.timestamp)

                    textDate.text = formattedDate

                  }

              }

 

          }

 

      }

-> holder 메서드의 경우 밖으로 빼낼 수도 있지만, 주로 내부에서 사용하기 때문에 내장 함수로 사용하는 것이 좋다.

-> var listData = MutableListOf<Memo> : 어댑터가 사용할 데이터 목록을 생성한다.

코드의 양을 줄이기 위하여 CustomAdapter(var listData:MutableListOf<Memo>)로 사용가능하다.

loadData() 의 데이터 타입과 동일하다.

-> override fun getItemCount(): Int : 목록의 개수를 알려준다. 목록의 사이즈(높이)가 여기서 결정된다.

-> override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder :

화면에 위치시킬 아이템을 하나하나 생성한다. 

 화면에 보이는 만큼만 생성해 놓고 재사용하는 것이 리사리클러뷰의 특징이다.

-> override fun onBindViewHolder(holder: Holder, position: Int) :

이때 재사용 리사이클러뷰의 특징이 사용된다.

oncreateViewHolder에서 생성된 아이템 이후에 새롭게 호출되어야 하는 아이템들을 재사용하여 호출한다.

** 홀더의 패턴 : 사용할 데이터를 꺼낸다(val memo = listData.get(position)) : position의 값을 통해 사용자가 선택한 데이터를 담는다.

-> 홀더에 데이터를 전달한다(holder.setMemo(memo)) : memo의 값을 홀더에 전달한다.

-> 받은 데이터를 화면에 출력한다.(홀더 클래스에서 일어난다.) : binding을 통해 화면의 위젯에 직접접근하여 각각의 데이터를 넣어준다.

이때, with scope 함수를 통해 binding을 축약하여 간단한 코드작성이 가능하다.

** SimpleDateFormet(pattern: "yyyy-MM-dd") : 이때 date은 형식을 지정해주는 메서드를 사용한다.

 

 (7) mainActivity에서 가상의 데이터를 생성하고 어댑터를 통해 레이아웃에 연결한다.

- 데이터를 생성한다.

- 앞서 생성해놓은 어댑터를 생성하는 메서드를 통해 어댑터를 생성하고 recyclerView 내부의 adapter 메서드를 활용하여 화면과 연결한다.

- 레이아웃 매니저를 설정하는 과정에서 리스트 형태로 화면을 구성할 수도, 그리드 형태로 구성할 수도 있다.

 

 class MainActivity : AppCompatActivity() {

 

    val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }

 

    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)

        setContentView(binding.root)

 

        1. 데이터 불러오기

        val data = loadData()

 

        2. 커스텀어댑터 생성하기

        val customAdapter = CustomAdapter(data)

 

        3. 화면의 RecyclerView와 연결하기

        binding.recyclerView.adapter = customAdapter

 

        4. 레이아웃 매니저

        binding.recyclerView.layoutManager = LinearLayoutManager(this)

    }

 

 

 

(8) 클릭처리 설정하기

- 각 데이터 한 칸이 클릭되었을 때의 처리동작을 설정한다.

- 클릭처리는 init 에서만 해야 한다. 그렇지 않을 경우, 심각한 자원낭비를 유발한다.

 

class Holder(val binding:ItemRecyclerBinding):recyclerView.ViewHolder(binding.root){

 

      var currentMemo:Memo? = null

      init {

binding.root.setOnClickListener { int: View!

Toast.makeText(binding.root.context, "클릭된 아이템: ${currentMemo.title}", Toast.LENGHT_SHORT).show()

}

fun setMemo(memo:Memo){

binding.textNo.text = "${memo.no}"

binding.textTitle.text = memo.title

val sdf = SimpleDateFormet(pattern: "yyyy-MM-dd")

val formattedDate = sdf.format(memo.timestamp)

textDate.text = formattedDate

}

}

 

-> var currentMemo:Memo? = null : 전역변수를 통해 클릭된 아이템을 저장함으로써 화면에 출력한다.

이렇게 구성할 경우, 화면에 출력하지 않은 데이터를 활용할 수도 있다.

만약 계속해서 데이터를 클릭하는 경우라면, lateinit var currentMemo: Memo 로 작성할 수도 있다.