将数据加载到LiveData观察回收者视图中:检测到不一致。无效的视图支架适配器positionViewHolder

时间:2018-07-10 22:47:31

标签: android android-recyclerview android-livedata

我正在研究带有照片和相册的示例社交媒体应用程序。使用Android建筑组件Room,LiveData和ViewModel进行翻新。 Room周围有一个存储层,用于处理数据操作。

  

RecyclerView-> ViewModel->存储库->会议室<-改造

@Entity(tableName = "photos",
        foreignKeys = {@ForeignKey(entity = AlbumModel.class, parentColumns = "id", childColumns = "album_id")},
        indices = {@Index(name = "index_album", value = "album_id")})
public class PhotoModel {

    @PrimaryKey
    private int id;

    @ColumnInfo(name = "album_id")
    private int albumId;

    private String title;
    private String url;
    private String thumbnail_url;
}

ViewModel
    public class PhotoViewModel extends AndroidViewModel {
        private LiveData<List<PhotoModel>> photos;
        private PhotoRepository photoRepository;

        public LiveData<List<PhotoModel>> getAlbumPhotos(int album_id) {
            if(photos == null) {
                photos = photoRepository.getAlbumPhotos(album_id);
            }
            return photos;
        }

    }

PhotoRepository
public class PhotoRepository implements PhotosInterface {
    private LiveData<List<PhotoModel>> photos;
    private PhotoDao photoDao;
    private AlbumRepository albumRepository;

    public LiveData<List<PhotoModel>> getAlbumPhotos(int album_id) {
        PhotoController.getAlbumPhotos(this, album_id);
        if (photos == null) {
            photos = photoDao.getAlbumPhotos(album_id);
        }
        return photos;
    }
}

@Dao
public abstract class PhotoDao {

    @Query("SELECT * FROM photos WHERE album_id=:album_id ORDER BY id DESC")
    public abstract LiveData<List<PhotoModel>> getAlbumPhotos(int album_id);
}

这是问题所在: 从服务器获取照片后,由于album_id是照片模型中的外键,因此它将检查相册。这发生在AsycTask中。因此,未按顺序插入照片,从而导致listadapter更新列表和UI,从而导致以下错误。 这是基于相同体系结构的应用程序中其他Recycler View的相同问题。

//Inserting photos through Photos Dao in Retro callback
    private  void InsertPhotoWithAlbum(PhotoDao photoDao, AlbumRepository albumRepository, PhotoModel photo) {
            if(photoDao.getPhotoObjectById(photo.getId()) == null ) {
                if(albumRepository.getAlbumObjectById(photo.getAlbumId()) != null) {
                    photoDao.insertPhoto(photo);
                } else {
                    albumRepository.fetchAlbum(photo.getAlbumId());
                    photoDao.insertPhoto(photo);
                }
            }
        }

现在,相册中包含照片,并使用StaggeredGridLayoutManager在RecyclerView中列出。下面是RecyclerView的ListAdapter。

public class CustomListAdapter extends ListAdapter<PhotoModel, CustomListAdapter.PhotoViewHolder> implements AlbumDetailActivity.RecyclerViewOnClickListener{
        private List<PhotoModel> photos;
        private Context mContext;

        public CustomListAdapter(@NonNull DiffUtil.ItemCallback<PhotoModel> diffCallback, Context context) {
            super(diffCallback);
            mContext = context;
            PhotoViewModel photoViewModel = ViewModelProviders.of(AlbumDetailActivity.this).get(PhotoViewModel.class);
            photoViewModel.getAlbumPhotos(album_id).observe(AlbumDetailActivity.this, new Observer<List<PhotoModel>>() {
                @Override
                public void onChanged(@Nullable List<PhotoModel> photoModels) {
                    submitList(photoModels);
                    photos = photoModels;
                }
            });
//            photos = photoViewModel.getAlbumPhotosObjects(album_id);
        }

        @NonNull
        @Override
        public PhotoViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            View itemView = LayoutInflater.from(mContext).inflate(R.layout.listitem_photos_list, null);
            PhotoViewHolder viewHolder = new PhotoViewHolder(itemView);
            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {

                }
            });
            return viewHolder;
        }

        @Override
        public void onBindViewHolder(@NonNull PhotoViewHolder holder, int position) {
            PhotoModel photo = photos.get(position);
            holder.tv_title.setText(photo.getTitle());
            Picasso.get().load(photo.getThumbnail_url()).into(holder.iv_photo_thumbnail);
        }

        public class PhotoViewHolder extends RecyclerView.ViewHolder {
            TextView tv_title;
            ImageView iv_photo_thumbnail;

            public PhotoViewHolder(View itemView) {
                super(itemView);
                tv_title = itemView.findViewById(R.id.tv_photo_title);
                iv_photo_thumbnail = itemView.findViewById(R.id.iv_photo_thumbnail);
            }

        }

        @Override
        public int getItemCount() {
            return photos == null ? 0 : photos.size();
        }

        @Override
        public void onItemClick(PhotoModel photo) {
            Toast.makeText(mContext, "Clicked on : " + photo.getId() + ":" + photo.getTitle() , Toast.LENGTH_SHORT).show();
        }
    }

加载照片时,应用程序崩溃并出现以下错误。

E/AndroidRuntime: FATAL EXCEPTION: main
                  Process: assess.talview.com.yalview_yasma, PID: 14830
                  java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder{b4cfec1 position=42 id=-1, oldPos=39, pLpos:39 scrap [attachedScrap] tmpDetached not recyclable(1) no parent} android.support.v7.widget.RecyclerView{a26494c VFED..... .F....ID 0,0-1080,1731 #7f08007b app:id/recyclerview_photos}, adapter:assess.talview.com.yalview_yasma.album.AlbumDetailActivity$CustomListAdapter@6a61dfa, layout:android.support.v7.widget.StaggeredGridLayoutManager@b391fab, context:assess.talview.com.yalview_yasma.album.AlbumDetailActivity@520aa8f
                      at android.support.v7.widget.RecyclerView$Recycler.validateViewHolderForOffsetPosition(RecyclerView.java:5610)
                      at android.support.v7.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:5792)
                      at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5752)
                      at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5748)
                      at android.support.v7.widget.LayoutState.next(LayoutState.java:100)
                      at android.support.v7.widget.StaggeredGridLayoutManager.fill(StaggeredGridLayoutManager.java:1611)
                      at android.support.v7.widget.StaggeredGridLayoutManager.onLayoutChildren(StaggeredGridLayoutManager.java:685)
                      at android.support.v7.widget.StaggeredGridLayoutManager.onLayoutChildren(StaggeredGridLayoutManager.java:607)
                      at android.support.v7.widget.RecyclerView.dispatchLayoutStep1(RecyclerView.java:3763)
                      at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:3527)
                      at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:4082)
                      at android.view.View.layout(View.java:19659)
                      at android.view.ViewGroup.layout(ViewGroup.java:6075)
                      at android.support.design.widget.CoordinatorLayout.layoutChild(CoordinatorLayout.java:1191)
                      at android.support.design.widget.CoordinatorLayout.onLayoutChild(CoordinatorLayout.java:876)
                      at android.support.design.widget.CoordinatorLayout.onLayout(CoordinatorLayout.java:895)
                      at android.view.View.layout(View.java:19659)
                      at android.view.ViewGroup.layout(ViewGroup.java:6075)
                      at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
                      at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
                      at android.view.View.layout(View.java:19659)
                      at android.view.ViewGroup.layout(ViewGroup.java:6075)
                      at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1791)
                      at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1635)
                      at android.widget.LinearLayout.onLayout(LinearLayout.java:1544)
                      at android.view.View.layout(View.java:19659)
                      at android.view.ViewGroup.layout(ViewGroup.java:6075)
                      at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
                      at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
                      at android.view.View.layout(View.java:19659)
                      at android.view.ViewGroup.layout(ViewGroup.java:6075)
                      at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1791)
                      at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1635)
                      at android.widget.LinearLayout.onLayout(LinearLayout.java:1544)
                      at android.view.View.layout(View.java:19659)
                      at android.view.ViewGroup.layout(ViewGroup.java:6075)
                      at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
                      at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
                      at com.android.internal.policy.DecorView.onLayout(DecorView.java:761)
                      at android.view.View.layout(View.java:19659)
                      at android.view.ViewGroup.layout(ViewGroup.java:6075)
                      at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2496)
                      at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2212)
                      at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1392)
                      at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6752)
                      at android.view.Choreographer$CallbackRecord.run(Choreographer.java:911)
                      at android.view.Choreographer.doCallbacks(Choreographer.java:723)
E/AndroidRuntime:     at android.view.Choreographer.doFrame(Choreographer.java:658)
                      at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:897)
                      at android.os.Handler.handleCallback(Handler.java:790)
                      at android.os.Handler.dispatchMessage(Handler.java:99)
                      at android.os.Looper.loop(Looper.java:164)
                      at android.app.ActivityThread.main(ActivityThread.java:6494)
                      at java.lang.reflect.Method.invoke(Native Method)
                      at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
                      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)

搜索错误提示submitting是新列表的副本,而不是没有影响的现有列表。

如何解决这个问题?有没有LiveData的解决方法或其他方法可以解决此问题?

1 个答案:

答案 0 :(得分:3)

对于遇到相同问题的人:

问题在于,由于getItemCount被覆盖且未更新,因此数据列表变大时,数据适配器的位置大于数据大小。因此,它无法通过RecyclerView.Recycle.validateViewHolderForOffsetPosition方法来满足此条件。

解决方案是使用not override列表适配器的getItemCount方法,并让Recycler View处理数据大小。之后,该应用程序可以完美运行。