ABOUT ME

-

  • Android) RecyclerView를 쉽게 사용하도록 도와주는 Epoxy 알아보기
    Android 2021. 2. 15. 13:56


    Android에서 복잡한 뷰를 쉽게 개발할 수 있게 해주는 AirbnbEpoxy에 대해서 알아보겠습니다.

     

    Epoxy

    • Airbnb에서 만든 오픈소스 라이브러리입니다.
    • 여러 뷰 타입을 갖는 RecyclerView에서 효율적으로 사용 가능합니다. 이를 구현하는데 필요한 boilerplate code를 제거해줍니다.
    • 디자인 변경에 빠르게 적응하고 코드를 모듈화하고 관리하기 쉽게 만들기 위한 많은 인터페이스를 제공합니다.

     

    예를 들어 위의 화면들과 같이 3개의 각각 다른 뷰타 입을 가지는 화면이 있습니다.

     

    이를 RecyclerView에서 구현하려면 다음과 같은 것들이 필요합니다.

     

    1. RecyclerView Object 
    2. RecyclerView.ViewHolder 
    3. RecyclerView.Adapter

    Epoxy를 사용하면 다음의 것들로 구현할 수 있습니다.

     

    1. Epoxy Models - 본질적으로 RecyclerView.ViewHolder 입니다. RecyclerView에서 뷰가 어떻게 보이는지 설명합니다.
    2. Epoxy Controllers - RecyclerView에 추가될 Model을 제어하고 Epoxy Models 또는 ViewHolder를 RecyclerView에 추가할 수 있는 플러그인-아웃 인터페이스를 제공하므로 다양한 화면을 빠르고 쉽게 개발가능합니다.

    Usage

    Android Sutdio 4.1.2 version 기준으로 작성한 예제입니다.

     

    build.gradle(:app)

    plugins {
        id 'kotlin-kapt'
    }
    
      //Added for Epoxy
        kapt {
            correctErrorTypes = true
        }
    dependencies {
    	// Android RecyclerView
        implementation "androidx.recyclerview:recyclerview:1.1.0"
    
        // Airbnb Epoxy
        implementation "com.airbnb.android:epoxy:2.8.0"
        kapt "com.airbnb.android:epoxy-processor:2.8.0"
    }

     

    먼저 Model을 만들겠습니다.

    Food

    data class Food (
            val image:Int=-1,
            val title:String="",
            val description:String=""
    )

     

    다음은 EpoxyModelWithHolder를 상속받는 뷰홀더 모델을 만듭니다. 

    특정 뷰를 뷰홀더에 바인딩 한 다음 데이터를 전달하기 위해 RecyclerView.Adapter에서 확장 된 어댑터가 필요합니다.

    SingleItemModel

    @EpoxyModelClass(layout = R.layout.singlefood_layout)
    abstract class SingleFoodModel :     EpoxyModelWithHolder<SingleFoodModel.FoodHolder>(){
    
        @EpoxyAttribute
        var id : Long = 0
    
        @EpoxyAttribute
        @DrawableRes
        var image : Int = 0
    
        @EpoxyAttribute
        var title:String? = ""
    
        @EpoxyAttribute
        var desc:String = ""
    
    
        override fun bind(holder: FoodHolder) {
            holder.imageView?.setImageResource(image)
            holder.titleView?.text = title
        }
    
        inner class FoodHolder : EpoxyHolder(){
    
            lateinit var imageView:ImageView
            lateinit var titleView: TextView
            lateinit var descView:TextView 
    
            override fun bindView(itemView: View?) {
                imageView = itemView?.image
                titleView = itemView?.title
                descView = itemView?.desc
            }
    
        }
    }

     

    Epoxy에서는 EpoxyModels에서 처리합니다. 어댑터와 마찬가지로 Epoxy Holders에서 확장된 ViewHolder 클래스도 있습니다.

    위의 코드에서 볼 수 있듯이이 Model 클래스는 RecyclerView.Adapter 클래스에있는 것과 같습니다. 이 경우 FoodHolder 클래스이고 EpoxyHolder에서 파생 된 뷰 홀더 클래스를 포함합니다.

    Model properties 들은 @EpoxyAttribute 어노테이션을 통해 정의됩니다.

     

     

    다음은 앱에 표시할 데이터가 필요합니다. 임의의 데이터를 생성하는 데이터 팩토리를 생성합니다.

    FoodDataFactory

    object FoodDataFactory{
    
        //region Random Data Generators
        private val random = Random()
    
        private val titles = arrayListOf<String>("Nachos", "Fries", "Cheese Balls", "Pizza")
    
        private fun randomTitle() : String {
            val title = random.nextInt(4)
            return titles[title]
        }
    
        private fun randomPicture() : Int{
            val grid = random.nextInt(7)
    
            return when(grid) {
                0 -> R.drawable.nachos1
                1 -> R.drawable.nachos2
                2 -> R.drawable.nachos3
                3 -> R.drawable.nachos4
                4 -> R.drawable.nachos5
                5 -> R.drawable.nachos6
                6 -> R.drawable.nachos7
                else -> R.drawable.nachos8
            }
        }
        //endregion
    
        fun getFoodItems(count:Int) : List<Food>{
            var foodItems = mutableListOf<Food>()
            repeat(count){
                val image = randomPicture()
                val title = randomTitle()
                @StringRes val desc = R.string.nachosDesc
                foodItems.add(Food(image,title,desc))
            }
            return foodItems
        }
    }
    

     

     

    그 다음은 controller를 만들러 가보겠습니다. EpoxyController를 상속받고 buildModels 메소드를 implement 합니다.

    SingleFoodController

    class SingleFoodController : EpoxyController(){
    
        var foodItems : List<Food>
    
        init {
            foodItems = FoodDataFactory.getFoodItems(50)
        }
    
        override fun buildModels() {
            var i:Long =0
    
            foodItems.forEach {food ->
                SingleFoodModel_()
                        .id(i++)
                        .image(food.image)
                        .title(food.title)
                        .addTo(this)
            }
        }
    
    }

    이 클래스는 EpoxyController 클래스에서 확장되었으며이 컨트롤러에 모델 / 모델을 추가하는 buildModels () 메서드를 재정의해야합니다.

     

    이제 리사이클러뷰에서 생성된 기능을 설정할 준비가 되었습니다. 이를 위해 MainActivity에서 다음과 같이 코드를 작성합니다.

     

    MainActivity

    class MainActivity : AppCompatActivity() {
    
        private val recyclerView : RecyclerView by lazy { findViewById<RecyclerView>(R.id.recycler_view) }
        
    private val singleFoodController : SingleFoodController by lazy 
    { SingleFoodController() }
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            initRecycler()
        }
    
        private fun initRecycler(){
            val linearLayoutManager = LinearLayoutManager(this)
            recyclerView.apply {
                layoutManager = linearLayoutManager
                setHasFixedSize(true)
                adapter = singleFoodController.adapter
                addItemDecoration(DividerItemDecoration(this@MainActivity, linearLayoutManager.orientation))
            }
    
            //This statement builds model and add it to the recycler view
            singleFoodController.requestModelBuild()
        }
    }

    Reference

    Simplifying Recycler View with Epoxy in Kotlin — Nachos Tutorial Series

    반응형

    댓글

Designed by Me.