RecyclerView??
RecyclerView는 안드로이드 앱에서 리스트 형태의 데이터를 표시하는데 사용되는 위젯
여러 아이템을 스크롤 가능한 리스트로 표현하며, 많은 아이템을 효율적으로 관리하고 보여주는 역할
처음 UMC에서 FLO앱 클론 코딩할때 이것때문에 진짜 힘들었...
-ListView
스크롤 할 때마다 위에 있던 아이템은 삭제되고, 맨 아래 아이템은 생성 되길 반복
아이템 개수 만큼 삭제와 생성을 반복하여 성능에 좋지 않다.
-RecyclerView
스크롤 할 때마다 위에 있던 아이템이 재활용 되어 아래로 이동하여 재사용
예를 들어 10개 정도 View를 만들고 10개를 재활용하여 사용한다.
View를 계속 만드는 ListView의 단점을 보완할 수 있다.
Adapter
데이터를 목록 형태로 보여주기 위해 사용
데이터를 아이템 뷰와 연결하는 역할
데이터와 RecyclerView 사이의 통신을 위한 연결체
리사이클러뷰에 표시될 아이템 뷰를 생성
ViewHolder
뷰홀더는 화면에 표시될 아이템 뷰를 저장하는 객체
어댑터를 통해 만들어진 각 아이템뷰는 뷰홀더 객체에 저장되어 화면에 표시되고 필요에 따라 생성 또는 재활용
스크롤 해서 위로 올라간 View를 재활용하기 위해 View를 기억하는 역할
implementation("androidx.recyclerview:recyclerview:1.1.0")
뷰바인딩 : 뷰에 바로 접근 가능
viewBinding {
enable = true
}
리사이클러뷰가 들어갈 자리에 추가해주기
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/profileStorage_rv"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@id/profileStorage_search"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
</androidx.recyclerview.widget.RecyclerView>
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="220dp"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/profile1_iv"
android:layout_width="150dp"
android:layout_height="230dp"
android:src="@drawable/profilestorage_profile"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_marginLeft="40dp"
android:adjustViewBounds="true"/>
<TextView
android:id="@+id/profileName_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="프로필1"
android:fontFamily="@font/gmarketsansmedium"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="220dp"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_marginLeft="90dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
아이템마다 가지는 데이터가 다르기 때문에 이 데이터들에 틀을 만들기위해?? 데이터 클래스 생성
data class ProfileData(
//이미지는 int 형식!!
val profile_img : Int,
val profile_name : String
)
+ViewHolder 생성
//items 변수 선언 : ProfileData리스트
//ProfileData 데이터클래스의 리스트를 받아온 후 각 아이템을 RecyclerView에 표시
//동적으로 할당되는 배열을 활용하기 위해서 MutableList를 사용
class ProfileRVAdapter(val items : MutableList<ProfileData>) :RecyclerView.Adapter<ProfileRVAdapter.ViewHolder>() {
//onCreateViewHolder: ViewHolder를 생성하여 반환
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProfileRVAdapter.ViewHolder {
// 레이아웃 인플레이터를 사용하여 아이템XML 레이아웃을 가져옴
val v = LayoutInflater.from(parent.context).inflate(R.layout.item_profilestorage, parent, false)
//아이템XML 레이아웃을 담은 ViewHolder를 생성하고 반환
return ViewHolder(v)
}
// onBindViewHolder: ViewHolder에 데이터(items=데이터클래스)를 바인딩
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
// ViewHolder에 현재 위치(position)의 아이템을 바인딩
holder.bindItems(items[position])
}
// getItemCount: 데이터 세트의 아이템 수를 반환
override fun getItemCount(): Int {
return items.count()
}
//ViewHolder: RecyclerView에서 각 아이템의 뷰를 보유하는 이너클래스
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
// bindItems: ViewHolder에 데이터를 바인딩하는 함수
fun bindItems(items : ProfileData){
//// XML에서 정의한 뷰들을 아이디를 이용해 가져오기
val profileImage = itemView.findViewById<ImageView>(R.id.profile1_iv)
val profileName = itemView.findViewById<TextView>(R.id.profileName_tv)
//아이템의 데이터로 뷰들을 설정
//setImageResource : 이미지 넣어주기 위해
profileImage.setImageResource(items.profile_img)
profileName.text = items.profile_name
}
}
}
LayoutManager 설정
//프래그먼트 속성 상속 받기
class ProfileStorageFragment : Fragment() {
//View Binding을 위한 변수 선언
lateinit var binding : FragmentProfilestorageBinding
// onCreateView: Fragment의 뷰를 생성하고 반환, 가장 먼저 실행되는 메서드
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
// View Binding을 사용하여 profilestorage프래그먼트XML 레이아웃 파일과 연결
binding=FragmentProfilestorageBinding.inflate(inflater,container,false)
//밑에서 정의한 함수 실행
initRecycler()
//Fragment의 루트 뷰 반환 : 해당 레이아웃에 있는 id만 사용할 수 있게 함
//Root View : 레이아웃에서 가장 바깥쪽의 View Container = 호출 시 XML에 있는 ConstraintLayout의 Root View를 반환
return binding.root
}
private fun initRecycler(){
// RecyclerView에 표시할 데이터 리스트 생성
val itemList = mutableListOf<ProfileData>()
itemList.add(ProfileData(R.drawable.profilestorage_profile, "프로필1"))
itemList.add(ProfileData(R.drawable.profilestorage_profile, "프로필2"))
itemList.add(ProfileData(R.drawable.profilestorage_profile, "프로필3"))
itemList.add(ProfileData(R.drawable.profilestorage_profile, "프로필4"))
itemList.add(ProfileData(R.drawable.profilestorage_profile, "프로필5"))
itemList.add(ProfileData(R.drawable.profilestorage_profile, "프로필6"))
itemList.add(ProfileData(R.drawable.profilestorage_profile, "프로필7"))
itemList.add(ProfileData(R.drawable.profilestorage_profile, "프로필8"))
itemList.add(ProfileData(R.drawable.profilestorage_profile, "프로필9"))
itemList.add(ProfileData(R.drawable.profilestorage_profile, "프로필10"))
itemList.add(ProfileData(R.drawable.profilestorage_profile, "프로필11"))
itemList.add(ProfileData(R.drawable.profilestorage_profile, "프로필12"))
// RecyclerView 어댑터 생성 및 데이터 리스트 전달
val rvadapter = ProfileRVAdapter(itemList)
// RecyclerView에 어댑터 설정(xml에서 설정한 리사이클러뷰 뷰바인딩)
binding.profileStorageRv.adapter = rvadapter
//RecyclerView 레이아웃 매니저 설정
//여기서는 GridLayout, 2열로 설정
//프래그먼트이기 때문에 this가 아닌 requireContext()
binding.profileStorageRv.layoutManager = GridLayoutManager(requireContext(), 2)
}
}
!! RecyclerView를 만들때 이상했던게 아이템이 한페이지에 하나씩 보인다는거였는데.. 이걸 계속 해결하지 못하고 있다가 원인을 알게댐!!
바로 item.xml에서 가장 최상위 레이아웃 높이가 match_parent 여서 그랬다는거,,,,,
꼭 기억하기!!