我正在研究带有照片和相册的示例社交媒体应用程序。使用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
的解决方法或其他方法可以解决此问题?
答案 0 :(得分:3)
对于遇到相同问题的人:
问题在于,由于getItemCount被覆盖且未更新,因此数据列表变大时,数据适配器的位置大于数据大小。因此,它无法通过RecyclerView.Recycle.validateViewHolderForOffsetPosition
方法来满足此条件。
解决方案是使用not override
列表适配器的getItemCount
方法,并让Recycler View处理数据大小。之后,该应用程序可以完美运行。