마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

리사이클러뷰 아이템 업데이트 문제 도와주세요 ㅠ

0 추천

가리기 취소를 누르면 아이템이 원래대로 보여지는 로직인데, 아이템 하나만 누를땐 문제없고 두개이상을 연달아 누르면 제대로 작동을 하지 않습니다 ㅠ 왜 그럴까요,... ㅠㅠ

viewModel2.isUnblindPost.observe(binding.lifecycleOwner!!) { event ->
    event.getContentIfNotHandled()?.let {
        if (it) {
            tempItem2.blinded = false
            univListAdapter.updateItem(tempItem2,clickedPosition!!)
        }
    }
}

override fun onCancelClick(post: PostResult, position: Int) {
    tempItem2 = post
    clickedPosition = position
    viewModel2.unblindPost("잔디밭",post.postId)
}

ㅜㅠ (280 포인트) 님이 2023년 3월 7일 질문
올리신 코드만 가지고는 어디가 문제인지 알기가 힘들어요. 관련된 부분의 코드를 모두 올려보세요. PostResult클래스, Adapter, ViewHolder, ViewModel의 관련 코드 정도는 올려주셔야 할 것 같아요.
data class PostResult(
    @SerializedName("postId") val postId: Int,
    @SerializedName("title") val title: String,
    @SerializedName("contents") val contents: String,
    @SerializedName("nickname") val nickname: String,
    @SerializedName("likeCount") val likeCount: String,
    @SerializedName("commentCount") val commentCount: String,
    @SerializedName("createdAt") val createdAt: String,
    @SerializedName("blinded") var blinded: Boolean,
    @SerializedName("fileAttached") val fileAttached: Boolean,
    @SerializedName("reported") val reported: Boolean
)
inner class UnivTotalViewHolder(val binding : ItemPostBinding) : RecyclerView.ViewHolder(binding.root) {
        fun bind(postResult: PostResult, position: Int) {
            binding.setVariable(BR.item, postResult)

            if (postResult.blinded) {

                binding.likeimg.visibility = View.INVISIBLE
                binding.commentimg.visibility = View.INVISIBLE
                binding.photo.visibility = View.INVISIBLE
                binding.contenttext.visibility = View.INVISIBLE
                binding.titletext.visibility = View.INVISIBLE
                binding.liketext.visibility = View.INVISIBLE
                binding.commenttext.visibility = View.INVISIBLE
                binding.anonymous.visibility = View.INVISIBLE
                binding.time.visibility = View.INVISIBLE
                binding.dot.visibility = View.INVISIBLE

                binding.blindedText.visibility = View.VISIBLE
                binding.cancelBlindTv.visibility = View.VISIBLE

                binding.root.setOnClickListener {
                    mItemClickListener.onItemClick(postResult, position, true, false, null)
                }
            }

            else if (postResult.reported) {
                binding.likeimg.visibility = View.INVISIBLE
                binding.commentimg.visibility = View.INVISIBLE
                binding.photo.visibility = View.INVISIBLE
                binding.contenttext.visibility = View.INVISIBLE
                binding.titletext.visibility = View.INVISIBLE
                binding.liketext.visibility = View.INVISIBLE
                binding.commenttext.visibility = View.INVISIBLE
                binding.anonymous.visibility = View.INVISIBLE
                binding.time.visibility = View.INVISIBLE
                binding.dot.visibility = View.INVISIBLE

                binding.blindedText.visibility = View.VISIBLE
                binding.blindedText.text = postResult.title

                binding.root.setOnClickListener {
                    mItemClickListener.onItemClick(postResult, position, false, true, postResult.title)
                }
            }

            else {
                val fileattached = postResult.fileAttached
                if (fileattached) {
                    binding.photo.visibility = View.VISIBLE
                }else{
                    binding.photo.visibility = View.GONE
                }

                binding.likeimg.visibility = View.VISIBLE
                binding.commentimg.visibility = View.VISIBLE
                binding.photo.visibility = View.VISIBLE
                binding.contenttext.visibility = View.VISIBLE
                binding.titletext.visibility = View.VISIBLE
                binding.liketext.visibility = View.VISIBLE
                binding.commenttext.visibility = View.VISIBLE
                binding.anonymous.visibility = View.VISIBLE
                binding.time.visibility = View.VISIBLE
                binding.dot.visibility = View.VISIBLE

                binding.blindedText.visibility = View.GONE

                binding.root.setOnClickListener {
                    mItemClickListener.onItemClick(postResult, position, false, false, null)
                }
            }

            binding.cancelBlindTv.setOnClickListener {
                mItemClickListener.onCancelClick(postResult, position)
            }

            binding.executePendingBindings()

        }
    }
// 게시글 가리기 취소 완료 여부
    private val _isUnblindPost = MutableLiveData<Event<Boolean>>()
    val isUnblindPost: LiveData<Event<Boolean>> = _isUnblindPost
fun unblindPost(boardType: String, postId: Int) {
        _loading.postValue(Event(true))

        viewModelScope.launch(Dispatchers.IO) {

            var req = repository.unblindUnivPost(postId)
            if (boardType == "광장")
                req = repository.unblindTotalPost(postId)

            req.let { response ->
                if (response.isSuccessful) {
                    _loading.postValue(Event(false))

                    when (response.body()!!.code) {
                        OK -> {
                            _isUnblindPost.postValue(Event(true))
                            Log.d("tag_success","unblindPost: ${response.body()}")
                        }
                    }
                } else {
                    Log.d("tag_fail", "unblindPost Error: ${response.code()}")
                }
            }
        }
    }
이걸로 될까요? ㅠㅠ 계속해봐도 문제를 모르겠네요 ㅠ

1개의 답변

0 추천

unblindpost를 한 다음 뷰에서 어댑터 리프레시 하는 부분이 없지만, 제 추측으로는 unblindpost를 하고 나서 리사이클러뷰에 변경된 로우만 업데이트가 되어야하는데, 현재는 변수를 공유하고 있어서 내가 변경한 로우가 아니라 다른 로우에도 변경이 발생하는 것 처럼 보입니다. 리사이클러뷰이므로 각 로우마다 링크취소에 대한 정보를 가지고 있어야 합니다.

구현방법은 여러가지이지만 이미 PostResult 클래스에 blinded 속성이 있기 때문에, 그걸 이용하시면 됩니다.

뷰모델에서는 아래처럼, unblind 했을 때, 해당 아이템의 blinded속성만 변경하고 리스트를 다시 뷰로 알려주는 방식이 되어야 합니다. 어댑터에서는 받은 아이템을 화면에 갱신하기만 합니다. 성능을 고려한다면 어댑터에 DiffUtil이나 ListAdapter를 사용하시구요.

// PostResult 리스트를 뷰에 통보하는 LiveData. 화면에 보이는 리스트를 가져와서 여기를 통해 뷰에 알려주세요.
private _postResultsLiveData = MutableLiveData<List<PostResult>>(emptyList())
val postResults: LiveData get() = _postResultsLiveData

fun unblindPost(boardType: String, postId: Int) {
        _loading.postValue(Event(true))

        viewModelScope.launch(Dispatchers.IO) {

            ,,,

            req.let { response ->
                if (response.isSuccessful) {
                    ..

                    when (response.body()!!.code) {
                        OK -> {
                            //  해당 아이템만 변경하고 업데이트 된 리스트를 뷰에 통보
                            val updatedPostResults = postResults.map { postResult ->
                                  if (postResult.id == id) 
                                       postResult.copy(blinded = false)
                                  else
                                       postResult
                            }

                            _postResultsLiveData.postValue(updatedPostResults)
                            ..
                        }
                    }
                } else {
                    ...
                }
            }
        }
    }

그리고 뷰홀더에서 데이터바인딩을 사용하므로, 굳이 추가적으로 뷰의 상태를 코딩하기 보다는, BindingAdapter같은 걸 만드시거나 PostResult의 blinded 속성을 Int값으로 처리하면 아래처럼 뷰의 Visibility를 직접 설정하는 부분은 필요가 없을 것 같습니다.

fun bind(postResult: PostResult, position: Int) {
            ..

            if (postResult.blinded) {
                binding.likeimg.visibility = View.INVISIBLE
                binding.commentimg.visibility = View.INVISIBLE
                binding.photo.visibility = View.INVISIBLE
                binding.contenttext.visibility = View.INVISIBLE

...}

굳이 필요하다면 아래처럼, 간단하게 할 수있구요.

// Kotlin ktx 이용. invisible하면 화면에서 보이지는 않지만 자리는 차지하고 있고 클릭도 됩니다.

val blinded = postResult.blinded
                binding.likeimg.invisibile = blinded
                binding.commentimg.invisible = blinded
                binding.photo.invisibile = blinded
                binding.contenttext.invisible = blinded

// 또는 ConstraintLayout의 ViewGroup를 사용하시면 한번에 여러뷰의 보임 속성을 설정할 수가 있습니다.

 

spark (227,530 포인트) 님이 2023년 3월 8일 답변
그리고 한가지 꼭 추가하고 싶은 말은, 데이터가 서버에 저장이 되기 때문에 unblinkpost를 호출해서 성공메세지가 나오면 서버에서 모든 리스트를 다시 가져와서 보여주는게, 데이터의 일관성을 유지하기 위해서 필요합니다. 서버에 데이터가 보관될 경우는 대부분 사용하는 방식이기도 합니다.
...