[英]Issue using ScrollListener to pull data from Firebase Realtime DB when user scrolls to the bottom of a RecyclerView
I have an application that pulls object data from a Firebase Realtime DB, adds it to an ArrayList, which is then added to a RecyclerView. 我有一个应用程序从Firebase实时数据库中提取对象数据,将其添加到ArrayList,然后将其添加到RecyclerView。 I utilize an onScrollListener on my RecyclerView so that when a user scrolls to the bottom of the RecyclerView, a function is called to pull more data from Firebase, add it to the ArrayList and notify the RecyclerViewAdapter of the Data set change.
我在RecyclerView上使用了onScrollListener,这样当用户滚动到RecyclerView的底部时,会调用一个函数从Firebase中提取更多数据,将其添加到ArrayList并通知RecyclerViewAdapter数据集更改。
When I run my application, it presents the initial pull of data in my RecyclerView, then immediately begins calling the function to fetch more data from my DB continuously until there is no more data left to pull and my RecyclerView is updated with this data; 当我运行我的应用程序时,它会在我的RecyclerView中显示最初的数据提取,然后立即开始调用该函数以持续从我的数据库中获取更多数据,直到没有剩余的数据可以拉动并且我的RecyclerView使用这些数据进行更新; this occurs regardless of whether I scroll(in any direction/of any magnitude) or not.
无论我是否滚动(在任何方向/任何幅度),都会发生这种情况。
Ideally, I would want the application to only pull data if the user scrolled to the bottom of the RecyclerView, and to do so one pull at a time. 理想情况下,如果用户滚动到RecyclerView的底部,我希望应用程序只提取数据,并且一次拉一次。 I'm having a very hard time debugging this issue as the logic seems to be correct;
由于逻辑似乎是正确的,我很难调试这个问题; any help would be immensely appreciated.
任何帮助都会非常感激。
Relevant global variables 相关的全局变量
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<>();
View loaded 查看已加载
@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;
}
First function that gets called. 第一个被调用的函数。 initializes the first pull of data from Firebase
从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);
}
Adding the Scroll Listener to my Recycler View. 将Scroll Listener添加到Recycler View。 Ideally, it is only supposed to fetch more data from Firebase if and only if the user scrolls to the bottom of the RecyclerView
理想情况下,当且仅当用户滚动到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);
}
}
}
});
}
Initializing Adapter with first pull of data 使用第一次数据初始化适配器
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));
}
This is my function to pull more data from Firebase. 这是我从Firebase获取更多数据的功能。 It gets called in my Scroll Listener
它在我的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);
}
}
This is my Logcat of Loading(boolean), that I kept track of throughout the program because I believe it was a main part of the issue. 这是我的加载Logcat(布尔值),我在整个程序中都会跟踪,因为我认为这是问题的主要部分。 I have attached it below:
我在下面附上:
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
In Your firstEventListener() , 在你的firstEventListener()中,
getReference().child("blogs") will actually fetches whole child data , which will be considered as read operation, even if you are not using it. getReference()。child(“blogs”)实际上将获取整个子数据,即使您没有使用它也将被视为读取操作。
So there will be no any data saving in your approach. 因此,您的方法中不会有任何数据保存。
Let me share one of the approach: 让我分享一下这个方法:
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;
}
});
}
Three new variables are important in the above function: mPostsPerPage: It is the Integer constant defined in the MainActivity.java class which specifies how many records will be queried per page. 在上面的函数中有三个新变量很重要:mPostsPerPage:它是MainActivity.java类中定义的Integer常量,它指定每页将查询的记录数。
mIsLoading: It is a boolean defined in the MainActivity that is used to keep track of whether the Firebase Database query is in progress or not. mIsLoading:它是MainActivity中定义的布尔值,用于跟踪Firebase数据库查询是否正在进行中。
Consts.FIREBASE_DATABASE_LOCATION_USERS: It is the constant defined in the Consts.java class where simply the location of the “users” node is stored Consts.FIREBASE_DATABASE_LOCATION_USERS:它是Consts.java类中定义的常量,其中只存储“users”节点的位置
Ref: https://blog.shajeelafzal.com/2017/12/13/firebase-realtime-database-pagination-guide-using-recyclerview/ 参考: https : //blog.shajeelafzal.com/2017/12/13/firebase-realtime-database-pagination-guide-using-recyclerview/
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.