11-4 리사이클러 뷰

StrayCat·2022년 11월 13일
0

리사이클러 뷰

  • 목록 화면을 만들 때 사용한다.

  • 구성요소

    • ViewHolder : 항목에 필요한 뷰 객체를 가진다.
    • Adapter : 항목을 구성한다.
    • LayoutManager : 항목을 배치한다.
    • ItemDecoration(옵션) : 항목을 꾸민다.
  • build.gradle 에 아래와 같이 등록한다.

dependencies {
	...
	implementation 'androidx.recyclerview:recyclerview:1.2.1'
}

리사이클러 뷰 등록

<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/recyclerView">
    
</androidx.recyclerview.widget.RecyclerView>
  • 내부 항목을 디자인한 item_main.xml 생성
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/item_root"
    android:orientation="horizontal"
    android:padding="16dp">

    <androidx.appcompat.widget.AppCompatTextView
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:textStyle="bold"
        android:textSize="16dp"
        android:id="@+id/item_data"/>
</androidx.appcompat.widget.LinearLayoutCompat>

뷰 홀더 준비

  • 뷰 바인딩 기법을 사용할 경우, 항목 레이아웃 XML 파일에 해당하는 바인딩 객체만 가지고 있으면 해당 객체에 항목을 구성하는 뷰가 자동으로 선언되었으므로 아래와 같은 코드로 생성이 가능한다.
class MyViewHolder(val binding: ItemMainBinding): RecyclerView.ViewHolder(binding.root)

어댑터 준비

  • 자동으로 호출되는 어댑터 함수들
    • getItemCount() : 항목 개수 판단
    • onCreateViewHolder() : 뷰 홀더를 생성하기 위해 호출
    • onBindViewHolder() : 뷰 홀더의 뷰에 데이터를 출력하려고 호출
class MyAdapter(val datas:MutableList<String>): RecyclerView.Adapter<RecyclerView.ViewHolder>(){

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        return MainActivity.MyViewHolder(
            ItemMainBinding.inflate(
                LayoutInflater.from(parent.context),
                parent,
                false
            )
        )
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        Log.d("lumineko", "onBindViewHolder $position")
        val binding = (holder as MainActivity.MyViewHolder).binding
		// 뷰 데이터 출력
        binding.itemData.text = datas[position]
		// 뷰 이벤트 추가
        binding.itemRoot.setOnClickListener{
            Log.d("lumineko", "click $position")
        }
    }

    override fun getItemCount(): Int {
        return datas.size
    }
}

리사이클러 뷰 출력

val datas = mutableListOf<String>()
for(i in 1..10) {
    datas.add("item $i")
}

binding.recyclerView.layoutManager = LinearLayoutManager(this)
binding.recyclerView.adapter = MyAdapter(datas)
binding.recyclerView.addItemDecoration(DividerItemDecoration(this, LinearLayoutManager.VERTICAL))

항목 동적 추가/제거

  • 항목을 구성하는 데이터에 새로운 값을 추가하거나 제거한 후 notifyDataSetChanged() 를 호출해준다.
datas.add("new data")
adapter.notifyDataSetChanged()

레이아웃 매니저

  • 레이아웃 매니저는 어댑터에서 만든 항목을 리사이클러뷰에 배치한다.
    • LinearLayoutManager : 가로/새로 배치
    • GridLayoutManager : 글리드 배치
    • StaggeredGridLayoutManager : 불규칙한 그리드 배치
  • 세로 배치
binding.recyclerView.layoutManager = LinearLayoutManager(this)
  • 가로 배치
val layoutManager = LinearLayoutManager(this)
layoutManager.orientation = LinearLayoutManager.HORIZONTAL
binding.recyclerView.layoutManager = layoutManager
  • 세로 그리드 배치
val layoutManager = GridLayoutManager(this, 2)
binding.recyclerView.layoutManager = layoutManager
  • 가로 그리드 배치
val layoutManager = GridLayoutManager(this, 2, GridLayoutManager.HORIZONTAL, false)
binding.recyclerView.layoutManager = layoutManager
  • 불규칙한 그리드 배치
val layoutManager = StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)
binding.recyclerView.layoutManager = layoutManager

아이템 데코레이션

  • 리사이클러 뷰를 다양하게 꾸밀 때 사용한다.
  • 라이브러리에서는 구분선을 출력하는 DividerItemDecoration 만을 제공해주므로 개발자가 직접 ItemDecoration 을 상속받는 클래스를 만들어 작업한다.
  • 구성요소
    • onDraw() : 항목이 화면에 배치되기전에 호출. Canvas 객체를 통해 항목 뒤의 배경을 작성한다.
    • onDrawOver() : 항목이 화면에 배치 된 후 호출. 항목 위쪽에 그림이 나타난다.( ex. 워터마크 )
    • getItemOffsets() : 항목 하나 당 한번씩 호출되어 각 항목을 꾸밀 수 있다.
class MyDecoration(val context : Context): RecyclerView.ItemDecoration(){
    override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
    	// 항목이 배치되기 전 호출
        super.onDraw(c, parent, state)
        c.drawBitmap(BitmapFactory.decodeResource(context.resources, R.drawable.stadium), 0f, 0f, null)
    }

    override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
    	// 항목이 모두 배치 된 후 호출
        super.onDrawOver(c, parent, state)
        val width = parent.width
        val height = parent.height

        val dr: Drawable? = ResourcesCompat.getDrawable(context.resources, R.drawable.kbo, null)
        val drWidth = dr?.intrinsicWidth
        val drHeight = dr?.intrinsicHeight

        val left = width/2 - drWidth?.div(2) as Int
        val top = height/2 - drHeight?.div(2) as Int
        c.drawBitmap(BitmapFactory.decodeResource(context.resources, R.drawable.kbo), left.toFloat(), top.toFloat(), null)
        
    }

    override fun getItemOffsets(
        outRect: Rect,
        view: View,
        parent: RecyclerView,
        state: RecyclerView.State
    ) {
    	// 개별 항목을 꾸밀 때 호출
        super.getItemOffsets(outRect, view, parent, state)
        
        val index = parent.getChildAdapterPosition(view) + 1
        if(index % 3 == 0)
        outRect.set(10,10,10,60)
        else
        outRect.set(10,10,10,10)

        view.setBackgroundColor(Color.LTGRAY)
        ViewCompat.setElevation(view, 20.0f)
    }

}
  • addItemDecoration() 함수를 통해 리사이클러 뷰에 적용한다.
binding.recyclerView.addItemDecoration(MyDecoration(this))

0개의 댓글