LayoutInfalter를 사용했었던 예제이다.
# 결과물 미리보기
화면을 넘겨 명언을 보는 앱이다. 매우 쉽다고 생각이 들 수도 있지만 나름 안드로이드 개발에 있어 중요한 개념을 사용했다. 리사이클러 뷰를 사용하여 리스트넘어가는 걸 구현했으며, 데이터 값 또한 FireBase에서 관리하기 때문에 Firebase에서 값을 받아온다.
# 개발순서
- 리사이클러뷰 만들기
- 파이어 베이스 Remot Config에 값 넣어주기
- 리사이클러뷰 어댑터에 리스트 넣어주기
- 프로그레스바 만들기
- setPageTransfomer이용하여 애니메이션 효과 주기
# 리사이클러뷰 만들기
리사이클러뷰의 탄생은 ListView를 보완하기 위해서 만들어졌다. ListView만을 사용하면 화면에서 보이지 않는 View들도 만들어지고 지워지고를 반복하면서 쓸데없는 자원을 낭비한다. RecyclerView는 이렇게 생성-삭제가 반복되는 View를 재사용하는 것이다.
리사이클러뷰를 만들기 위해서는
- 안에 들어갈 View에 대해 XML 만들기
- 어댑터 속 ViewHolder 구현하기
- 어댑터 속 override 하기
- onCreateViewHolder() override
- onBindViewHolder() override
- getItemCount() override
- 어댑터 속에 ViewHolder구현하기
여기서 가장 중요한 개념은 ViewHolder는 View하나를 정의하는 것이라고 생각하면 된다. findViewById와 같은 것을 통해 안에 들어갈 것들을 매칭한다고 생각하면 된다.
어댑터를 만들면서 인자로는 List, Array등 여러개의 데이터가 담긴 집합을 넘겨주면, 어댑터안에서 바인드 시키는 것을 구현하면 된다.
## data class
본 앱에서는 인용구 인용구에는 quote(명언)와 name(저자)를 파이어베이스에서 받아와 사용 할 것이다. data class를 정의하여 어댑터가 해당 데이터 클래스의 리스트를 받게 해주자.
// data class 정의
data class Quote(
val quote: String,
val name : String
)
## 어댑터
// inner class와 클래스안에 클래스를 정의하는 것..?
class QutoesPagerAdapter(
val quotes: List<Quote>
) : RecyclerView.Adapter<QutoesPagerAdapter.QuoteViewHolder>() {
//view 홀더가 생성 되는 곳
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QuoteViewHolder {
}
override fun onBindViewHolder(holder: QuoteViewHolder, position: Int) {
}
override fun getItemCount(): Int {
}
// view 홀더자체를 정의하는 곳
class QuoteViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
}
}
}
다음과 같이 구성된다. 어댑터가 인자로 데이터를 받아온다. 이제 이 인자를 사용했을때 view들이 어떻게 그려져야 하는지를 정해줘야 한다. ViewHolder를 정의해주자
## ViewHolder
맨위 그림처럼 파란색 박스 하나가 Holder라고 생각했다. 그래서 그 홀더에 어떤게 들어거야하는지를 명시해준다. bind는 이후 onBindeViewHolder에서 조금 직관적이게 bind시키려고 함수화 시켰다.
class QuoteViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val quoteTextView: TextView = itemView.findViewById(R.id.quoteTextView)
private val nameTextView: TextView = itemView.findViewById(R.id.nameTextView)
fun bind(quote: Quote) {
quoteTextView.text = quote.quote
nameTextView.text = quote.name
}
}
홀더를 만드는방법을 정의했으면, 홀더를 만들어줘야한다.
## onCreateViewHolder
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QuoteViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_quote, parent, false)
return QuoteViewHolder(view)
}
return값에 주목하면 ViewHolder를 만들어서 반환한다. LayoutInflater를 통해 내가 안에 들어갈 layout(내가 설계한)을 가져온다. ViewHolder는 전달받은 view를 통해서 어디에 뭐가 들어가야할지를 알아낸다
## onBindViewHolder
override fun onBindViewHolder(holder: QuoteViewHolder, position: Int) {
holder.bind(quotes[position], isNameRevealed)
}
이름 그대로 viewHolder에 데이터값들을 bind시킨다고 생각했다. 지금은 빈 viewHolder들이 만들어진 상태일텐데 이 viewHolder에 데이터들을 채워주는 것이라고 생각했다. 스크롤하면서 변하는 position들이 실시간으로 바뀌는 듯 하다.
## getItemCount
override fun getItemCount(): Int {
return quotes.size
}
전체 아이템 갯수를 리턴한다.
## 어댑터 연결
viewPager.adapter = QutoesPagerAdapter(quotes)
이제 사용할 곳에서 quotes를 넘겨준걸 적용할 view에 어댑터속성으로 주면된다. 나는 viewPager를 사용했기 때문에 viewPager의 adapter로 넘겨줬다.
# 파이어베이스 Remote config이용해서 JSON값 주고 받기
매개변수 추가를 통해 JSON형식의 quotes들의 배열을 넣어준다. is_name_revealed는 추가적인 옵션을 위해 만든 건데 false옵션을 주게 되면 명언 앱 사용자들은 명언 - 이름 구조로 돼 있는데 이름을 볼수 없게 된다. 위 코드는 저 기능을 배제했지만 간단하게 viewHolder에서 visibility속성을 분기처리해주며 된다. 여튼 이제 저 값을 받아오도록 하자
## remote config에서 값 받아오기
// fetch시간은 디폴트가 12시간임 그렇기 때문에 기본 설정시간을 바꿔줘야함
private fun initData() {
val remoteConfig = Firebase.remoteConfig
// 기본 fetch시간을 0 으로설정
remoteConfig.setConfigSettingsAsync(
remoteConfigSettings {
minimumFetchIntervalInSeconds = 0
}
)
remoteConfig.fetchAndActivate().addOnCompleteListener {
if(it.isSuccessful) {
progressBar.isGone = true // 로딩창 안보이게 하기
val quotes = parseQuotesJson(remoteConfig.getString("quotes"))
val isNameRevealed = remoteConfig.getBoolean("is_name_revealed")
displayQuotesPager(quotes,isNameRevealed)
}
}
}
private fun parseQuotesJson(json: String): List<Quote> {
val jsonArray = JSONArray(json)
var jsonList = emptyList<JSONObject>()
for (index in 0 until jsonArray.length()) {
val jsonObject = jsonArray.getJSONObject(index)
jsonList = jsonList + jsonObject
}
return jsonList.map{
Quote(
quote = it.getString("quote"),
name = it.getString("author")
)
}
}
주기설정을 해주지 않으면 기본 12시간 주기로 변경 사항이 반영된다. 배포할떄는 위 설정을 권유하지만, 개발단계이기 때문에 0초로 준다. 그래야지 변경되는걸 바로 확인 할 수 있다. 받아온 값을 Quote로 만들어 List로 만들어 어댑터에 전달해준다.
# 느낀점
리사이클러뷰 옛날에 만들때 정말 애먹었는데 지금은 나름 구조를 알면서 사용해보니까 생각보다 쉬웠다. 그때는 저기에 버튼까지 연동해서 버튼에 대한 동작도 정의했는데, 지금 생각해보면 저기에 버튼 연동하는것도 어려울거 같다. 물론 필수적이겠지..
remote Config는 정말 신기했다. 단순 데이터만 주는게 아니라 저런식으로 false ture값으로 어떤걸 보여주고 안보여주고 할 수 있다니.. 읽어보면 더 많은 기능들이 있었다.
참고