[android] ListAdapter가 RecyclerView에서 항목을 업데이트하지 않음

새 지원 라이브러리를 사용하고 있습니다 ListAdapter. 다음은 어댑터의 코드입니다.

class ArtistsAdapter : ListAdapter<Artist, ArtistsAdapter.ViewHolder>(ArtistsDiff()) {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        return ViewHolder(parent.inflate(R.layout.item_artist))
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bind(getItem(position))
    }

    class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        fun bind(artist: Artist) {
            itemView.artistDetails.text = artist.artistAlbums
                    .plus(" Albums")
                    .plus(" \u2022 ")
                    .plus(artist.artistTracks)
                    .plus(" Tracks")
            itemView.artistName.text = artist.artistCover
            itemView.artistCoverImage.loadURL(artist.artistCover)
        }
    }
}

어댑터를 업데이트하고 있습니다.

musicViewModel.getAllArtists().observe(this, Observer {
            it?.let {
                artistAdapter.submitList(it)
            }
        })

내 diff 클래스

class ArtistsDiff : DiffUtil.ItemCallback<Artist>() {
    override fun areItemsTheSame(oldItem: Artist?, newItem: Artist?): Boolean {
        return oldItem?.artistId == newItem?.artistId
    }

    override fun areContentsTheSame(oldItem: Artist?, newItem: Artist?): Boolean {
        return oldItem == newItem
    }
}

어댑터가 모든 항목을 처음 렌더링 할 때 submitList가 호출 될 때 발생하는 일이 발생하지만, submitList가 업데이트 된 객체 속성으로 다시 호출 될 때 변경된 뷰를 다시 렌더링하지 않습니다.

목록을 스크롤하면 뷰가 다시 렌더링됩니다. bindView()

또한 adapter.notifyDatasSetChanged()제출 목록 후 호출 하면 업데이트 된 값으로 뷰가 렌더링되지만 notifyDataSetChanged()목록 어댑터에 diff 유틸리티가 내장되어 있으므로 호출하고 싶지 않습니다 .

누구든지 여기서 나를 도울 수 있습니까?



답변

편집 : 왜 이것이 내 요점이 아닌지 이해합니다. 내 요점은 최소한 경고를 주거나 notifyDataSetChanged()함수를 호출해야한다는 것 입니다. 분명히 submitList(...)이유 때문에 함수를 호출하고 있기 때문입니다. 나는 사람들이 submitList ()가 조용히 호출을 무시한다는 것을 알아낼 때까지 몇 시간 동안 무엇이 잘못되었는지 알아 내려고 노력하고 있다고 확신합니다.

이것은 Google이상한 논리 때문입니다 . 따라서 동일한 목록을 어댑터에 전달하면 DiffUtil.

public void submitList(final List<T> newList) {
    if (newList == mList) {
        // nothing to do
        return;
    }
....
}

ListAdapter동일한 목록에서 변경 사항을 처리 할 수 ​​없다면 이 모든 내용을 이해하지 못합니다 . 목록의 항목을 변경하고 ListAdapter변경 사항을 확인하려면 목록의 전체 복사본을 만들어야하거나 RecyclerView자신의 DiffUtill클래스에 일반을 사용해야 합니다 .


답변

라이브러리는 업데이트 될 때마다 새로운 비동기 목록을 제공하는 Room 또는 다른 ORM을 사용하고 있다고 가정하므로 submitList를 호출하는 것만으로도 작동하며 엉성한 개발자의 경우 동일한 목록이 호출되면 계산을 두 번 수행하지 않습니다.

받아 들여지는 대답은 정확하며 설명은 제공하지만 해결책은 제공하지 않습니다.

이러한 라이브러리를 사용하지 않는 경우 수행 할 수있는 작업은 다음과 같습니다.

submitList(null);
submitList(myList);

또 다른 해결책은 submitList (빠른 깜박임을 유발하지 않음)를 다음과 같이 재정의하는 것입니다.

@Override
public void submitList(final List<Author> list) {
    super.submitList(list != null ? new ArrayList<>(list) : null);
}

또는 Kotlin 코드 :

override fun submitList(list: List<CatItem>?) {
    super.submitList(list?.let { ArrayList(it) })
}

의심스러운 논리이지만 완벽하게 작동합니다. 내가 선호하는 방법은 각 행이 onBind 호출을받지 않기 때문에 두 번째 방법입니다.


답변

Kotlin을 사용하면 목록을 이와 같은 새로운 MutableList 또는 용도에 따라 다른 유형의 목록 으로 변환하면 됩니다.

.observe(this, Observer {
            adapter.submitList(it?.toMutableList())
        })


답변

비슷한 문제가 있었지만 잘못된 렌더링은 setHasFixedSize(true)및 의 조합으로 인해 발생했습니다 android:layout_height="wrap_content". 처음으로 어댑터에 빈 목록이 제공되었으므로 높이가 업데이트되지 않고 0. 어쨌든 이것은 내 문제를 해결했습니다. 다른 사람이 동일한 문제를 가지고있을 수 있으며 어댑터의 문제라고 생각할 것입니다.


답변

사용할 때 문제가 발생하는 경우

recycler_view.setHasFixedSize(true)

https://github.com/thoughtbot/expandable-recycler-view/issues/53#issuecomment-362991531 이 댓글을 확실히 확인해야합니다.

그것은 내 편에서 문제를 해결했습니다.

(여기에 요청 된 댓글의 스크린 샷이 있습니다.)

여기에 이미지 설명 입력


답변

오늘 나는 또한이 “문제”를 발견했다. 의 도움으로 insa_c의 대답RJFares의 솔루션 나 자신에게 코 틀린 확장 기능을했다 :

/**
 * Update the [RecyclerView]'s [ListAdapter] with the provided list of items.
 *
 * Originally, [ListAdapter] will not update the view if the provided list is the same as
 * currently loaded one. This is by design as otherwise the provided DiffUtil.ItemCallback<T>
 * could never work - the [ListAdapter] must have the previous list if items to compare new
 * ones to using provided diff callback.
 * However, it's very convenient to call [ListAdapter.submitList] with the same list and expect
 * the view to be updated. This extension function handles this case by making a copy of the
 * list if the provided list is the same instance as currently loaded one.
 *
 * For more info see 'RJFares' and 'insa_c' answers on
 * /programming/49726385/listadapter-not-updating-item-in-reyclerview
 */
fun <T, VH : RecyclerView.ViewHolder> ListAdapter<T, VH>.updateList(list: List<T>?) {
    // ListAdapter<>.submitList() contains (stripped):
    //  if (newList == mList) {
    //      // nothing to do
    //      return;
    //  }
    this.submitList(if (list == this.currentList) list.toList() else list)
}

그런 다음 어디서나 사용할 수 있습니다. 예 :

viewModel.foundDevices.observe(this, Observer {
    binding.recyclerViewDevices.adapter.updateList(it)
})

현재로드 된 목록과 동일한 경우에만 (그리고 항상) 목록을 복사합니다.


답변

공식 문서 에 따르면 :

submitList호출 할 때마다 비교 및 표시 할 새 목록을 제출합니다 .

이유는 당신이 submitList를 호출 할 때마다이전 (이미 제출 목록) 는 DIFF를 계산하지 않습니다 및 않습니다 어댑터를 통지하지 데이터 집합의 변경.