我想在RecyclerView列表中添加新项目,并始终将其放在列表的最顶部。但遗憾的是,更新列表时出现奇怪的行为我使用getAdapterPosition()来监控索引/位置是否正在更新。
默认列表:0,1,2,3,4是我的第一个列表
My Pagination规则是每次用户在最底部滚动时都会添加5个项目。
现在我想添加新项目并将其放在列表顶部而不使用太多内存,这就是我根本不想使用notifyDataSetChanged()的原因。 但是在添加新项目之后,这就是结果。
添加新项目时:"这是新项目" > 0,0,1,2,3,4 getAdapterPosition()没有得到更新,而是重复索引,因此每次添加新项目时都会创建两个或更多的零索引?
我对notifyItemRangeInserted()的理解是,当添加新项目时,它将更新列表中的其余项目,从我们传递的第一个参数开始,即" positionStart"并在该positionStart之后立即更新项目的下一个/其余部分。
这是我第一次使用Firestore查询将获得onCreate方法的前5项。
//Load the first item(s) to display
//Set a query according to time in milliseconds
mQuery = mDatabase.collection("Announcements")
.orderBy("time", Query.Direction.DESCENDING)
.limit(5);
//Getting all documents under Announcement collection with query's condition
annon_listener = mQuery.addSnapshotListener(new EventListener<QuerySnapshot>() {
@Override
public void onEvent(final QuerySnapshot documentSnapshots, FirebaseFirestoreException e) {
//If something went wrong
if (e != null)
Log.w(TAG, "Listen failed.", e);
//If any post exist put it to model and add it to List to populate the CardView
//If data exist in the first 5 items then item should be loaded making our 'isFirstListLoaded' variable to be true
if (!documentSnapshots.isEmpty()){
//If first item are loaded then every update post should be on the top not at the bottom
//This can only be called once to avoid confusion/duplication getting new item
if (isFirstListLoaded){
//Get the documents of last item listed in our RecyclerView
mLastSeen = documentSnapshots.getDocuments().get(documentSnapshots.size()-1);
//Clear the list first to get a latest data
announcementList.clear();
}
//Loop to read each document
for (DocumentChange doc : documentSnapshots.getDocumentChanges()){
//Only added document will be read
switch (doc.getType()){
case ADDED:
//Call the model to populate it with document
AnnouncementModel annonPost = doc.getDocument().toObject(AnnouncementModel.class)
.withId(doc.getDocument().getId());
//To retrieve all post data in the same time we need to place this if else over here
//So user data and his/her post will be retrieve at the same time
//This can only be called once to avoid confusion getting new item(s)
if (isFirstListLoaded){
announcementList.add(annonPost);
announcementRecyclerAdapter.notifyDataSetChanged();
}
if (isJustDelete)
isJustDelete = false;
//If someone just remove a post then do nothing and return the state to false
//This will be called once a user added new item to database and put it to top of the list
else if (!isFirstListLoaded && !isJustDelete){
//Before adding new item to the list lets save the previous size of the list as a reference
int prevSize = announcementList.size();
//This will be called only if user added some new post
announcementList.add(0, annonPost);
//Update the Recycler adapter that new data is added
announcementRecyclerAdapter.notifyItemRangeInserted(0, announcementList.size() - prevSize);
}
//Just checking of where's the data fetched from
String source = documentSnapshots.getMetadata().isFromCache() ?
"Local" : "Server";
Log.d(TAG, "Data fetched from " + source + "\n" + doc.getDocument().getData());
break;
}
}
//After the first item/latest post was loaded set it to
false it means that first items are already fetched
isFirstListLoaded = false;
}
//If no post exist then display no content TextView
else if (announcementList.isEmpty()){
noContent.setVisibility(View.VISIBLE);
annonRecyclerView.setVisibility(View.GONE);
}
当用户向下滚动并到达底部时,将调用此方法以获取数据库中可用的下五个项目。
//Load more queries
private void loadMoreList(){
//Load the next item(s) to display
//Set a query according to time in milliseconds
//This time start getting data AFTER the last item(s) loaded
mQuery = mDatabase.collection("Announcements")
.orderBy("time", Query.Direction.DESCENDING)
.startAfter(mLastSeen)
.limit(5);
//Getting all documents under Announcement collection with query's condition
annon_listener = mQuery.addSnapshotListener(new EventListener<QuerySnapshot>() {
@Override
public void onEvent(final QuerySnapshot documentSnapshots, FirebaseFirestoreException e) {
//If something went wrong
if (e != null)
Log.w(TAG, "Listen failed.", e);
//If no more item(s) to load
if (documentSnapshots.isEmpty()){
isFullyLoaded = true;
messenger = Snackbar.make(mPullToRefreshView,"No more item(s) to load.",Snackbar.LENGTH_LONG)
.setActionTextColor(Color.WHITE)
.setAction("Dismiss", new View.OnClickListener() {
@Override
public void onClick(View v) {
messenger.dismiss();
}
});
messenger.show();
}
else{
//If more data exist then update our 'mLastSeen' data
//Update the last list shown in our RecyclerView
mLastSeen = documentSnapshots.getDocuments().get(documentSnapshots.size()-1);
//Loop to read each document
for (DocumentChange doc : documentSnapshots.getDocumentChanges()){
//Only added document will be read
switch (doc.getType()){
case ADDED:
//Call the model to repopulate it with document
AnnouncementModel annonPost = doc.getDocument().toObject(AnnouncementModel.class)
.withId(doc.getDocument().getId());
//This if condition is use to avoid rumbling the item position when deleting some item
if (isJustDelete)
isJustDelete = false;
else if (!isJustDelete && !isFullyLoaded){
int prevSize = announcementList.size();
//Add any new item(s) to the List
announcementList.add(announcementList.size(), annonPost);
//Update the Recycler adapter that new data is added
//This trick performs recycling even though we set nested scroll to false
announcementRecyclerAdapter.notifyItemRangeInserted(announcementList.size(), announcementList.size() - prevSize);
}
//Just checking of where's the data fetched from
String source = documentSnapshots.getMetadata().isFromCache() ?
"Local" : "Server";
Log.d(TAG, "Data fetched from " + source + "\n" + doc.getDocument().getData());
break;
case REMOVED:
break;
case MODIFIED:
break;
}
}
}
}
});
如何在不使用notifyDataSetChange()的情况下解决这个问题,或者甚至可以解决这个问题? 另一件事是当我使用notifyItemRangeInserted(0,list.size());它抛出一个错误: IndexOutOfBoundsException:检测到不一致。
答案 0 :(得分:1)
我发现使用notifyItemRangeChange(startPosition,list.size())是更新适配器位置的正确方法,而不使用notifyDataSetChanged()。它很有用,因为它只会更新从给定的startPosition到最后一个项目的项目,而不是刷新整个列表。