본문 바로가기

카테고리 없음

RecyclerView,viewHolder,Fagment

RecyclerView 사용 이유 

Listview의 경우 새롭게 리스트 항목을 렌더링 할때마다 레이아웃을 인플레이트 해야 하며  인플레이트 된 뷰를 찾아야 합니다. 그런데 이 두과정이 메모리와 성능에 악영향을 미치기 때문에 이것을 해결하기 위해 RecycleView를 사용합니다

RecycleView를 사용하면 레이아웃을 매번 새롭게 인플레이트 하지 않고 기존에 있었던것을 재활용하게 됩니다. 그리고 ViewHolder pattern을 강제하기 때문에 한번 찾아 놓은 뷰를 계속해서 재활용 할 수 있습니다.

 

ViewHolder pattern

인플레이트 된 뷰들을 계속해서 찾는게 아니라 한번 찾아 놓고 그 객체의 참조를 어댑터 클래스의 내부 클래스로 저장해놓는 방식을 말합니다. 이렇게 객체를 보관 해두면 딱 한번만 찾으면 되기 때문에 성능 저하를 막을수 있습니다.

 

Multiple View Types RecycleView

서로 다른 타입으로 넣을 data를 만듭니다 ->

"data" : [
  {
    "viewType" : "ONE_TEXT"
    "viewObject" : {
      "contents" : "제목이 없는 글 입니다"
    }

  }
  {
    "viewType" : "TWO_TEXT"
    "viewObject" : {
      "title" : "제목입니다"
      "contents" : "제목이 없는 글 입니다"
    }
   
  }

  {
    "viewType" : "ONE_IMAGE"
    "viewObject" : {
      "url" : "
    }
   
  }

 

 

각 뷰 타입에 맞는 레이아웃을 만듭니다. ->

위에서 다뤘던 서버로 부터 데이터를 담게 될 Entity를 정의 합니다

data class OneLineTextViewObjext(
  val contents: String
) : ViewObjext()
data class OneLineTextViewObjext(
  val title: String
  val contents: String
) : ViewObjext()
data class OneLineTextViewObjext(
  val url: String
) : ViewObjext()

 

각 뷰 타입을 함유하는 enum class를 정의 합니다

enum class ViewType(name: String){
  ONE_TEXT("ONE_TEXT")
  TWO_TEXT("TWO_TEXT")
  ONE_IMAGE("ONE_IMAGE")
}

 

새로운 뷰타입이 생길때 마다 뷰 홀더를 추가합니다

package com.example.commonrecyclerview.viewholder

import androidx.databinding.ViewDataBinding
import androidx.recyclerview.widget.RecyclerView
import com.example.commonrecyclerview.data.CommonItem

abstract class CommonViewHolder(
    binding: ViewDataBinding
) : RecyclerView.ViewHolder(binding.root) {
    abstract fun bind(item: CommonItem)
}

 

class OneLineTextViewHolder(
  private val binding: ItemOneLineTextBinding
) : CommonViewHolder(binding) {
  override fun bind(item: CommonItem) {
      val viewObject = item.viewObject as OneLineTextViewObject
      binding.textContents.text = viewObject.contents
  }
}

 

class TwoLineTextViewHolder(
  private val binding: ItemTwoLineTextBinding
) : CommonViewHolder(binding) {
  override fun bind(item: CommonItem) {
      val viewObject = item.viewObject as TwoLineTextViewObject
      binding.textTitle.text = viewObject.title
      binding.textContents.text = viewObject.contents
  }
}
class OneImageViewHolder(
  private val binding: ItemOneImageBinding
) : CommonViewHolder(binding) {
  override fun bind(item: CommonItem) {
      val viewObject = item.viewObject as OneImageViewObject
      Glide.with(binding.root)
          .load(viewObject.url)
          .into(binding.image)
  }
}

 

 

class CommonAdapter(
  private val dataSet: Array<CommonItem>
) : RecyclerView.Adapter<CommonViewHolder>() {

  override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CommonViewHolder {
      return CommonViewHolderFactory.createViewHolder(parent, viewType)
  }

  override fun onBindViewHolder(holder: CommonViewHolder, position: Int) {
      holder.bind(dataSet[position])
  }

  override fun getItemCount(): Int = dataSet.size

  override fun getItemViewType(position: Int): Int {
      return ViewType.valueOf(dataSet[position].viewType).ordinal
  }
}

 

object CommonViewHolderFactory {
  fun createViewHolder(parent: ViewGroup, viewType: Int): CommonViewHolder {
      return when(viewType) {
          ViewType.ONE_LINE_TEXT.ordinal -> OneLineTextViewHolder(getViewDataBinding(parent, R.layout.item_one_line_text))
          ViewType.TWO_LINE_TEXT.ordinal -> TwoLineTextViewHolder(getViewDataBinding(parent, R.layout.item_two_line_text))
          else -> OneImageViewHolder(getViewDataBinding(parent, R.layout.item_one_image))
      }
  }

  private fun <T: ViewDataBinding> getViewDataBinding(parent: ViewGroup, layoutRes: Int): T {
      return DataBindingUtil.inflate(
          LayoutInflater.from(parent.context),
          layoutRes,
          parent,
          false
      )
  }
}

 

1. ViewType enum class에 새 ViewType를 넣는다.

2. ViewObject를 상속하는 DAO data class를 추가한다.

3. CommonViewHolder를 상속하는 ViewHolder class를 추가한다.

4. CommonViewHolderFactory에 새 분기문을 추가한다.

 

Activity 생명주기

 

  • onCreate(): 액티비티가 생성될 때 호출되는 메서드입니다. 초기화 작업이나 인터페이스 설정 등을 수행합니다.
  • onStart(): 액티비티가 화면에 보여지기 직전에 호출되는 메서드입니다. 액티비티가 사용자에게 보여지기 시작합니다.
  • onResume(): 액티비티가 사용자와 상호작용을 시작하고 활동 상태에 들어갈 때 호출되는 메서드입니다. 액티비티가 포그라운드에 있으며 사용자 입력을 받을 수 있습니다.
  • onPause(): 액티비티가 일시 중지되거나 다른 액티비티가 화면을 가리는 등 포그라운드에서 벗어나기 전에 호출되는 메서드입니다. 데이터 저장이나 네트워크 연결 해제 등의 작업을 수행합니다.
  • onStop(): 액티비티가 더 이상 사용자에게 보여지지 않고 화면에서 완전히 가려질 때 호출되는 메서드입니다. 자원 해제나 정리 작업을 수행합니다.
  • onRestart(): 액티비티가 onStop() 상태에서 다시 시작되기 전에 호출되는 메서드입니다. 일시 중지된 액티비티를 다시 시작할 때 필요한 초기화 작업을 수행합니다.
  • onDestroy(): 액티비티가 소멸될 때 호출되는 메서드입니다. 액티비티에 할당된 자원을 해제하고 종료 작업을 수행합니다.

Fagment

액티비티 내에 배치되어 어플리케이션 사용자 인터페이스를 구성하는 안드로이드 구성요소이다

 

특징 

  • 액티비티를 분할하여 화면의 한부분을 정의한다
  • 액티비티와 같이 레이아웃, 동작 처리, 생명주기를 가지는 독립적인 모듈이다
  • 다른 액티비티에서도 사용 할 수 있어 재사용이 뛰어나다
  • 액티비티 내에서 실행 중에 추가, 제거가 가능하다

Fragment 생성 시 default 생성자만 사용하길 추천

Fagment를 만들 때는 생성자를 오버로딩 하지 않고 생성시 파라미터를 Bundle에 담아 setArgument() 함수를 호출하는 방식을 사용하는 것이 일반적이다. 왜냐하면 안드로이드에 의해서 프래그먼트가 복원될 때는 프래그먼트의 기본 생성자를 호출하기 때문에 오버로딩된 생성자의 호출이 보장되지 않는다.

Fagment 생명주기

  • onAttach(): 프래그먼트가 액티비티에 연결될 때 호출되는 메서드입니다. 프래그먼트가 액티비티에 붙을 때 필요한 초기화 작업을 수행합니다.
  • onCreate(): 프래그먼트가 생성될 때 호출되는 메서드입니다. 초기화 작업이나 인터페이스 설정 등을 수행합니다.
  • onCreateView(): 프래그먼트의 레이아웃을 그리는 메서드입니다. 프래그먼트의 UI를 생성하고 반환합니다.
  • onViewCreated(): onCreateView() 후에 호출되는 메서드로, 프래그먼트의 UI가 만들어진 후에 호출됩니다. UI 관련 작업을 수행합니다.
  • onStart(): 프래그먼트가 화면에 보여지기 직전에 호출되는 메서드입니다. 프래그먼트가 사용자에게 보여지기 시작합니다.
  • onResume(): 프래그먼트가 사용자와 상호작용을 시작하고 활동 상태에 들어갈 때 호출되는 메서드입니다. 프래그먼트가 포그라운드에 있으며 사용자 입력을 받을 수 있습니다.
  • onPause(): 프래그먼트가 일시 중지되거나 다른 프래그먼트가 화면을 가리는 등 포그라운드에서 벗어나기 전에 호출되는 메서드입니다. 데이터 저장이나 네트워크 연결 해제 등의 작업을 수행합니다.
  • onStop(): 프래그먼트가 더 이상 사용자에게 보여지지 않고 화면에서 완전히 가려질 때 호출되는 메서드입니다. 자원 해제나 정리 작업을 수행합니다.
  • onDestroyView(): 프래그먼트의 UI가 소멸될 때 호출되는 메서드입니다. UI와 관련된 리소스를 해제합니다.
  • onDestroy(): 프래그먼트가 소멸될 때 호출되는 메서드입니다. 프래그먼트에 할당된 자원을 해제하고 종료 작업을 수행합니다.
  • onDetach(): 프래그먼트가 액티비티와의 연결이 끊길 때 호출되는 메서드입니다. 프래그먼트와 액티비티 간의 연결을 해제합니다.

 

728x90