Android RecyclerView

지훈·2022년 1월 2일
0
post-thumbnail

Android RecyclerView

리사이클러뷰는 '많은 수의 데이터 집합을 제한된 영역 내에서 유연하게 표시 가능하게 하는 위젯. (목록을 화면에 출력해주고 동적으로 표현해주는 컨테이너)

어댑터는 어떤 레이아웃을 사용하느냐에 따라 표시되는 모양을 다르게 만들 수 있다.

리사이클러뷰는 기존에 리스트뷰에 유연함과 성능을 더한 리스트뷰의 개선판 OR 확장판.

Recycler(리사이클러)

  • 재활용(Recycle)

리스트뷰의 경우 리스트 항목이 갱신될 때마다 매번 아이템 뷰를 새로 구성해야 한다. 카카오톡 대화창을 예시로 들면 이전 채팅 내용을 보기 위해서는 위로 스크롤해야 한다. 이 때 위로 스크롤하면서 굉장히 많은 View를 보게 되는데 이 뷰들이 계속해서 생성된다는 것이다. 이는 굉장한 낭비이다. 리사이클러뷰는 아이템을 표시하기 위해 생성한 뷰를 재활용한다. 이를 위해 뷰홀더(ViewHolder) 패턴을 사용한다

  • 유연함(Flexibility)

리사이클러뷰의 구현 요소 또는 구현에 따른 결과물이 쉽게 변경되거나 확장될 수 있다.
리사이클러뷰는 리스트뷰와 다르게 개발자가 쉽게 구현할 수 있도록 만들어 준다. 수직뿐만 아니라 수평 방향으로 아이템들이 나열되게 만들 수 있고, 아이템 뷰의 동적(Dynamic) 구성을 용이하게 만들어주며, 이를 런타임에 바꾸게 만들 수 있다.

리사이클러뷰 구성요소

  • 어댑터 (Adapter)
    데이터 목록을 아이템 단위의 뷰로 구성하여 화면에 표시하기 위해 사용
    리사이클러뷰에 표시될 아이템 뷰를 생성하는 역할. 사용자가 데이터 리스트로부터 아이템 뷰를 만든다.

  • 레이아웃 매니저 (Layout Manager)
    아이템뷰가 나열되는 형태를 관리하기 위한 요소
    어댑터에서 아이템 뷰를 생성하기 이전에 어떤 형태로 배치될 아이템 뷰를 만들지 결정. 안드로이드 SDK에서는 아래 레이아웃 매니저가 기본으로 제공된다.

    • LinearLayoutManager : Horizontal OR Vertical 방향 Linear로 아이템 뷰 매치
    • GridLayoutManager : 바둑판 모양 Grid 형태로 아이템 뷰 배치
    • StaggeredGridLayoutManager : 엇갈린 Grid 형태로 아이템 뷰 배치

  • 뷰홀더 (VeiwHolder)
    레이아웃 매니저가 제공하는 레이아웃 형태. 어댑터를 통해 만들어진 각 아이템뷰는 뷰홀더 객체에 저장되어 화면에 표시되고 필요에 따라 생성 또는 재활용(Recycler)된다.
    뷰홀더는 화면에 표시될 아이템 뷰를 저장하는 객체. 어댑터에 의해 관리되는데 필요에 따라 어댑터에서 생성된다.(레이아웃매니저의 아이템 뷰 재활용 기준에 따라) 물론, 미리 생성된 뷰홀더 객체가 있으면 새로 생성하지 않고 이미 만들어진 뷰홀더를 재활용한다. 이 때 데이터는 오직 뷰홀더의 아이템 뷰에 Binding된다.

리사이클러뷰 사용법

각 아이템이 이미지뷰 하나로 구성된 형태로 데이터 리스트를 표시하자.
화면의 형태는 LinearLayoutManager를 Horizontal 방식으로 좌우로 스크롤하는 형태로 만들어봅시다.

1. 리사이클러뷰가 보일 위치에 리사이클러뷰 추가

contnet_some.xml - 레이아웃 리소스 XML에 리사이클러뷰를 추가한다.

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/home_today_music_album_rv"
            android:layout_width="match_parent"
            android:layout_height="210dp"
            android:layout_marginTop="10dp"
            app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
            android:orientation="horizontal"
            app:layout_constraintTop_toBottomOf="@id/home_today_music_total_tv"/>


이 단계에서는 위처럼 보이는 게 정상이다. 안드로이드에서 기본으로 보여지는 형태를 만들어 놓은 것.

2. 리사이클러뷰에 들어갈 아이템들의 뷰 레이아웃 구성

recyclerview_item.xml - 리사이클러뷰 아이템에 표시될 아이템 뷰 레이아웃을 추가한다. 오직 이미지뷰를 감싸는 카드뷰와 텍스트뷰 등을 포함하는 아이템으로 만들어 봅시다.

<androidx.constraintlayout.widget.ConstraintLayout 
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">
    
<androidx.cardview.widget.CardView
        android:id="@+id/item_album_cover_img_cardView"
        app:cardCornerRadius="7dp"
        app:cardElevation="0dp"
        ```>
        <ImageView
            android:id="@+id/item_album_cover_img_iv"
           ``` />
    </androidx.cardview.widget.CardView>

    <ImageView
        android:id="@+id/item_album_play_img_iv"
        ``` />

    <TextView
        android:id="@+id/item_album_title_tv"
        ```/>

    <TextView
        android:id="@+id/item_album_singer_tv"
        ``` />
        
</androidx.constraintlayout.widget.ConstraintLayout>        

필요에 맞게 크기와 위치를 고려해서 만들면 된다. 이때 가장 상위에 있는 태그인 Layout(여기서는 ConstraintLayout)에서는 너비/ 높이를 기본적으로 wrap_content로 해주어야 한다. 만약 match_content로 하면 리사이클러뷰의 전체를 채우게 되버린다.

3. 아이템 뷰 객체들에 들어갈 데이터리스트 생성

Album Data Class를 만들어서 ArrayList에 담아준다.

- Album.kt

data class Album(
    var title: String? = "",
    var singer: String? = "",
    var coverImg: Int? = null
)

데이터 클래스의 데이터 형식을 받는 데이터 리스트를 생성해주고 그 데이터 레스트에 데이터를 넣어준다.
- HomeFragment.kt

class HomeFragment : Fragment() {
    lateinit var binding: FragmentHomeBinding
    // 데이터 리스트 albumDatas 생성
    //Album Data Class 에 있는 데이터들을 ArrayList 형태로 albumDatas 변수에 넣어준다.
    private var albumDatas = ArrayList<Album>()

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        binding = FragmentHomeBinding.inflate(inflater, container, false)

        // 데이터 리스트에 데이터 넣어주기
        albumDatas.apply {
            add(Album("Butter", "BTS", R.drawable.img_album_exp))
            add(Album("Butter", "BTS", R.drawable.img_album_exp))
            add(Album("Butter", "BTS", R.drawable.img_album_exp))
            add(Album("Butter", "BTS", R.drawable.img_album_exp))
            add(Album("Butter", "BTS", R.drawable.img_album_exp))
        }
        return binding.root
    }
}

여기서 apply는 스코프 함수이다. '수신 객체'와 '수신 객체 지정 람다(lambda with receiver)'를 갖는다.
인스턴스를 생성한 후 변수에 담기 전에 '초기화 과정'을 수행할 때 많이 쓰인다.
apply와 같은 스코프함수는 main 함수와 별도의 스코프에서 인스턴스의 변수와 함수를 조작하므로 코드가 깔끔해진다.

  • 만약 apply를 사용하지 않았다면
albumDatas.add(Album("Butter","BTS", R.drawable.img_album_exp))
albumDatas.add(Album("Butter","BTS", R.drawable.img_album_exp))
albumDatas.add(Album("Butter","BTS", R.drawable.img_album_exp))
albumDatas.add(Album("Butter","BTS", R.drawable.img_album_exp))
albumDatas.add(Album("Butter","BTS", R.drawable.img_album_exp))
albumDatas.add(Album("Butter","BTS", R.drawable.img_album_exp))

이런식으로 더 코드가 길어진다. 나중에 스코프함수에 대해서는 구체적으로 공부하도록 합시다.

4. 리사이클러뷰 어댑터 및 뷰홀더 구현

레이아웃을 결정했으면 Adapter 및 ViewHolder를 구현해야 한다. 이 두 클래스가 함께 작동하여 데이터 표시 방식을 정의한다.

  • ViewHolder : 리스트에 있는 개별 아이템 뷰의 Wrapper.
  • Adapter : 필요에따라 ViewHolder 객체를 만들고 이러한 뷰에 데이터를 설정하기도 한다.
  • 바인딩 : 뷰를 데이터에 연결하는 프로세스

어댑터 정의할 때는 세가지 메서드를 override 해야한다.

  • onCreateViewHolder(ViewGroup parent, int viewType) : RecyclerView는 ViewHolder를 새로 만들 때마다 이 메서드를 호출. 뷰홀더와 그에 연결된 view를 생성하지만 뷰의 내용을 채우지는 않는다. (ViewHolder가 아직 특정 데이터와 바인딩하기 전이기 때문에)
    viewType에 해당하는 ViewHolder를 생성하여 return

  • onBindViewHolder(ViewHolder holder, int position) : 리사이클러뷰는 ViewHolder를 데이터와 연결할 때 이 메서드를 호출. 적절한 데이터를 가져와서 뷰 홀더의 레이아웃을 채운다.
    어댑터가 해당 position에 해당하는 데이터를 결합

  • getItemCount() : 리사이클러뷰는 데이터 세트 크기를 가져올 때 이 메서드를 호출한다. 예를 들어 주소록 앱에서는 총 주소 갯수를 세는 것.
    전체 아이템 개수 return

- TodayRealesdRVAdapter.kt

package com.example.FloSh1mj1

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.FloSh1mj1.databinding.ItemTodayReleasedBinding

// 어댑터의 매개변수로 홈 프래그먼트에서 만들었던 데이터 리스트가 들어감. 어댑터는 RecyclerView.Adpater(<- type은 ViewHolder) 를 상속받는다.
class TodayRealesdRVAdapter(private val albumList: ArrayList<Album>) :
    RecyclerView.Adapter<TodayRealesdRVAdapter.ViewHolder>() {

        // ViewHolder를 만들어준다. 뷰홀더는 화면에 표시될 아이템 뷰를 저장하는 객체이므로 매개변수로 아이템뷰 binding
        // 그리고 상속받는 ViewHolder 생성자에는 꼭 binding.root를 전달해야 한다.
        inner class ViewHolder(val binding: ItemTodayReleasedBinding) : RecyclerView.ViewHolder(binding.root) {
            fun bind(album:Album){
                binding.itemAlbumTitleTv.text = album.title
                binding.itemAlbumSingerTv.text = album.singer
                binding.itemAlbumCoverImgIv.setImageResource(album.coverImg!!)
            }
        }
    // ViewHolder 생성하는 함수. 아이템 뷰 객체를 binding해서 뷰홀더에 던져준다..
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TodayRealesdRVAdapter.ViewHolder {
        // 아이템 뷰 객체를 만들어 주어야 한다.
        val binding : ItemTodayReleasedBinding = ItemTodayReleasedBinding.inflate(LayoutInflater.from(parent.context))
        return ViewHolder(binding)
    }
    // ViewHolder에 데이터를 binding해준다. -> 화면에 표시되는 데이터가 바뀔 때마다 실행되는 함수
    override fun onBindViewHolder(holder: TodayRealesdRVAdapter.ViewHolder, position: Int) {
        holder.bind(albumList[position])
    }
    // 데이터 리스트 크기를 리턴. 리사이클러뷰의 마지막이 어디인지를 알 수 있게.
    override fun getItemCount(): Int = albumList.size
}

5. 리사이클러뷰에 어댑터 연결 및 레이아웃 매니저 추가

더미 데이터를 넣어주었던 HomeFragment로 다시 돌아가서 리사이클러뷰에 어댑터 연결을 해주고 레이아웃 매니저를 추가해준다.

class HomeFragment : Fragment() {
    lateinit var binding: FragmentHomeBinding
    // 데이터 리스트 albumDatas 생성
    //Album Data Class 에 있는 데이터들을 ArrayList 형태로 albumDatas 변수에 넣어준다.
    private var albumDatas = ArrayList<Album>()

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        binding = FragmentHomeBinding.inflate(inflater, container, false)

        // 데이터 리스트에 데이터 넣어주기 (실제로는 서버에서 혹은 데이터베이스에서 데이터를 가져올 것임)
        albumDatas.apply {
            add(Album("Butter", "BTS", R.drawable.img_album_exp))
		...
        }
        // 더미 데이터와 Adapter 연결
        val todayRealesdRVAdapter = TodayRealesdRVAdapter(albumDatas)
        // 리사이클러뷰에 어댑터를 연결
        binding.homeTodayMusicAlbumRv.adapter = todayRealesdRVAdapter
        // 레이아웃 매니저 설정
        binding.homeTodayMusicAlbumRv.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
        
        return binding.root
    }
}

이렇게 코드를 작성하면 리사이클러뷰는 완성했다고 볼 수 있다.
그런데 여기서 레이아웃 매니저 설정 부분이 뭔가 이상할 수 있다. 1번 과정에서 XML에 리사이클러뷰를 추가할 때 layoutManager와 orientation을 만들지 않았었나?
사실 리사이클러뷰의 배열 설정은 위와 같이 코틀린 소스 코드에서도 가능하고 XML 파일에서도 가능하다. 즉, XML 파일에서의

<androidx.recyclerview.widget.RecyclerView
            ...
            app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
            android:orientation="horizontal"
            />

이 layoutManager와 orientation을 뺴더라도 정상적으로 보인다. 아래는 그 상황에서의 XML 파일에서의 화면이다.

아마 리사이클러뷰의 기본값은 LinearLayoutManager에 Vertical임을 유추할 수 있다. 실제로 개발을 할 떄 이렇게 화면이 보이게 되면 불편할 것이다. 그래서 리사이클러뷰 태그 안에도 초기에 배열하고 싶은 코드를 같이 작성하는 것이다. 추가로 간단히 코드 한줄만 추가하면 아래처럼 앱 실행시 보이는 화면을 미리 볼 수 있다.

<androidx.recyclerview.widget.RecyclerView
            ...
            tools:listitem="@layout/item_today_released"
            app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
            android:orientation="horizontal"/>

여기서 listitem에는 2번에서 만들었던 아이템 뷰가 들어간다.

RecyclerView 아이템 ClickListener 사용하기

리스트뷰(ListView)에서는 setOnItemClickListener() 사용과 유사한 방법을 통해 아이템 클릭 이벤트를 처리할 수 있다. 그렇다면 리사이클러뷰에서도 이것이 가능할까?

리사이클러뷰에서는 리스트뷰에 비해 훨씬 유연하고 다양한 형태로 아이템을 표시하게 만들어 준다. 레이아웃매니저를 통해 아이템을 배치하는 형태를 다양하게 구성할 수 있고 애니매이션 효과 등을 손쉽게 적용할 수 있다. 장점 덕분에 아이템 클릭 이벤트 처리가 조금 복잡하다.

보통 리사이클러뷰는 아이템 클릭 이벤트 리스너를 자신이 직접 다루지 않고, 아이템뷰에서 OnClickListener를 통해 처리한다.

리사이클러뷰 뷰홀더에서 아이템 클릭 이벤트 처리

어댑터를 통해 만들어진 각 아이템 뷰는 뷰홀더 객체에 저장되어 화면에 표시되고 필요에 따라 생성 또는 재활용된다.

위는 내용에 따르면 아래 방식대로 코드를 작성하면 될 것이다.

    1. 아이템 뷰에서 클릭 이벤트를 직접 처리
    1. 아이템 뷰는 뷰홀더 객체가 갖고 있으므로
    1. 아이템 클릭 이벤트는 뷰홀더에서 작성
class TodayRealesdRVAdapter(private val albumList: ArrayList<Album>) :
    RecyclerView.Adapter<TodayRealesdRVAdapter.ViewHolder>() {

        inner class ViewHolder(val binding: ItemTodayReleasedBinding) : RecyclerView.ViewHolder(binding.root) {
            fun bind(album:Album){
                ...}
        }

    // ViewHolder 생성하는 함수. 아이템 뷰 객체를 binding해서 뷰홀더에 던져준다..
    override fun onCreateViewHolder(
	    ...
    }
    // ViewHolder에 데이터를 binding해준다. -> 화면에 표시되는 데이터가 바뀔 때마다 실행되는 함수
    override fun onBindViewHolder(holder: TodayRealesdRVAdapter.ViewHolder, position: Int) {
        holder.bind(albumList[position])
        ////////////////////////////
        holder.itemView.setOnClickListener { /*작업들*/ }
        ///////////////////////////////
    }
    override fun getItemCount(): Int = albumList.size
}

그런데 위처럼 코딩을 하게 되면 원하는대로 작동하지 않은 경우가 많다. 만약 여기서 setOnClickListener를 하게 되면 어댑터 범위 안에서만 이벤트 처리가 가능하고 HomeFragment 에서는 이벤트 처리를 할 수 없다. 실제 앱 런칭 시 아이템 뷰가 클릭이 되었을 때 어댑터 내부가 아닌 외부에서 이벤트처리를 하고 싶은 경우가 대다수일 것이다. 그래서 우리는 인터페이스를 구현하여 통해 이벤트 처리를 하도록 만들 것이다.

클릭 인터페이스 정의

먼저 만든 어댑터에서 클릭 인터페이스를 정의한다. 인터페이스 안에 아직 정의되지 않은 함수 `onItemClick() 을 만든다.
- TodayRealesedRVAdapter

// 어댑터의 매개변수로 홈 프래그먼트에서 만들었던 데이터 리스트가 들어감. 어댑터는 RecyclerView.Adpater(<- type은 ViewHolder) 를 상속받는다.
class TodayRealesdRVAdapter(private val albumList: ArrayList<Album>) :
    RecyclerView.Adapter<TodayRealesdRVAdapter.ViewHolder>() {

    // ViewHolder를 만들어준다. 뷰홀더는 화면에 표시될 아이템 뷰를 저장하는 객체이므로 매개변수로 아이템뷰 binding
    // 그리고 상속받는 ViewHolder 생성자에는 꼭 binding.root를 전달해야 한다.
    inner class ViewHolder(val binding: ItemTodayReleasedBinding) :
        RecyclerView.ViewHolder(binding.root) {
        fun bind(album: Album) {
            binding.itemAlbumTitleTv.text = album.title
            binding.itemAlbumSingerTv.text = album.singer
            binding.itemAlbumCoverImgIv.setImageResource(album.coverImg!!)
        }
    }
    ////////////////////////////////////////////////////////////////////////////////
    	// 1. 클릭 인터페이스 정의
    interface TodayItemClickListener {
        fun onItemClick(album: Album)
    }
    	//  리스너 객체를 전달받는 함수와 리스터 객체를 저장할 변수
    private lateinit var mItemClickListener : TodayItemClickListener

    // 외부에서 리스너 객체를 전달 받을 함수
    fun setTodayItemClickListener(itemClickListener: TodayItemClickListener){
        mItemClickListener = itemClickListener
    }
    ////////////////////////////////////////////////////////////////////////////////
    
    // ViewHolder 생성하는 함수. 아이템 뷰 객체를 binding해서 뷰홀더에 던져준다..
    override fun onCreateViewHolder(
        parent: ViewGroup,
        viewType: Int
    ): TodayRealesdRVAdapter.ViewHolder {

        // 아이템 뷰 객체를 만들어 주어야 한다.
        val binding: ItemTodayReleasedBinding =
            ItemTodayReleasedBinding.inflate(LayoutInflater.from(parent.context))
        return ViewHolder(binding)
    }

    // ViewHolder에 데이터를 binding해준다. -> 화면에 표시되는 데이터가 바뀔 때마다 실행되는 함수
    override fun onBindViewHolder(holder: TodayRealesdRVAdapter.ViewHolder, position: Int) {
        holder.bind(albumList[position])
        ////////////////////////////////////////////////////////////////////////////
        // 2. 여기서 리스터 객체를 사용하여 setOnclickListener 함수 호출. 이렇게 다른 Fragment에서 이벤트 처리를 할 수 있다. 
        //여기에 position 값이 있기 때문에 몇번째 항목이 클릭되었는지를 알기 편하다. 여기서 이벤트 처리 코드를 만들자.
        holder.itemView.setOnClickListener { mItemClickListener.onItemClick(albumList[position]) }
    }
    	/////////////////////////////////////////////////////////////////////////////
    // 데이터 리스트 크기를 리턴. 리사이클러뷰의 마지막이 어디인지를 알 수 있게.
    override fun getItemCount(): Int = albumList.size
}

어댑터에서 이렇게 코드를 작성하면 어댑터에서 할 작업은 끝이 났다.

어댑터에서 할 일을 정리하면

    1. 리스너 인터페이스 정리
    1. 리스너 객체를 전달한 메서드 setTodayItemClickListener 선언, 전달할 객체를 저장할 변수 mItemClickListener 선언
    1. 어댑터를 만들 때 override 한 메서드 onBindViewHolder에서 아이템 클릭시 수행할 동작 작성. (이 때 1에서 만든 인터페이스의 추상함수 onItemClick을 사용한다.
      onItemClick 함수는 외부에서 정의해주어서 다른 액티비티 혹은 프래그먼트에서의 이벤트 처리를 override해준다.

클릭 인터페이스를 객체로 받기

리사이클러뷰가 보일 액티비티 혹은 프래그먼트에서 클릭인터페이스를 받아 이벤트를 처리하는 코드를 작성해야 한다. 이를 위해서는 아래처럼 코드를 작성한다.

- HomeFragment

class HomeFragment : Fragment() {
    lateinit var binding: FragmentHomeBinding

    // 데이터 리스트 albumDatas 생성
    //Album Data Class 에 있는 데이터들을 ArrayList 형태로 albumDatas 변수에 넣어준다.
    private var albumDatas = ArrayList<Album>()

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
    ): View {
        binding = FragmentHomeBinding.inflate(inflater, container, false)

        // 데이터 리스트에 데이터 넣어주기 (실제로는 서버에서 혹은 데이터베이스에서 데이터를 가져올 것임)
        albumDatas.apply {
            add(Album("Butter", "BTS", R.drawable.img_album_exp))
            ...
        }
        
        // 더미 데이터와 Adapter 연결
        val todayRealesdRVAdapter = TodayRealesdRVAdapter(albumDatas)
        // 리사이클러뷰에 어댑터를 연결
        binding.homeTodayMusicAlbumRv.adapter = todayRealesdRVAdapter
        //////////////////////////////////////////////////////////////////////////////////////////////////
        // 여기서 Adapter에서 정의한 ClickListener 객체를 전달받으면 된다
        // 인자로 어댑터에서 만든 인터페이스를 객체로서 받는다. 이때 인터페이스 안에서 구현했던 onItemClick() 함수를 override 해야 해.
        todayRealesdRVAdapter.setTodayItemClickListener(object : TodayRealesdRVAdapter.TodayItemClickListener {
            override fun onItemClick(album: Album) {
                (context as MainActivity).supportFragmentManager.beginTransaction()
                    .replace(R.id.main_frm, AlbumFragment().apply {
                        arguments = Bundle().apply {
//                            val gson = Gson()
//                            val albumJson = gson.toJson(album)
                            putString("title", album.title)
                            putString("singer", album.singer)
                            putInt("coverImg", album.coverImg!!)
                        }
                    })
                    .commitAllowingStateLoss()
            }
        })
	//////////////////////////////////////////////////////////////////////////////////////////////////
        // 레이아웃 매니저 설정
        binding.homeTodayMusicAlbumRv.layoutManager =
            LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
        return binding.root
    }
}

위 코드를 보면 데이터와 어댑터, 리사이클러뷰와 어댑터를 연결하고 나서 어댑터.kt에서 정의한 리스터 객체를 인자로 전달받는다. 이 때 어댑터에서 정의한 인터페이스 안의 onItemClick 함수를 override 한다.

코드를 살펴보면 HomeFragment에서 리사이클러뷰의 아이템을 클릭하면 그에 대한 이벤트 처리로 MainActivity의 main_frm을 AlbumFragment로 대체하고 (AlbumFragment가 실행되고) arguments로 album의 대한 정보를 보내주는 것을 확인할 수 있다.

이벤트 처리 마무리(이 부분은 리사이클러뷰 이벤트처리와는 관련이 없는 부분)

위 코드까지는 HomeFrag에서 리사이클러뷰의 아이템을 클릭했을 때 AlbumFrag가 실행되면서 album의 데이터들을 보내주는 것까지로 리사이클러뷰 이벤트 처리하는 과정이 끝났다. 아래 과정은 그저 AlbumFrag가 HomeFrag로부터 데이터를 받는 과정을 작성한 것이다.

- AlbumFragment

class AlbumFragment: Fragment() {

    lateinit var binding: FragmentAlbumBinding

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
    ): View? {
        binding = FragmentAlbumBinding.inflate(inflater, container, false)

        // HomeFragment에서 넘어온 데이터 받기
        val albumTitle = arguments?.getString("title")
        val albumSinger = arguments?.getString("singer" )
        val albumImg = arguments?.getInt("coverImg")

        // Home에서 넘어온 데이터 반영
        binding.albumAlbumIv.setImageResource(albumImg!!)
        binding.albumMusicTitleTv.text = albumTitle.toString()
        binding.albumSingerNameTv.text = albumSinger.toString()

        binding.albumBackIv.setOnClickListener {
            (context as MainActivity).supportFragmentManager.beginTransaction()
            .replace(R.id.main_frm, HomeFragment()).commitAllowingStateLoss()
        }


        return binding.root
    }
}

간단히 arguments와 Bundle을 이용하여 데이터를 넘겨주었다. Fragment끼리 데이터를 주고 받는 부분에 있어 Gson과 Json을 이용하면 더 쉽고 깔끔한 코드로 데이터를 전달해줄 수 있다. Gson과 Json에 대해서는 나중에 따로 정리하겠습니다.

아래 전체 과정을 한눈에 보기쉽게 정리했다.

이번 리사이클러뷰 학습을 통해 Adapter와 리스너, 그리고 인터페이스 등 많은 부분을 학습하였다. 조금 더 구체적인 부분이나 이론적인 부분 등은 추후에 지속적으로 추가하겠습니다.

출처 :
ddolcat.tistory.com/590
dev-imaec.tistory.com/27
bbaktaeho-95.tistory.com/73
neosla.tistory.com/46
recipes4dev.tistory.com/154
java-boy.tistory.com/37
flow9.net/bbs/board.php?bo_table=android&wr_id=27

profile
안드로이드 개발 공부

0개의 댓글