자주자주 사용하는 것들은 정말 나 또한 자주자주 적고 있다.
그 예를 든 것이 뷰 페이저와 리사이클러뷰인데,
최근에 코틀린 버전으로 뷰 페이저를 썼고,
저 두 가지는 이미 자바로 배운 것을 정리했다.
지금은 처음으로 코틀린으로 리사이클러뷰 사용법을 적어보려고 한다.
언어가 바뀌어도 어느 정도 이해를 해야 하는데,
내가 자바를 사용할 때도, 그리 리사이클러뷰에 대해서 잘 이해하지 못하고 넘어간 것 같다.
그래도 지금은 조금 진전이 있는 상태에서 리사이클러뷰(코틀린 버전)를 정리해보려고 한다.
(홍드 로이드 님의 영상을 보고 참고했습니다.)
우선 라이브러리를 추가해주세요.
dependencies {
implementation "androidx.recyclerview:recyclerview:1.1.0"
// For control over item selection of both touch and mouse driven selection
implementation "androidx.recyclerview:recyclerview-selection:1.1.0"
}
우선 뷰 페이저도 마찬가지이겠지만,
리사이클러뷰를 사용하려면 세 가지가 필요하다.
뷰에 어떤 인자가 들어갈지 틀을 만들어주는 클래스 하나와
내가 리사이클(재활용)할 뷰들의 아이디를 참조해주는 뷰 홀더 클래스
그리고, 이 뷰 홀더를 position에 맞게 생성해주고,
또다시 그 포지션에 맞게 리사이클해주고,
아이템을 몇 개 생성할 건지, 클릭 시 액션을 설정해줄 수 있는 어뎁터
이 세가지만 알면 리사이클러뷰를 다 알았다고 해도 무방하다.
하지만 이렇게 말로 해서는 무슨 소리인지 모르는 것이 사실이다.
우선 내가 사용할 뷰 아이템을 만들어보자.!
내가 생성할 뷰의 모습을 만드는 것이라고 생각하면 된다.
layout을 누르고 new키를 눌러 list_item.xml을 생성한다.
<?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="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/imageView"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:srcCompat="@tools:sample/avatars" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_marginLeft="15dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="269dp"
android:layout_marginRight="269dp"
android:text="백수열"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/imageView"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/jobView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="136dp"
android:layout_marginRight="136dp"
android:text="안드로이드 개발자"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.555" />
<TextView
android:id="@+id/ageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_marginLeft="15dp"
android:layout_marginTop="44dp"
android:layout_marginEnd="269dp"
android:layout_marginRight="269dp"
android:text="26살"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@+id/imageView"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
대충 이런 모습의 뷰가 만들어질 것이다.
마무리에서는 이 뷰가 여러 개가 되면, 재사용(리사이클)을 할 것이다.
그다음에는 activity_main에서 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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/re"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
이런 식으로 리사이클러뷰를 만들어주자.
이후에 이 곳에다가 내가 만든 아이템을 코드를 통해 보내줄 것이다.
우선 여기까지 하고, 다음으로 넘어가 보자.
우선은 저 뷰 아이템에 들어갈 데이터 틀을 만들기 위해서
클래스를 하나 만들 것이다.
클래스로 만드는 이유는
이따가 어뎁터에 데이터를 넘길 때, 리스트 형태로 넘기게 되는데,
리사이클을 하는 바인드 홀더 부분에서 손쉽게 데이트를 집어넣을 수 있기
용이하기 때문이다.
그러니 클래스로 한번 만들어 보자.
java에 오른쪽 클릭 후 new에서 코틀린 파일을 클릭 후
Profiles.kt를 만들어주자.
class Profiles (val profile : Int, val age : Int, val name : String, val job : String)
그리고 다음 같이 클래스(틀)를 만들어 주자.
나 같은 경우는 뷰에 들어갈 사진(Int)과 나이와 이름 직업을 자료형에 맞게 넣어주었다.
그리고 어뎁터를 만들어보자.
class ProfileAdapter(val profileList : ArrayList<Profiles>) : RecyclerView.Adapter<ProfileAdapter.CustomViewHolder>() {
//뷰홀더가 처음 생성될때
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProfileAdapter.CustomViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false)
return CustomViewHolder(view).apply { //클릭시 액션
itemView.setOnClickListener { //여기서 itemview는 뷰홀더의 아이템들을 의미한다.
val curPos : Int = adapterPosition //누른 뷰의 순서값
val profile : Profiles = profileList.get(curPos) //객체형태로 번호에 맞게 가져오기
Toast.makeText(parent.context, "이름 : ${profile.name} 나이 : ${profile.age} 직업 : ${profile.job}", Toast.LENGTH_LONG).show()
}
}
//뷰홀더에 뷰를 넘겨주고 이 것을 반환한다.
}
//재활용해주는 곳 및 값을 넣어주는 곳
override fun onBindViewHolder(holder: ProfileAdapter.CustomViewHolder, position: Int) {
holder.profile.setImageResource(profileList.get(position).profile)
holder.name.text = profileList.get(position).name
holder.age.text = profileList.get(position).age.toString() // 인트형이기 때문에
holder.job.text = profileList.get(position).job
}
//리스트의 갯수를 적어준다
override fun getItemCount(): Int {
return profileList.size
}
//뷰홀더 클래스(음료수처럼 잡아주는 홀더)
//이곳에서 파인드뷰아이디로 리스트 아이템에 있는 뷰들을 참조한다.
inner class CustomViewHolder(itemView : View) : RecyclerView.ViewHolder(itemView) {
val profile = itemView.findViewById<ImageView>(R.id.imageView) //사진
val name = itemView.findViewById<TextView>(R.id.textView) //이름
val age = itemView.findViewById<TextView>(R.id.ageView) //나이
val job = itemView.findViewById<TextView>(R.id.jobView) //직업
}
}
위에서부터 보면 ProfileAdapter가 Profiles(틀) 형태로
ArrayList로 값을 받으려고 하고 RecyclerView.Adapter를 상속받고,
이제 만들 CustomViewHolder를 제너릭에 넣어준다.
그 후 먼저 맨 아래에 CustomViewHolder를 만들어 준다.
여기서 inner class란 내부의 클래스라는 뜻으로 붙여준다.
그냥 class를 써도 되지만, 그냥 class를 쓰면 static처리가 되어,
쓸데없이 메모리를 잡아먹게 된다(메모리 릭으로 이어질 수 있다)
뷰를 인자로 담은 리사이클 러뷰를 상속으로 받는다.
이렇게 되면 이제 list_item에 있는 뷰들의 아이디 값을 참조할 수 있게 된다.
싹 다 참조해준다.
이 메서드에서는 뷰 홀더가 맨 처음 생성될 때 호출되는 클래스로
이제 아이디 값을 참조한 CustomViewHolder에
인플레이션 한 뷰(아까 만든 list_item)를 인자로 넣어
새로운 뷰를 리턴 받습니다.
(리 사이클러 뷰 아이템 생성)
이렇게 뷰 값이 어뎁터로 전달될 때마다 새로운 뷰가 만들어지는 곳이라고 할 수 있습니다.
여기까지만 아셔도 되지만,
이 return 받는 뷰에서 apply를 붙이고
itemview를 참조하는 동시 클릭 리스너를 붙이고,
각 아이템을 클릭할 때마다 액션을 지정하실 수 있습니다.
추가로 apply란
함수를 호출하는 객체를 이어지는 블록의 리시버로 전달하고, 객체 자체를 반환해줍니다.
여기서 리시버란, 바로 이어지는 블록 내에서 메서드 및 속성에 바로 접근할 수 있도록 할 객체를 의미합니다.
예를 들어 특정 객체를 생성하면서 동시에 호출해야 하는 초기화 코드가 있는 경우에 사용할 수 있습니다.
바로 지금과 같은 상황이죠,
새롭게 onCreateViewHolder가 만들어지는 초기화 상황에서 말이죠.
getItemCount는 간단히 말해 뷰를 몇 개 호출할지 지정해주는 메서드입니다.
이곳에서는 뷰에 값을 넣어주는 곳입니다.
인자에 있는 position은 각 뷰 홀더에 위치에 맞는 값을 전달해주기 위해
존재합니다.
우선 홀더에 CustomViewHolder를 참조하여 뷰 홀더에 있는 (아까 참조한 아이디)
값들에 다가 각각 값을 넣어주는데,
Adpater자체에 인자로 넘어온 profileList에서 값들을 꺼내서 입력합니다.
그러면 이 profileList를 만들어서 이 어뎁터에 보내 봅시다.
다시 MainActivity로 넘어갑니다.
class MainActivity : AppCompatActivity() {
private var mBinding : ActivityMainBinding? = null
private val binding get() = mBinding!!
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val profileList = arrayListOf(
Profiles(R.drawable.ic_launcher_background, 26, "수열백", "안드개발자"),
Profiles(R.drawable.ic_launcher_background, 245, "dsad", "안드개발자"),
Profiles(R.drawable.ic_launcher_background, 25, "sadqwf", "안드개발자"),
Profiles(R.drawable.ic_launcher_background, 254, "vadsv", "안드개발자"),
Profiles(R.drawable.ic_launcher_background, 452, "vdsav", "안드개발자"),
Profiles(R.drawable.ic_launcher_background, 25, "sdvaw", "안드개발자"),
)
binding.re.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
//레이아웃매니저를 이용해 어뎁터의 방향을 결정
binding.re.setHasFixedSize(true)//어뎁터에 성능을 위한것
binding.re.adapter = ProfileAdapter(profileList) //어뎁터에 리스트 자료를 넣는다.
}
}
우선 arrayListOf형태로 profileList라는 변수에 담습니다.
그리고 그 안에다가 아까 저희가 만든 Profiles(틀)을 활용합니다.
이런 식으로 형식에 맞는 값을 넣고,
저 맨 아래에
ProfileAdpater(아까 위에서 만든 어뎁터입니다.)에다가 인자로 보내줍니다.
만약 서버를 이용하신다면 저 arrayListOf의 값을 잘 이용하시면 될 겁니다.
이렇게 되면 어뎁터는 잘 마무리가 될 것입니다!!
아 그리고 추가로 저 리니어 레이아웃 매니저는 리사이클러뷰의 방향을 설정해주는 녀석으로
현재는 버티컬로 되어있지만, 원하시면 호리즈날로 변경 가능하십니다.
setHasFixedSize는 보통 기능적인 면을 위해 리사이클러뷰에 넣는 속성 중 하나입니다.
더 자세히 설명드리자면
기본적으로 리사이클 러뷰의 아이템의 항목을 추가하거나 이동하거나 제거가 될 때,
리사이클러뷰의 크게 변경될 수도 있습니다.
그래서 항목을 자주 추가하거나 제거하면 문제가 되는데,
setHasFixedSize를 true로 줌으로 써 어뎁터의 내용을 변경할 때
높이와 너비가 변경되지 않게 할 수 있는 기능입니다.
여기까지 리사이클 러뷰의 코틀린 버전 설명이었습니다.
저도 아직 초보라 모르는 게 많아 혹시 틀린 점이 있다면 피드백 부탁드립니다.
감사합니다.
Android Kotlin 비동기 프로그래밍이란? 개념 이해 TIL # 33 (0) | 2021.05.17 |
---|---|
Android Kotlin/ 단말기를 흔들었을때 호출되는 메서드 TIL # 32 (0) | 2021.05.17 |
안드로이드 스튜디오 GitHub 오류 / 422 unprocessable entity - repository creation failed. [repository; description]custom: description control characters are not allowed TIL # 31 (0) | 2021.05.16 |
안드로이드 코틀린 / lottie Animation을 이용해 인스타그램 하트기능 만들기/ TIL # 30 (2) | 2021.05.15 |
안드로이드 코틀린 Intent 알아보기 TIL #28 (2) | 2021.04.26 |
안드로이드 코틀린 Button 리스너와 setText 사용하기 TIL # 27 (0) | 2021.04.22 |
안드로이드 코틀린 뷰 바인딩 View Binding TIL #26 (0) | 2021.04.22 |
안드로이드 코틀린 FCM(FireBase Cloud messaging) 이용해서 알림 보내기 #25 (1) | 2021.04.20 |