๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿค2024 ์•ˆ๋“œ๋กœ์ด๋“œ/Filmo ์˜ํ™” ํ”„๋กœ์ ํŠธ

Android Recyclerview : no attached adapter , skipping layout ์—๋Ÿฌ ํ•ด๊ฒฐ ๊ณผ์ • ๊ธฐ๋ก

by hyeonha 2024. 5. 15.

๋ชฉ์ฐจ

    ๐Ÿ”Ž๋ฌธ์ œ ์ƒํ™ฉ

    ๋ฉ”์ธ ํ™”๋ฉด์ด ๋‚˜ํƒ€๋‚  ๋•Œ ๋ฆฌ์‚ฌ์ดํด๋Ÿฌ๋ทฐ๊ฐ€ ๋ณด์ด์ง€ ์•Š๋Š”๋‹ค.

    ๐Ÿซฑ์‹œ๋„ํ•œ ๋ฐฉ๋ฒ•

    ๊ตฌ๊ธ€๋ง์„ ํ†ตํ•ด ๋ช‡๊ฐ€์ง€ ์›์ธ์„ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ์—ˆ๊ณ , ํ•ด๋‹น ๋‚ด์šฉ์— ๋”ฐ๋ผ ์ฝ”๋“œ๋ฅผ ํ™•์ธํ•ด๋ณด์•˜๋‹ค.

    1๏ธโƒฃsetAdapter๊ฐ€ ์ž˜ ๋˜๊ณ ์žˆ๋Š”์ง€ ํ™•์ธ

    ํ”„๋ž˜๊ทธ๋จผํŠธ์—์„œ onViewCreated, onCreateView ์œ„์น˜ ๋ชจ๋‘์— ์ ์šฉํ•ด๋ณด์•˜์ง€๋งŒ ํ•ด๊ฒฐ๋˜์ง€ ์•Š์•˜๋‹ค.

    2๏ธโƒฃ๋ ˆ์ด์•„์›ƒ ๋งค๋‹ˆ์ €๊ฐ€ ์ž˜ ์„ค์ •๋˜์–ด์žˆ๋Š”์ง€ ํ™•์ธ

    ํ”„๋ž˜๊ทธ๋จผํŠธ ์ƒ์—์„œ ๋ ˆ์ด์•„์›ƒ ๋งค๋‹ˆ์ €๋ฅผ ์„ค์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•๊ณผ xml ๋ ˆ์ด์•„์›ƒ ํŒŒ์ผ ์ƒ์—์„œ ์„ค์ •ํ•ด์ฃผ๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ๋ชจ๋‘ ์‹œ๋„ํ–ˆ์ง€๋งŒ ํ•ด๊ฒฐ๋˜์ง€ ์•Š์•˜๋‹ค.

     

    - ํด๋ž˜์Šค ๋‚ด์—์„œ ๋ ˆ์ด์•„์›ƒ ๋งค๋‹ˆ์ € ์„ค์ •

    binding.reviewRecyclerView.layoutManager = LinearLayoutManager(context)

     

    - ๋ ˆ์ด์•„์›ƒ ํŒŒ์ผ์—์„œ ๋ ˆ์ด์•„์›ƒ ๋งค๋‹ˆ์ € ์„ค์ •

     <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/review_recycler_view"
                    ```
                app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>

     

    3๏ธโƒฃ binding์ด ์ œ๋Œ€๋กœ ์„ค์ •๋˜์–ด์žˆ๋Š”์ง€ ํ™•์ธ

    ํ•œ ๋ธ”๋กœ๊ทธ ๊ธ€์—์„œ binding์œผ๋กœ ๋ณ€๊ฒฝ ํ›„ ํ•ด๊ฒฐ๋˜์—ˆ๋‹ค๋Š” ๊ธ€์„ ๋ณด์•˜๋‹ค.

    ๋‹น์‹œ ์–ด๋Œ‘ํ„ฐ์—์„œ๋Š” ๋ฐ”์ธ๋”ฉ์ด ์•„๋‹Œ findViewById ๋กœ ๋ทฐ๋ฅผ ์ฐธ์กฐํ•ด์„œ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด ๋ถ€๋ถ„๋„ binding์„ ์‚ฌ์šฉํ•˜๋„๋ก ๋ณ€๊ฒฝํ•ด์ฃผ์—ˆ๋‹ค.

    ๋˜ํ•œ ๋ณ€๊ฒฝํ•˜๋ฉด์„œ binidng๊ณผ findViewById ๋กœ ๋ทฐ๋ฅผ ์ฐธ์กฐํ•˜๋Š” ๊ฒƒ์˜ ์ฐจ์ด์— ๋Œ€ํ•ด์„œ๋„ ์•Œ์•„๋ณด์•˜๋‹ค.(์•„๋ž˜์—์„œ ๋‹ค๋ฃฐ ์˜ˆ์ •!)

    4๏ธโƒฃAdapter์—์„œ item์˜ size๊ฐ€ 0์ด ์•„๋‹Œ์ง€ ํ™•์ธ, Adapter๋ฅผ ListAdapter๋กœ ๋ณ€๊ฒฝํ•ด์„œ ํ™•์ธ

    class ReportAdapter : ListAdapter<ReportList,ReportAdapter.ReportViewHolder>(diffutil) {
    
    ```
    
     companion object {
            val diffutil = object : DiffUtil.ItemCallback<ReportList>() {
                override fun areContentsTheSame(oldItem: ReportList, newItem: ReportList): Boolean {
                    return oldItem == newItem
                }
    
                override fun areItemsTheSame(oldItem: ReportList, newItem: ReportList): Boolean {
                    return oldItem.reportId ==newItem.reportId
                }
            }
        }
    }

     

    ๐Ÿšจ์›์ธ

    ์›์ธ์œผ๋กœ ์ถ”์ธก๋˜๋Š” ๋ชจ๋“  ๊ณณ์— ๋กœ๊ทธ๋ฅผ ์‹ฌ์–ด ํ™•์ธํ•ด๋ณด์•˜๋Š”๋ฐ  ๋ทฐ๋ชจ๋ธ์˜ livedata ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ์ด ๊ด€์ฐฐ๋˜์ง€ ์•Š์•˜๋‹ค.

    ๋ทฐ๋ชจ๋ธ์—์„œ report๊ฐ€ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๊ณ  ์žˆ์—ˆ๋‹ค. 

     

    ์—๋Ÿฌ ๋‚ด์šฉ์„ ํ™•์ธํ•ด๋ณด์•˜๋”๋‹ˆ ์„œ๋ฒ„์—์„œ ๋Œ์•„์˜ค๋Š” ์‘๋‹ต๊ฐ’๊ณผ ์ง€์ •ํ•ด์ค€ DTO์˜ ์†์„ฑ์ด ๋งž์•„ Serializable  ๊ด€๋ จ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๊ณ  ์žˆ์—ˆ๋‹ค.

    ๋‚˜๋Š” ๋ฆฌ์‚ฌ์ดํด๋Ÿฌ๋ทฐ์— ๋ณด์—ฌ์ค„ ๋ฐ์ดํ„ฐ ๋ฆฌ์ŠคํŠธ์— ์ข‹์•„์š” ์—ฌ๋ถ€๋ฅผ ๋‹ด์€ ๋ฆฌ์ŠคํŠธ๋ฅผ ํ•ฉ์ณ์„œ ์–ด๋Œ‘ํ„ฐ์— ๋„˜๊ฒจ์ฃผ๊ธฐ ์œ„ํ•ด DTO ํด๋ž˜์Šค์— API ์‘๋‹ต๊ฐ’์—๋Š” ์—†๋Š” isLiked ์†์„ฑ์„ ์ถ”๊ฐ€ํ•ด์ฃผ๊ณ  ์žˆ์—ˆ๋Š”๋ฐ ์ด๊ฒƒ์ด ๋ฌธ์ œ์˜ ์›์ธ์ด์—ˆ๋‹ค.

     

     

    ํ˜„์žฌ ๊ฐ์ƒ๋ฌธ์˜ DTO๋Š” ์„œ๋ฒ„์—์„œ ์ „์†ก๋˜๋Š” ์‘๋‹ต๊ฐ’ ํ˜•ํƒœ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์–ด์•ผํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์‘๋‹ต๊ฐ’์— ํฌํ•จ๋˜์–ด ์žˆ์ง€ ์•Š์€ isLiked๋ฅผ ํฌํ•จํ•˜๊ณ  ์žˆ์–ด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒƒ์ด๋‹ค.

    ์œ„์™€ ๊ฐ™์€ ํ๋ฆ„์œผ๋กœ ์–ด๋Œ‘ํ„ฐ๊ฐ€ ๊ตฌ์„ฑ๋˜์ง€ ์•Š์•„ no attached adapter, skipping layout ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒƒ์ด์—ˆ๋‹ค.

     

    ๊ฒฐ๊ตญ ๊ทผ๋ณธ์ ์ธ ์—๋Ÿฌ์˜€๋˜ ๊ฒƒ์ด๋‹ค!! 

    ์„œ๋ฒ„ api ์‘๋‹ต๊ฐ’ ์ˆ˜์ • ์‚ฌํ•ญ์„ ๋ฐ˜์˜ํ•ด์ฃผ์ง€ ์•Š์•„ ์ƒ๊ธด ์—๋Ÿฌ


    ๐Ÿ’กํ•ด๊ฒฐ

    ์šฐ์„  ์„œ๋ฒ„์˜ ์‘๋‹ต๊ฐ’์„ ๋‹ด์•„์ฃผ๋Š” ReportList ๋ฐ์ดํ„ฐ ํด๋ž˜์Šค์—์„œ isLiked ์†์„ฑ์„ ์‚ญ์ œํ•ด์ฃผ์—ˆ๋‹ค.

    ๐ŸซฑDTO ๋งคํ•‘ ๊ณผ์ •์˜ ํ•„์š”

    ์‘๋‹ต๊ฐ’์„ ๋‚ด๊ฐ€ ์›ํ•˜๋Š” ํ˜•ํƒœ๋กœ ์กฐ์ž‘ํ•  ์ˆ˜ ์—†์„๊นŒ?  ์•Œ์•„๋ณด๋Š” ์ค‘, DTO๋ฅผ ํ™”๋ฉด์— ๋ณด์—ฌ์ค„ ์ •๋ณด๋งŒ์œผ๋กœ ์ด๋ฃจ์–ด์ง„ ๊ฐ์ฒด๋กœ ๋งคํ•‘ํ•˜๋Š” ๊ณผ์ •์ด ํ•„์š”ํ•˜๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ฒŒ ๋˜์—ˆ๋‹ค!

     

    ๋‚˜๋Š” ์ง€๊ธˆ DTO๋ฅผ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ฆฌ์‚ฌ์ดํด๋Ÿฌ๋ทฐ ์•„์ดํ…œ์—์„œ ์ง์ ‘์ ์œผ๋กœ ์“ฐ์ด์ง€ ์•Š๋Š” ์ •๋ณด๊นŒ์ง€ ๋ชจ๋‘ ํฌํ•จ๋œ ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ๋‹ค.

    ๋˜ํ•œ ๊ฒŒ์‹œ๊ธ€ ์ •๋ณด๋ฅผ ๋‹ด๊ณ  ์žˆ๋Š” api์˜ ์‘๋‹ต์—์„œ๋Š” ์ข‹์•„์š” ์—ฌ๋ถ€ ์†์„ฑ์„ ๋‹ด๊ณ  ์žˆ์ง€ ์•Š์•˜๋‹ค. ๋”ฐ๋ผ์„œ 

     

    ๋”ฐ๋ผ์„œ ํ•„์š”ํ•œ ๊ฐ์ฒด๋กœ์˜ ๋งคํ•‘ํ•˜๋Š” ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋ฉฐ ์ข‹์•„์š” ์—ฌ๋ถ€๋ฅผ ๋‹ด๊ณ  ์žˆ๋Š” ์†์„ฑ๋„ ํ•จ๊ป˜ ํด๋ž˜์Šค์— ๋„ฃ์–ด์ฃผ๊ธฐ๋กœ ํ•˜์˜€๋‹ค.

     

    ์ด๋Š” ๋ฆฌ์‚ฌ์ดํด๋Ÿฌ๋ทฐ๊ฐ€ ์ฒ˜์Œ ๋œฐ ๋•Œ์—๋„ ์ข‹์•„์š” ์—ฌ๋ถ€๊นŒ์ง€ ํฌํ•จ๋œ ํ™”๋ฉด์„ ์ œ๊ณตํ•˜๊ธฐ ์œ„ํ•ด์„œ์ด๋‹ค. 

    ๋”ฐ๋ผ์„œ ์•„๋ž˜์™€ ๊ฐ™์ด ํ™”๋ฉด์— ๋ณด์—ฌ์ค„ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ด์€ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด์ฃผ์—ˆ๋‹ค.

     

    class ReportItem(
        val reportId: String,
        val title: String,
        val content: String,
        val createDate: String,
        val imageUrl: String?,
        val nickname: String,
        val likeCount: Int,
        val replyCount: Int,
        val bookmarkCount: Int,
        var isLiked: Boolean,
    )

     

    ๊ทธ๋ฆฌ๊ณ  ์„œ๋ฒ„ api์—์„œ ๋ฐ›์€ ์‘๋‹ต๊ฐ’๊ณผ ๋งคํ•‘์‹œ์ผœ์ฃผ์—ˆ๋‹ค.

            private fun mapForReportItem(
                reportList: List<ReportList>,
                likeList: MutableList<Boolean>,
            ): List<ReportItem> {
                return reportList.mapIndexed { index, reportItem ->
                    ReportItem(
                        reportId = reportItem.reportId,
                        title = reportItem.title,
                        content = reportItem.content,
                        createDate = reportItem.createDate,
                        imageUrl = reportItem.imageUrl,
                        nickname = reportItem.nickname,
                        likeCount = reportItem.likeCount,
                        replyCount = reportItem.replyCount,
                        bookmarkCount = reportItem.bookmarkCount,
                        isLiked = likeList[index],
                    )
                }
            }

     

    reportList ๋ฟ ์•„๋‹ˆ๋ผ likeList๋„ ๊ฐ๊ฐ ReportItem์— ๋„ฃ์–ด์ค˜์•ผํ•œ๋‹ค. ์ด ๋•Œ ๊ทธ๋ƒฅ map์„ ์‚ฌ์šฉํ•ด์„œ๋Š” likeList๋ฅผ isLiked ์†์„ฑ์— ๊ฐ๊ฐ ๋งคํ•‘ํ•ด์ค„ ์ˆ˜ ์—†์—ˆ๋‹ค.

     

    ์ด๋ฅผ mapIndexed๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค. ์•„๋ž˜์—์„œ ์•Œ์•„๋ณด์ž!

    - mapIndexed๋ž€?

    ๋”๋ณด๊ธฐ

    - mapIndexed๋ž€?

       fun requestReport() {
                viewModelScope.launch {
                    val result = searchReport()
                    if (result.isSuccess) {
                        val response = result.getOrNull()
                        if (response != null) {
                            response.reportList.forEach {
                                val likeResult = checkLike(it.reportId)
                                if (likeResult.isSuccess) {
                                    likeResult.getOrNull()?.let { isLiked ->
                                        likeList.add(isLiked)
                                    }
                                }
                            } 
                            _report.value = mapForReportItem(response.reportList, likeList)
    ```
    }

    ๐Ÿฅณ๋“œ๋””์–ด ํ•ด๊ฒฐ!!!


    ๐Ÿ’ก์ถ”๊ฐ€๋กœ ๊ณต๋ถ€ํ•  ๋‚ด์šฉ

    ๐Ÿซฑ๋ทฐ ๊ฒฐํ•ฉ๊ณผ findViewById์˜ ์ฐจ์ด

    ๐ŸซฑDiffUtil์ด๋ž€?

    ๐ŸซฑDTO๋ฅผ ๋งคํ•‘ํ•ด์ค„ ํด๋ž˜์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•

    ๐ŸซฑSerializable์˜ ์—ญํ•  ์ดํ•ดํ•˜๊ธฐ 

    ๐ŸซฑCall๊ณผ Result์˜ ์‚ฌ์šฉ ์‚ฌ๋ก€ ์•Œ์•„๋ณด๊ธฐ

     

    728x90