java - 当用户滚动到 RecyclerView 的底部时,使用 ScrollListener 从 Firebase Realtime DB 中提取数据的问题
问题描述
我有一个从 Firebase Realtime DB 中提取对象数据的应用程序,将其添加到 ArrayList,然后将其添加到 RecyclerView。我在我的 RecyclerView 上使用了一个 onScrollListener,这样当用户滚动到 RecyclerView 的底部时,会调用一个函数从 Firebase 中提取更多数据,将其添加到 ArrayList 并通知 RecyclerViewAdapter 数据集更改。
当我运行我的应用程序时,它会在我的 RecyclerView 中显示最初的数据提取,然后立即开始调用该函数以连续从我的数据库中获取更多数据,直到没有更多数据可以提取并且我的 RecyclerView 使用这些数据进行更新;无论我是否滚动(在任何方向/任何幅度),都会发生这种情况。
理想情况下,我希望应用程序仅在用户滚动到 RecyclerView 底部时才拉取数据,并且一次拉取一次。我很难调试这个问题,因为逻辑似乎是正确的;任何帮助将不胜感激。
相关的全局变量
RecyclerView rvVertical;
RecyclerViewAdapter rvaVertical;
long mTotalChildren = 0;
int mLastKey = 0;
ChildEventListener childEventListenerMain, childEventListenerPager;
boolean Loading = false;
private ArrayList<BlogObject> blogArray = new ArrayList<>();
private ArrayList<BlogObject> tempList = new ArrayList<>();
查看已加载
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.socialmediatab, container, false);
rvVertical = (RecyclerView) view.findViewById(R.id.bloglistRecyclerView);
Loading = true;
Log.d(TAG, "onCreateView - Loading = " + Loading);
firstEventListener();
regAdapterVertical();
addOnScrollListener();
return view;
}
第一个被调用的函数。初始化从 Firebase 提取的第一个数据
private void firstEventListener() {
FirebaseDatabase.getInstance().getReference().child("blogs").addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
mTotalChildren = dataSnapshot.getChildrenCount();
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) { }
});
final Query BlogQuery = FirebaseDatabase.getInstance().getReference().child("blogs").orderByChild("createdAt").limitToLast(3);
childEventListenerMain = new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot ds, String s) {
Loading = true;
Log.d(TAG, "First get blogs instance - Loading = " + Loading);
BlogObject addblog = ds.getValue(BlogObject.class);
if (addblog != null) {
BlogObject addBlog = new BlogObject(ds.getValue(BlogObject.class).getImage(), ds.getValue(BlogObject.class).getTitle(), ds.getValue(BlogObject.class).getTimeStamp(), ds.getValue(BlogObject.class).getCreatedAt());
blogArray.add(addBlog);
rvaVertical.notifyDataSetChanged();
mLastKey = blogArray.get(0).getCreatedAt();
rvVertical.scrollToPosition(blogArray.size() - 1);
}
}
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) { }
@Override
public void onChildRemoved(DataSnapshot dataSnapshot) { }
@Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) { }
@Override
public void onCancelled(DatabaseError databaseError) { }
};
BlogQuery.addChildEventListener(childEventListenerMain);
ValueEventListener BlogChildSingleListener = new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
Loading = false;
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) { }
};
BlogQuery.addListenerForSingleValueEvent(BlogChildSingleListener);
}
将 Scroll Listener 添加到我的 Recycler 视图中。理想情况下,仅当且仅当用户滚动到 RecyclerView 的底部时,它才应该从 Firebase 获取更多数据
private void addOnScrollListener() {
rvVertical.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
Log.d(TAG, "Just Entered onScrolled and Loading = " + Loading);
super.onScrolled(recyclerView, dx, dy);
if(!rvVertical.canScrollVertically(1)) {
LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
if (blogArray.size() >= 3 && !Loading && linearLayoutManager.findLastCompletelyVisibleItemPosition() == blogArray.size() - 1 && blogArray.size() < mTotalChildren) {
Loading = true;
getMoreBlogs();
Log.d(TAG, "Right After getMoreBlogs gets called. Loading = " + Loading);
}
}
}
});
}
使用第一次数据拉取初始化适配器
private void regAdapterVertical() {
LinearSnapHelper linearSnapHelper = new SnapHelperOneByOne();
linearSnapHelper.attachToRecyclerView(rvVertical);
rvaVertical = new RecyclerViewAdapter(blogArray);
rvVertical.setAdapter(rvaVertical);
rvVertical.setLayoutManager((RecyclerView.LayoutManager) new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false));
}
这是我从 Firebase 中提取更多数据的功能。它在我的 Scroll Listener 中被调用
public void getMoreBlogs() {
tempList.clear();
Query getMoreQuery = FirebaseDatabase.getInstance().getReference().child("blogs").orderByChild("createdAt").endAt(mLastKey).limitToLast(3);
childEventListenerPager = new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot ds, String s) {
Loading = true;
BlogObject blogObject = ds.getValue(BlogObject.class);
if (blogObject != null) {
BlogObject addBlog = new BlogObject(ds.getValue(BlogObject.class).getImage(), ds.getValue(BlogObject.class).getTitle(), ds.getValue(BlogObject.class).getTimeStamp(), ds.getValue(BlogObject.class).getCreatedAt());
tempList.add(addBlog);
}
}
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) { }
@Override
public void onChildRemoved(DataSnapshot dataSnapshot) { }
@Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) { }
@Override
public void onCancelled(DatabaseError databaseError) { }
};
getMoreQuery.addChildEventListener(childEventListenerPager);
ValueEventListener addtoAdapter = new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
tempList.remove(tempList.size() - 1);
blogArray.addAll(0, tempList);
mLastKey = blogArray.get(0).getCreatedAt();
rvaVertical.notifyDataSetChanged();
Loading = false;
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
};
getMoreQuery.addListenerForSingleValueEvent(addtoAdapter);
}
}
这是我的 Loading(boolean) Logcat,我在整个程序中一直跟踪它,因为我相信它是问题的主要部分。我附在下面:
2019-05-30 10:26:14.590 24930-24970/com.yourgesture.newsocialmediatab I/FirebaseAuth: [FirebaseAuth:] Loading module via FirebaseOptions.
2019-05-30 10:26:14.956 24930-24930/com.yourgesture.newsocialmediatab D/Social Media Tab: onCreateView - Loading = true
2019-05-30 10:26:16.399 24930-24930/com.yourgesture.newsocialmediatab D/Social Media Tab: First get blogs instance - Loading = true
2019-05-30 10:26:16.406 24930-24930/com.yourgesture.newsocialmediatab D/Social Media Tab: First get blogs instance - Loading = true
2019-05-30 10:26:16.534 24930-24930/com.yourgesture.newsocialmediatab D/Social Media Tab: Just Entered onScrolled and Loading = false
2019-05-30 10:26:16.539 24930-24930/com.yourgesture.newsocialmediatab D/Social Media Tab: Right After getMoreBlogs gets called. Loading = true
2019-05-30 10:26:16.663 24930-24930/com.yourgesture.newsocialmediatab D/Social Media Tab: Just Entered onScrolled and Loading = false
2019-05-30 10:26:16.666 24930-24930/com.yourgesture.newsocialmediatab D/Social Media Tab: Right After getMoreBlogs gets called. Loading = true
2019-05-30 10:26:16.826 24930-24930/com.yourgesture.newsocialmediatab D/Social Media Tab: Just Entered onScrolled and Loading = false
2019-05-30 10:26:16.828 24930-24930/com.yourgesture.newsocialmediatab D/Social Media Tab: Right After getMoreBlogs gets called. Loading = true
2019-05-30 10:26:16.976 24930-24930/com.yourgesture.newsocialmediatab D/Social Media Tab: Just Entered onScrolled and Loading = false
2019-05-30 10:26:16.977 24930-24930/com.yourgesture.newsocialmediatab D/Social Media Tab: Right After getMoreBlogs gets called. Loading = true
2019-05-30 10:26:17.229 24930-24930/com.yourgesture.newsocialmediatab D/Social Media Tab: Just Entered onScrolled and Loading = false
解决方案
在您的 firstEventListener() 中,
getReference().child("blogs") 实际上会获取整个子数据,这将被视为读取操作,即使您不使用它也是如此。
因此,您的方法不会保存任何数据。
让我分享一种方法:
private void getUsers(String nodeId) {
Query query;
if (nodeId == null)
query = FirebaseDatabase.getInstance().getReference()
.child(Consts.FIREBASE_DATABASE_LOCATION_USERS)
.orderByKey()
.limitToFirst(mPostsPerPage);
else
query = FirebaseDatabase.getInstance().getReference()
.child(Consts.FIREBASE_DATABASE_LOCATION_USERS)
.orderByKey()
.startAt(nodeId)
.limitToFirst(mPostsPerPage);
query.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
UserModel user;
List<UserModel> userModels = new ArrayList<>();
for (DataSnapshot userSnapshot : dataSnapshot.getChildren()) {
userModels.add(userSnapshot.getValue(UserModel.class));
}
mAdapter.addAll(userModels);
mIsLoading = false;
}
@Override
public void onCancelled(DatabaseError databaseError) {
mIsLoading = false;
}
});
}
三个新变量在上述函数中很重要: mPostsPerPage:它是 MainActivity.java 类中定义的整数常量,它指定每页将查询多少条记录。
mIsLoading:它是在 MainActivity 中定义的布尔值,用于跟踪 Firebase 数据库查询是否正在进行。
Consts.FIREBASE_DATABASE_LOCATION_USERS:它是在 Consts.java 类中定义的常量,其中仅存储“用户”节点的位置
推荐阅读
- python - 使用 Python randomQuizGenerator 错误自动化无聊的事情
- tensorflow - Exactly match 2 rows in a tensor Tensorflow
- r - 将 setdiff 结果存储在数据框单元格中
- java - 为什么我的对象没有被添加到我的数组中?
- c++ - 同时运行两个函数。(即时的)
- .net-core - .NET Core 键控依赖注入
- python - 如何直接通过方法而不是通过实例为变量赋值
- rust - 使用线程和异步/等待时如何解决“无法返回引用本地数据的值”?
- android - 无法捕获 Exec 类型的 Gradle 任务的异常
- angular - 给出错误结果的时刻日期格式