I have a recyclerview which initially fetching last 10 posts. I want a process where when the recyclerview reaches bottom after scrolling it again fetches next 10 posts and so on. I have used gridlayoutmanager for the recyclerview.
My code is:
Query query = mDatabase.orderByChild("UserId").equalTo(userId).limitToLast(10);
FirebaseRecyclerAdapter<HomeBlog, BlogViewHolder> firebaseRecyclerAdapter = new FirebaseRecyclerAdapter<HomeBlog, BlogViewHolder>(
HomeBlog.class,
R.layout.profile_row,
BlogViewHolder.class,
query
)
{
#Override
protected void populateViewHolder(BlogViewHolder viewHolder, HomeBlog model, int position) {
final String post_key = getRef(position).getKey();
//final String postPosition = String.valueOf(position);
viewHolder.setImage(post_key);
}
};
mBlogList.setAdapter(firebaseRecyclerAdapter);
//mBloglist is the recyclerview
Now I may put the above code in a method and call it from onStart in the beginning and then call it everytime (with some modification) when the recyclerview reaches bottom (with showing a progressbar perhaps)
I guess at first I need some scrolllistener to detect when recyclerview reaches bottom, and then when it does, fetch more data. But I cant seem to do it. Any help will be appreciated.
Related
I'm using firebase-ui to retrieve from firebase and I want to remove from displaying all the status that are equal to unlive I don't want to delete it from firebase database but only removed it from displaying on the recyclerview tried also doing the solution Cannot call this method while RecyclerView is computing a layout or scrolling when try remove item from recyclerview here still encountering the error. Please help I'm stuck here for days. Can't find any solution. Thanks
mAdapter = new FirebaseRecyclerAdapter<Hotels_Model, Adapter_HotelsHolder>(mOptions) {
#Override
protected void onBindViewHolder(Adapter_HotelsHolder holder,int position, Hotels_Model model) {
String status = model.getStatus();
if (status.equals("unlive")) {
mAdapter.notifyItemRemoved(holder.getAdapterPosition());
mAdapter.notifyItemRangeChanged(holder.getAdapterPosition(), getItemCount());
}
}
This is my error if I scroll to the position where the status is unlive
Cannot call this method while RecyclerView is computing a layout or scrolling android.support.v7.widget.RecyclerView
So you can do it easily in that way:
mAdapter = new FirebaseRecyclerAdapter<Hotels_Model, Adapter_HotelsHolder>(mOptions) {
#Override
protected void onBindViewHolder(Adapter_HotelsHolder holder,int position, Hotels_Model model) {
String status = model.getStatus();
if (status.equals("unlive")) {
holder.yourRowView.setVisibility(View.GONE);
}
}
You don't need to remove your items from recyclerview.
I have a problem using FirebaseRecyclerAdapater, at first it was working fine but now this adapter is firing twice. The database reference is only referring one child, but it is always firing twice. The Toast with text "counter" will appear twice
FirebaseRecyclerAdapter<RequestVisit, RequestViewHolder> requestAdapter =
new FirebaseRecyclerAdapter<RequestVisit, RequestViewHolder>(
RequestVisit.class,
R.layout.seekerrequests_layout,
RequestViewHolder.class,
requestDatabase.child("2DBwmhGplGMoAlLy6337HZEShi93")
) {
#Override
protected void populateViewHolder(final RequestViewHolder viewHolder, RequestVisit model, int position) {
Toast.makeText(getContext(), "counter" +
viewHolder.getAdapterPosition(), Toast.LENGTH_SHORT).show();
}
};
requestVisitList.setAdapter(requestAdapter);
A Firebase*Adapter shows a list of items, the child nodes under the location that you attach it to.
If populateViewHolder gets called with two different positions, that means there are two children under requestDatabase.child("2DBwmhGplGMoAlLy6337HZEShi93").
Keep in mind that if 2DBwmhGplGMoAlLy6337HZEShi93 is a child node with two properties, then your approach will call populateViewHolder for each of those properties.
If you want to show only a single item in the RecyclerView, you can create a simple query with:
FirebaseRecyclerAdapter<RequestVisit, RequestViewHolder> requestAdapter =
new FirebaseRecyclerAdapter<RequestVisit, RequestViewHolder>(
RequestVisit.class,
R.layout.seekerrequests_layout,
RequestViewHolder.class,
requestDatabase.orderByKey().equalTo("2DBwmhGplGMoAlLy6337HZEShi93")
)
In my Activity, I use a RecyclerView that uses a FirebaseRecyclerAdapter to draw a list:
mChatDetailsAdapter = new ChatDetailsAdapter(
this,
MessageData.class,
R.layout.layout_chat_item,
ChatDetailsAdapter.MessageViewHolder.class,
query);
mChatListView.setHasFixedSize(true);
LinearLayoutManager lLinearLayoutManager = new LinearLayoutManager(ChatDetailsActivity.this, LinearLayoutManager.VERTICAL, false);
lLinearLayoutManager.setStackFromEnd(true);
lLinearLayoutManager.setReverseLayout(true);
mChatListView.setLayoutManager(lLinearLayoutManager);
mChatListView.addItemDecoration(new VerticalSpaceItemDecoration(UIUtils.dpToPx(ChatDetailsActivity.this, 8)));
mChatListView.setAdapter(mChatDetailsAdapter);
The Adapter takes care about the views:
#Override
protected void populateViewHolder(final MessageViewHolder pViewHolder, final MessageData pMessageData, int pPosition) {
pViewHolder.mMessageTextView.setText(pMessageData.getMessage());
pViewHolder.mSelfLinearLayout.setVisibility(View.VISIBLE);
pViewHolder.mTimeTExtView.setText(StringUtils.toMessageTimeString(mContext, pMessageData.getTime()));
}
In the onBindViewHolder() method I basically do the same, be assigning the data from the model (works correctly after looking at the log messages). Now when scrolling the list the RecyclerView changes its values and doesn't recycle correctly and I have no idea why. Any idea?
I use smoothScrollToPosition to scroll RecyclerView. It does scroll every time a new entry is inserted; but to the top, not to the bottom, which is the direction i want.
list_chat = (RecyclerView) findViewById(R.id.list_chat);
//Set up Layout Manager
linearLayoutManager = new LinearLayoutManager(this);
linearLayoutManager.setStackFromEnd(true);
list_chat.setLayoutManager(linearLayoutManager);
//set adapter
list_chat.setAdapter(adapter);
//set scroll
list_chat.post(new Runnable() {
#Override
public void run() {
list_chat.smoothScrollToPosition(adapter.getItemCount());
}
});
The adapter is from Firebase
adapter = new FirebaseRecyclerAdapter<ChatItem, ChatRecylerViewHolder>(ChatItem.class,R.layout.chat_item
,ChatRecylerViewHolder.class,queryChat ) {
#Override
protected void populateViewHolder(ChatRecylerViewHolder viewHolder, ChatItem model, int position) {
viewHolder.tvAuthorChat.setText(model.chatAuthor);
viewHolder.tvContentChat.setText(model.chatContent);
}
};
You do notice you are using linearLayoutManager.setStackFromEnd(true); this mean you first position is at the bottom. I suggest you a better option.
RecycleView didn't work the way listView work, you can scroll it with your layout manager something like this
linearLayoutManager.scrollToPositionWithOffset(position,offset);
Which position is the position you want to scroll to, offset is the offset within the current position. You could just use with one parameter as well.
linearLayoutManager.scrollToPosition(position);
Ok. I found the answer.
First, the old problem with my question: i thought list_chat.post is called whenever an item is inserted (turn out that is wrong). The reason for it keeps scrolling top is linearLayoutManager.setStackFromEnd(true);
Thus, the question comes down to Where to call the scrolling ?
The answer is : Since adapter manages data, it makes sense to guess that adapter will notify the insertion.
Here is the code
adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
#Override
public void onItemRangeInserted(int positionStart, int itemCount) {
super.onItemRangeInserted(positionStart, itemCount);
list_chat.smoothScrollToPosition(adapter.getItemCount());
}
});
I know there are lot of ways to have an empty view for a RecyclerView. But my question is for FirebaseRecyclerView.
My layout is:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="#+id/feed_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical" />
<ProgressBar
android:id="#+id/feed_loading"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:visibility="gone"/>
</RelativeLayout>
So I am showing Loading ProgressBar before the RecyclerView fills its items. Now what if server doesn't have any item. In this situation my RecyclerView is empty always and my Loading ProgressBar always visible to user.
So instead of showing the ProgressBar for indefinite period, I want to show some empty layout. E.g. "No Data Found" or something similar message.
The final result should be: ProgressBar should be shown until data is loaded to RecyclerView and once data is loaded ProgressBar should be invisible. But if no data is present in server, some empty layout should be shown instead of ProgressBar.
In normal RecyclerView, we have a dataset (some ArrayList, etc) and if it is empty then we can show that empty layout. But in case of FirebaseRecyclerAdapter, I dont have the reference of Snapshot in my Activity or Context. Nor I have any callback which tells me that no data is present in server.
Any workaround will help a lot.
Here is what I would try. First check out the accepted answer to the question linked below. It provides some very good insight into how Firebase queries work. I'd consider the info trusted since the answer is by someone on the Firebase team:
How to separate initial data load from incremental children with Firebase?
So, based on the answer to the question linked above and the fact that the FirebaseRecyclerAdapter is backed by a FirebaseArray which is populated using a ChildEventListener I would add a Single value event listener on the same database reference used to populate your FirebaseRecyclerAdapter. Something like this:
//create database reference that will be used for both the
//FirebaseRecyclerAdapter and the single value event listener
dbRef = FirebaseDatabase.getInstance().getReference();
//setup FirebaseRecyclerAdapter
mAdapter = new FirebaseRecyclerAdapter<Model, YourViewHolder>(
Model.class, R.layout.your_layout, YourViewHolder.class, dbRef) {
#Override
public void populateViewHolder(YourViewHolder holder, Model model, int position){
//your code for populating each recycler view item
};
mRecyclerView.setAdapter(mAdapter);
//add the listener for the single value event that will function
//like a completion listener for initial data load of the FirebaseRecyclerAdapter
dbRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//onDataChange called so remove progress bar
//make a call to dataSnapshot.hasChildren() and based
//on returned value show/hide empty view
//use helper method to add an Observer to RecyclerView
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
That would handle the initial setup of the RecyclerView. When onDataChange is called on the single value event listener use a helper method to add an observer to the FirebaseRecyclerAdapter to handle any subsequent additions/deletions to database location.
mObserver = new RecyclerView.AdapterDataObserver() {
#Override
public void onItemRangeInserted(int positionStart, int itemCount) {
//perform check and show/hide empty view
}
#Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
//perform check and show/hide empty view
}
};
mAdapter.registerAdapterDataObserver(mObserver);
Answer from: https://stackoverflow.com/a/40204298/2170278
the FirebaseUI adapters nowadays have an onDataChanged() method that you can override to detect when they're done loading a set of data.
See the source code on github. From there:
This method will be triggered each time updates from the database have been completely processed. So the first time this method is called, the initial data has been loaded - including the case when no data at all is available. Each next time the method is called, a complete update (potentially consisting of updates to multiple child items) has been completed.
You would typically override this method to hide a loading indicator (after the initial load) or to complete a batch update to a UI element.
The FirebaseUI sample app overrides onDataChanged() to hide its "loading" indicator:
public void onDataChanged() {
// If there are no chat messages, show a view that invites the user to add a message.
mEmptyListMessage.setVisibility(getItemCount() == 0 ? View.VISIBLE : View.GONE);
}