๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿค์•ˆ๋“œ๋กœ์ด๋“œ ๊ฐœ๋ฐœ ๊ธฐ๋ก/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