1

I'm having this issue, with recyclerView, may you check two screenshots below:

enter image description here

enter image description here

So that's my issue, when onNotifyItemChange runs, other info are changed, incorrectlty. Now here goes my adapter:

class TimelineAdapter(var timeline: TimelineDTO,
                      var toggleLikeClicked: OnRowClick,
                      var onCommentClicked: OnRowClick,
                      var onMediaClick: OnRowClick,
                      val onUserClicked: OnRowClick,

                      val reportPost: OnRowClick,
                      val editPost  : OnRowClick,
                      val deletePost: OnRowClick,

                      val contract: TimelineViewContract) : BaseAdapter<RecyclerView.ViewHolder>() {


    init {
        setHasStableIds(true)
    }

    private var currentItem: Int = 0

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        when (PostType.fromInt(viewType)) {
            PostType.BASIC -> {
                return PostViewHolder(parent.inflate(R.layout.row_post_default_item),
                                                    toggleLikeClicked, onCommentClicked, onMediaClick,
                                                    onUserClicked, reportPost,
                                                    editPost,
                                                    deletePost,
                                                    FirebaseAnalytics.getInstance(contract.returnContext()))
            }
            PostType.NEXT_TALKS -> {
                return PostNextTalksViewHolder(parent.inflate(R.layout.row_post_next_talks_item),
                                                    contract)
            }
            else -> {
                if(!BuildConfig.DEBUG) {
                    Crashlytics.log("Should not come here")
                }
                logE("adapter else!!")
                return PostViewHolder(parent.inflate(R.layout.row_post_default_item),
                                            toggleLikeClicked, onCommentClicked, onMediaClick,
                                            onUserClicked, reportPost,
                                            editPost,
                                            deletePost,
                                            FirebaseAnalytics.getInstance(contract.returnContext()))
            }
        }
    }

    override fun getItemCount(): Int {
        var count = timeline.posts.size
        if(hasValue(timeline.nextTalks.size)){
            count++
        }
        return count
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        currentItem = position
        val alignedPositon = getAlignedPosition(position)

        when (holder) {
            is PostViewHolder -> holder.bind(timeline.posts[alignedPositon])

            is PostNextTalksViewHolder -> {
                holder.bind(timeline.nextTalks)
            }
            is PostCarousselViewHolder -> {
                holder.bind(ArrayList<String>())
            }
        }
    }

    fun getPostAt(position: Int): PostDTO {
        val post: PostDTO
        val alignedPositon = getAlignedPosition(position)
        post = timeline.posts[alignedPositon]

        return post
    }

    override fun getItemId(position: Int): Long {
        val aligned = getAlignedPosition(position)

        return aligned.toLong()
    }

    private fun getAlignedPosition(position: Int): Int {
        var alignedPositon = position

        if (hasValue(timeline.nextTalks.size)){
            alignedPositon--
        }

        return alignedPositon
    }

    override fun getItemViewType(position: Int): Int {
        val hasPinned = timeline.posts.any { it.postType == PostType.PINNED.id }

        if(hasPinned) {
            if(position == 1 && timeline.nextTalks.any()){
                return PostType.NEXT_TALKS.id
            }
        }
        else {
            if(position == 0 && timeline.nextTalks.any()){
                return PostType.NEXT_TALKS.id
            }
        }

        return timeline.posts[getAlignedPosition(position)].postType

    }

    fun updateItemAt(postLocal: PostLocal, commentIndexPost: Int) {
        timeline.posts.removeAt(commentIndexPost)
        timeline.posts.add(commentIndexPost, PostDTO(postLocal))
        notifyItemChanged(commentIndexPost)
    }

    fun addItems(newPosts: TimelineDTO) {
        timeline.posts.addAll(newPosts.posts)
        timeline.nextTalks.addAll(newPosts.nextTalks)

        notifyItemRangeInserted(itemCount, newPosts.posts.size)
    }

    fun resetItems(nextPosts: TimelineDTO) {
        timeline.posts.clear()
        timeline.nextTalks.clear()

        timeline.posts.addAll(nextPosts.posts)
        timeline.nextTalks.addAll(nextPosts.nextTalks)

        notifyDataSetChanged()
    }

    fun removeAt(position: Int) {
        timeline.posts.removeAt(position)
        notifyItemRemoved(position)
        notifyItemRangeChanged(position, timeline.posts.size)
    }
}
guisantogui
  • 4,017
  • 9
  • 49
  • 93
  • 2
    This is because your view is recycled, so you need to always set the view value. For example with your Liked view, you need to set the value whether it's liked or not. Something like `view.setLiked(isLiked);` where isLiked is the value. – ישו אוהב אותך Jun 25 '18 at 15:22
  • @ישואוהבאותך Man, you just saved my day! I forgot about this issue when working with recycler view! – guisantogui Jun 25 '18 at 17:10

1 Answers1

0

Using notifyItemChanged() might trigger "fading in and out" effect which is not necessarily desired (unless You use stable IDs or killed change animation in animator).

If You know what was changed in an item, it's better to use an update payload (see an example here) to partially update your ViewHolders without triggering full rebind.

Otherwise if list is relatively small and You don't know what changed, you can also use DiffUtil to help generate list of changes/change payloads "semi-automatically".

Pawel
  • 15,548
  • 3
  • 36
  • 36