RecyclerView 사용하기

KEH·2021년 7월 22일
0
post-thumbnail

RecyclerView란?

RecyclerView란 아래와 같이 한 화면에 동일한 형식의 아이템(뷰)을 여러개 출력하고, 스크롤을 통해 아이템들을 확인할 수 있도록 구성하는 뷰이다.

아이템뷰 생성하기

첫 번째로 동일한 형식의 아이템뷰 파일을 생성한다. layout 리소스에 post_detail.xml 파일을 생성한다.

위와 같이 화면이 구성되도록 post_detail.xml 파일을 작성한다.

Adapter 클래스 생성

//Adapter : 데이터와 아이템에 관한 View를 생성.
    inner class PostAdapter: RecyclerView.Adapter<RecyclerView.ViewHolder>() {
        private lateinit var postDetailBinding: PostDetailBinding
        private var store: FirebaseFirestore = FirebaseFirestore.getInstance()
        private var contentsList = arrayListOf<ContentDTO>()

        //DB에서 게시글 데이터 가져오기
        init {
            store.collection("posts")
                .orderBy("timestamp", Query.Direction.DESCENDING)
                .addSnapshotListener { posts, e->
                    if (e!=null) {
                        println(e)
                        Toast.makeText(activity, R.string.upload_fail, Toast.LENGTH_SHORT).show()
                    } else {
                        contentsList.clear()
                        for (post in posts!!.documents) {
                            var content = post.toObject(ContentDTO::class.java)
                            contentsList.add(content!!)
                        }
                    }
                    notifyDataSetChanged()
                }
        }

        //post_detail.xml을 아이템뷰 생성
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
            var inflater: LayoutInflater = parent.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
            postDetailBinding = PostDetailBinding.inflate(inflater, parent, false)

            return PostHolder(postDetailBinding.root)
        }

        //아이템뷰 화면에 입력돼야 할 데이터 추가.
        override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
            postDetailBinding.usernameTextView.text = contentsList.get(position)!!.userEmail
            Glide.with(holder.itemView).load(contentsList.get(position).imageUrl).into(postDetailBinding.postPhotoIV)
            postDetailBinding.favoriteCountTextView.text = "좋아요 ${contentsList.get(position)!!.favoriteCount}개"
            postDetailBinding.postTextView.text = contentsList.get(position).exaplain
        }

        //총 몇 개의 아이템이 추가되어야 하는지 확인.
        override fun getItemCount(): Int {
            return contentsList.size
        }
    }

Home 화면에 적용할 아이템뷰(post_detail.xml)를 생성하는 PostAdapter 클래스를 생성한다. 해당 클래스는 RecyclerView.Adapter 클래스를 상속받고, onCreateViewHolder(), onBindViewHolder(), getItemCount() 메서드를 오버라이딩한다.

첫 번째로 화면에 적용할 게시글 데이터가 필요하기 때문에 생성자로 Firebase Firestore에서 데이터를 읽어오고 ContentDTO arrayList를 생성한다.

onCreateViewHolder() 메서드에서는 사용하고자 하는 아이템뷰를 생성한다. 여기서는 post_datail.xml 파일을 아이템뷰로 생성한다.

onBindViewHolder() 메서드는 아이템뷰에 가져온 데이터를 적용하는(바인딩) 메서드이다. contentDTO에서 사용자 이메일, 게시글 이미지, 게시글, 좋아요 수 데이터를 이용해 화면에 바인딩한다.

getItemCount() 메서드는 필요한 아이템의 수를 리턴하면 된다. 이곳에선 게시글 데이터 수만큼 아이템을 생성해야 하므로 ContentDTO arrayList의 사이즈를 리턴한다.

ViewHolder 클래스 생성

//뷰를 보관하는 Holder 객체.
//item 뷰들을 재활용하기 위해 각 요소를 저장해두고 사용.
//아이템 생성시 뷰 바인딩은 한 번만 하고, 바인딩 된 객체를 가져다 사용해 성능이 효율적.
inner class PostHolder(root: View) : RecyclerView.ViewHolder(root)

viewHolder는 adapter 클래스에서 생성한 아이템뷰를 보관하는 클래스이다. 아이템 뷰 생성과 바인딩은 딱 한 번 생성되고, 필요할 때마다 viewHolder에 보관된 아이템뷰를 가져다 사용(재활용)한다.

RecyclerView.ItemDecoration 클래스를 이용해 아이템 간 수직 간격 설정하기

//아이템 간 수직(위아래) 간격 설정
    inner class VerticalItemDecorator(var divHeight: Int):RecyclerView.ItemDecoration() {
        override fun getItemOffsets(
            outRect: Rect,
            view: View,
            parent: RecyclerView,
            state: RecyclerView.State
        ) {
            super.getItemOffsets(outRect, view, parent, state)
            outRect.top = divHeight
            outRect.bottom = divHeight
        }
    }

ItemDecoration을 사용하면 아이템 간의 간격을 설정할 수 있다. 해당 화면에서는 아이템 간의 수직 간격(위아래)을 띄우기 위해 사용했다.

HomeFragement.kt에 RecyclerView 적용.

HomeFragement.kt에 지금까지 만든 리사이클러뷰를 적용하기 위해 layout 파일로 이동하여 RecyclerView를 추가한다.

<?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"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/postsRecyclerView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
class HomeFragment: Fragment() {
    private lateinit var mBinding: FragmentHomeBinding

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

        mBinding.postsRecyclerView.adapter = PostAdapter()
        //layoutManager : 아이템의 배치를 담당.
        //LinearLayoutManager : 가로/세로
        //GirdLayoutManager : 그리드 형식
        //StaggeredGirdLayoutManager : 지그재그형의 그리드 형식
        mBinding.postsRecyclerView.layoutManager = LinearLayoutManager(activity)
        mBinding.postsRecyclerView.addItemDecoration(VerticalItemDecorator(30))

        return mBinding.root
    }

리사이클러뷰에 지금 만든 어댑터 클래스를 생성하고, layoutManager를 이용해 아이템의 배치를 어떻게 할 것인지 설정한다. 그리고 생성한 ItemDecoration 클래스를 사용해 아이템 간 간격을 설정한다.

전체 소스 코드

[출처] 개발일지-[Android/Kotlin] RecyclerView 만들기
[출처] Bbaktaeho-[Android] RecyclerView (리사이클러뷰, 뷰홀더, ViewHolder)

profile
개발을 즐기고 잘하고 싶은 안드로이드 개발자입니다 :P

0개의 댓글