[Kotlin]RecyclerView 작성하기
블로그 주인은 이제 안드로이드에 대해 공부를 시작하는 수준이다.
공부하면서 알았던 것들을 잊어버리지 않기 위해 작성한다.
RecyclerView 에 대해 적어보려고 한다.
RecyclerView는 Layout의 종류로 데이터 집합들을 각각의 개별 아이템 단위로 구성하여 화면에 출력해주는 뷰 그룹이며, 수 많은 데이터를 스크롤 가능한 리스트 형태로 표시해주는 위젯을 의미한다.
ListView와 동일한 형식이지만 RecyclerView는 ViewHolder 패턴을 이용하며 ItemLayout에서 가로,Grid형식을 지원하고
Decoration을 할때 RecyclerView.ItemDecoration 객체를 이용해서 구분선을 설정한다.
두 레이아웃의 가장 큰 차이점은 RecyclerView는 재사용성이 훨씬 좋다.
RecyclerView 주요 클래스
ViewHolder - 각각의 뷰를 보관하는 Holder 객체
LayoutManager - 아이템의 배치를 담당 (LinearLayoutManager, GridLayoutManager, StaggeredGridLayoutManager)
Adapter - ListView와 동일한 Adapter의 개념으로 View를 생성하는 기능을 담당
ItemAnimation - RecyclerView의 아이템을 꾸미는 역할을 한다.
작성하는 법
우선 Activity를 생성하고 해당 Activity의 Layout에 RecyclerView를 추가한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/linearLayoutCompat" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".TowerlistActivity" > <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" aapp:layoutManager="androidx.recyclerview.widget.GridLayoutManager" tools:listitem="@layout/tower_list" /> </LinearLayout> | cs |
배치할 아이템 뷰를 만든다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | <?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" xmlns:tools="http://schemas.android.com/tools" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/gamebtn1" android:paddingRight="10dp" android:paddingLeft="10dp"> <TextView android:id="@+id/tv_tower" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="타워이름" android:textSize="15sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.042" app:layout_constraintStart_toEndOf="@+id/iv_tower" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.555" /> <ImageView android:id="@+id/iv_tower" android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:srcCompat="@drawable/bonewormblack" /> </androidx.constraintlayout.widget.ConstraintLayout> | cs |
어댑터를 작성하기위한 클래스를 추가하고 다음과 같이 ViewHolder를 만든다.
클릭이벤트를 위해 setOnClickListener를 작성하고 item의 data를 putExtra 하였다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | class TowerViewHolder(v : View) : RecyclerView.ViewHolder(v){ private val profile: ImageView = itemView.findViewById(R.id.iv_tower) private val text: TextView = itemView.findViewById(R.id.tv_tower) fun bind(item:Data){ //바인드 함수를 집어넣어서 저장된 요소와 뷰와 바인딩을 하였다. text?.text = item.name profile?.setImageResource(item.profile) itemView.setOnClickListener { val intent = Intent(itemView.context, TowerprofileActivity::class.java) itemView.context.startActivity(intent) intent.putExtra("data", item) itemView.context.startActivity(intent) println("넘어가기") } println("바인드함수") } } | cs |
Data 클래스 , 클래스를 putExtra 하기에 Serializable 함
1 2 | class Data(val profile:Int, val name:String) : Serializable | cs |
Adapter class를 만든다
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class TowerAdapter(val DataList:ArrayList<Data>) : RecyclerView.Adapter<TowerViewHolder> (){ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TowerViewHolder { // viewType 형태의 아이템 뷰를 위한 뷰홀더 객체 생성 val cellForRow = LayoutInflater.from(parent.context).inflate(R.layout.tower_list,parent,false) println("온크리트뷰홀더") return TowerViewHolder(cellForRow) } override fun onBindViewHolder(holder: TowerViewHolder, position: Int) { //position에 해당하는 데이터를 뷰홀더의 아이템 뷰에 표시 얘는 내용을 수정하는 역할 holder.bind(DataList[position]) println("바인드뷰홀더") } override fun getItemCount(): Int { //전체 아이템 개수 리턴. return DataList.size } } | cs |
Activity에 아이템목록인 arrayList의 DataList를 만들었다. DataList를 adapter로 받아 구현함
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | class TowerlistActivity : AppCompatActivity() { val DataList = arrayListOf( Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"), Data(R.drawable.bonewormgreen, "초록지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"), Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"), Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"), Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"), Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"), Data(R.drawable.bonewormgreen, "초록지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"), Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"), Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"), Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"), Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"), Data(R.drawable.bonewormgreen, "초록지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"), Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"), Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"), Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"), Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"), Data(R.drawable.bonewormgreen, "초록지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"), Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"), Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"), Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"), Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"), Data(R.drawable.bonewormgreen, "초록지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"), Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"), Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"), Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"), Data(R.drawable.bonewormblack, "검은지렁이"),Data(R.drawable.bonewormblack, "검은지렁이"), ) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_towerlist) val recyclerView : RecyclerView = findViewById(R.id.recyclerView) val gridLayoutManager = GridLayoutManager(applicationContext, 2) // Grid형태의 레이아웃 1줄당 2개 씩 배치 recyclerView.layoutManager = gridLayoutManager recyclerView.adapter = TowerAdapter(DataList) recyclerView.addItemDecoration(VerticalItemDecorator(5)) // 줄간 데코 간격을 5씩 주었다. } | cs |
줄간 간격을 위한 Decorator class
1 2 3 4 5 6 7 8 9 10 11 12 13 | class VerticalItemDecorator(private val Height : Int) : RecyclerView.ItemDecoration(){ override fun getItemOffsets( outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State ) { super.getItemOffsets(outRect, view, parent, state) outRect.top = Height outRect.bottom = Height } } | cs |
클릭이벤트로 클릭했을때 넘어가는 액티비티를 추가하였다.
해당 액티비티의 레이아웃을 만들었다 액티비티 종료를 위한 버튼 만듬
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | <?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" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".TowerprofileActivity"> <ImageView android:id="@+id/imageView2" android:layout_width="132dp" android:layout_height="126dp" android:layout_marginStart="139dp" android:layout_marginTop="168dp" android:layout_marginEnd="140dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:srcCompat="@drawable/bonewormblack" /> <TextView android:id="@+id/textView2" android:layout_width="103dp" android:layout_height="38dp" android:layout_marginTop="48dp" android:background="#E3A5A5" android:text="지렁이이름" android:textSize="15dp" android:textStyle="italic" android:gravity="center" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.498" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/imageView2" /> <Button android:id="@+id/button" android:layout_width="103dp" android:layout_height="38dp" android:layout_marginTop="32dp" android:background="#ABCBDA" android:text="닫기" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.498" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/textView2" /> </androidx.constraintlayout.widget.ConstraintLayout> | cs |
위 레이아웃에 해당하는 액티비티 null에러가 떠서 어플이 강제종료 되길래 저렇게 입력했다
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | class TowerprofileActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_towerprofile) var datas = intent.getSerializableExtra("data") as Data? val profiledetail: ImageView = findViewById(R.id.imageView2) val textdetail: TextView = findViewById(R.id.textView2) val btn : Button = findViewById(R.id.button) if(datas != null) { profiledetail.setImageResource(datas.profile) textdetail.text = datas.name }else{ finish() println("null 에러") } btn.setOnClickListener(){ finish() println("프로필 종료") } } } | cs |
결과 화면
로그를 찍어서 recyclerview의 생성 과정을 살펴보았다.
onCreateViewHolder로 생성된 이후 onBindViewHolder 로 객체들과 View들을 하나씩 Bind하였다.
위 혹은 아래로 드래그시 화면에 표시되지않은 recyclerview의 view들이 onCreateViewHolder로 생성된 이후 Bind 되는것을 알수있었다. View들이 전부 생성된 이후로는 위아래로 드래그하면 onBindViewHolder 함수가 계속 작동하는것을 볼수 있었다.