i created a recylcerview for myChats. In this Recyclerview i can see a thumb, the last message and the name. if i send or receive a new message the item should go to first position, like in whatsapp. to receive the new message i created the following method:
private void getLastMSG(final String userId, final TextView lastMSG){
String userid = FirebaseAuth.getInstance().getCurrentUser().getUid();
DatabaseReference ref = FirebaseDatabase.getInstance().getReference().child("Users").child(userid).child("connections").child("matches").child(userId);
ref.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.exists()){
String lastMsg = dataSnapshot.child("lastMsg").getValue().toString();
lastMSG.setText(lastMsg);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
Now i want to set this item to first position but i dont know how
You need to call:
notifyItemMoved(oldPos, newPos) in your recyclerview adapter.
Note that you need to update your data model before that, in order to bind correct view in onBindViewHolder method.
Referece to adapter documentation.
Related
i want to access on this point by clicking on element inside cardView
i only can reach to the point before it by using this line
reference = FirebaseDatabase.getInstance().getReference("Posts")
.child(FirebaseAuth.getInstance().getCurrentUser().getUid());
String str = reference.getKey();
To get the keys of the child nodes, you'll need to load the reference and then loop over snapshot.getChildren(). So something like:
reference.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
System.out.println(postSnapshot.getKey());
...
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException();
}
}
If you only want to retrieve one specific node, you'll need to know the key of that node. Typically this means that you need to keep the key of each snapshot when you read the data, and associate that with the position of each node in the recycler view. The once the user clicks on an item in the view, you look up the key based on the position they clicked, and can get that item from the database again with:
reference.child(key).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
System.out.println(dataSnapshot.getKey());
...
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException();
}
}
For some more on this, see:
Get parent key value on click of child in recyclerview from firebase database
How to getKey from Firebase using RecyclerView
How to access the KEY of child by it's position in custom Adapter class
How to delete selected child on Firebase Database Android
How do I know what I'm clicking Recycle View - Firebase detele data
FirebaseRecyclerOptions<Order> options = new FirebaseRecyclerOptions.Builder<Order()
.setQuery(query, Order.class)
.build();
adapter = new FirebaseRecyclerAdapter<Order, myOrderRecyclerViewHolder.myViewHolder>(options)
{
String key =options.getSnapshots().getSnapshot(position).getKey()
}
So, as the title says, I am looking to retrieve data from a Firebase database before constructing an object in FirebaseRecyclerOptions to be used in a FirebaseRecyclerAdapter. Basically, what I am trying to do is make a friends list in an app I'm working on. This what the database looks like:
Friends:
uid1:
id: friendID
uid2:
id: friendID
Users:
uid1:
name: name
status: status
image: profileImageUrl
uid2:
name: name
status: status
image: profileImageUrl
I've got code that currently looks like this:
FirebaseRecyclerOptions<Users> options = new FirebaseRecyclerOptions.Builder<Users>().setQuery(usersDatabase, new SnapshotParser<Users>() {
#NonNull
#Override
public Users parseSnapshot(#NonNull DataSnapshot snapshot) {
System.out.println(snapshot);
rootRef.child("Users").child(snapshot.getValue().toString()).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
System.out.println(dataSnapshot);
name = dataSnapshot.child("name").getValue().toString();
status = dataSnapshot.child("status").getValue().toString();
image = dataSnapshot.child("image").getValue().toString();
return;
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
System.out.println(snapshot);
return new Users(name, image, status);
}
}).build();
The problem is that the ValueEventListener I add does not trigger until after the new Users instance is returned. Should I be adding the ValueEventListener to the same DatabaseReference (userDatabase) as the FirebaseRecyclerOptions query?
What you're trying to do isn't really possible with FirebaseUI. The snapshot parser needs to return a User object immediately, or synchronously. You can't perform an asynchronous database query (which does not complete immediately, or even guaranteed to complete at all) in order to provide that value.
If you need to perform multiple queries in order to populate your views, you won't be able to use FirebaseUI effectively. You should probably consider doing all your lookups ahead of time, or write a special adapter that allows view contents to be populated asynchronously as the results become available. This will end up being a lot of code to do correctly.
It perhaps seems a little redundant to be answering my own question, but this is mostly for anyone else that has trouble with this. Following #Doug Stevenson's suggestion, I started trying to make my own custom recycler adapters and options class. However, I realized that the queries for the options could be modified. So basically, the solution is this:
Query query = database.collection("Users");
#Override
protected void onStart() {
super.onStart();
String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
DocumentReference ref = FirebaseFirestore.getInstance().collection("Users").document(uid);
ref.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()){
DocumentSnapshot document = task.getResult();
if (document.exists()) {
friends = (ArrayList<String>) document.get("friends");
if (friends.size() > 0) {
FirestoreRecyclerOptions<Users> options = new FirestoreRecyclerOptions.Builder<Users>().setQuery(query.whereIn("user_id", friends), Users.class).build();
FirestoreRecyclerAdapter<Users, UsersViewHolder> adapter = new FirestoreRecyclerAdapter<Users, UsersViewHolder>(options) {
#Override
public UsersViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.single_user_layout, parent, false);
return new UsersViewHolder(view);
}
#Override
protected void onBindViewHolder(#NonNull UsersViewHolder usersViewHolder, int i, #NonNull Users users) {
if (users != null) {
usersViewHolder.setName(users.name);
usersViewHolder.setStatus(users.status);
usersViewHolder.setImg(users.image);
final String userID = getSnapshots().getSnapshot(i).getId();
usersViewHolder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent profilePage = new Intent(FriendsList.this, ProfileActivity.class);
profilePage.putExtra("userID", userID);
startActivity(profilePage);
}
});
}
}
};
usersListPage.setAdapter(adapter);
adapter.startListening();
}
}
}
}
});
}
In the setQuery method, rather than using the collection reference as the query, I created a query object from it, and then modified the query query.whereIn(), which allows you to check to see if the field of a document contains the given object or one of the objects in a list.
My code here is very much a mess, I know.
I dont like to show empty RecyclerView to users. I know there is getItemCount method in recyclerview, but i think reyclerview is executed on separate thread maybe. Its because whenever i try to call adapter.getitemcount it gives count 0 quickly even when my recyclerview will have values coming from firebase. So i think maybe till the values come the getItemCount method is executed before and returns 0 or maybe it is executed on separate thread. But either way i get 0 even when my recyclerview has values coming from firebase. So i want to check if my firebase recycler is empty or not and then change views accordingly, becoz i dont like to show the users and empty recyclerview (an empty activity).
plz suggest how should i check it? btw heres my simple firebase adapter code :
allFriendsAdapter = new FirebaseRecyclerAdapter<FriendsModel, ViewHolder>(friendsOptions) {
#Override
protected void onBindViewHolder(#NonNull final ViewHolder holder, final int position, #NonNull FriendsModel model) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
if(allFriendsAdapter .getItemcount==0){ //this is always returning 0 even when my recyclerview has values
}
Simply override onDataChanged and check for getItemCount() there.
You can use ValueEventListener to check if any values exists:
DatabaseReference ref = FirebaseDatabase.getInstance().getReference().child("Users");
Query queries = ref.orderByChild("name").equalTo(name_here);
queries.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
Toast.makeText(HomeActivity.this,"data exists",Toast.LENGTH_SHORT).show();
}
else{
Toast.makeText(HomeActivity.this,"No data exists",Toast.LENGTH_SHORT).show();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
In your Adapter, you need to use a query to be able to get the data, example as the above query and it will also check if the data exists.
Then you can configure the adapter by building FirebaseRecyclerOptions:
FirebaseRecyclerOptions<FriendsModel> friendsOptions =
new FirebaseRecyclerOptions.Builder<FriendsModel>()
.setQuery(queries, FriendsModel.class)
.build();
Each user in my app can send and get friend requests. When the user checks his friends requests, I want the app to go through each user who sent him a friend request and retrieve his information from the Realtime Database.
This is my code in order to accomplish this:
public void check_For_Friends_And_Requests(){
String loggedUser=SaveSharedPreference.getLoggedEmail(getApplicationContext());
final DatabaseReference mDatabase= FirebaseDatabase.getInstance().getReference();
final DatabaseReference userRef=mDatabase.child("Users").child(loggedUser);
userRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()){
final List<User> friendRequestsReceived_UserList=new ArrayList<>();
for (DataSnapshot postSnapshot: dataSnapshot.child("friend_requests_received").getChildren()) {
final String senderEmail=postSnapshot.getKey();
Toast.makeText(getApplicationContext(),
senderEmail, Toast.LENGTH_SHORT).show();
if (senderEmail!=null){
mDatabase.child("Users").child(senderEmail).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Toast.makeText(getApplicationContext(),
dataSnapshot.child("name").getValue(String.class), Toast.LENGTH_SHORT).show();
friendRequestsReceived_UserList.add(
new User(
senderEmail,
dataSnapshot.child("name").getValue(String.class),
dataSnapshot.child("level").getValue(Integer.class),
dataSnapshot.child("skill").getValue(Double.class)));
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
}
UserListAdapter friendRequestsReceived_Adapter =
new UserListAdapter(getApplicationContext(),
R.layout.friend_requests_received_listview_row,
friendRequestsReceived_UserList);
friendRequestsReceived_ListView.setAdapter(friendRequestsReceived_Adapter);
}
else
connectionErrorGoToMain();
}
#Override
public void onCancelled(DatabaseError databaseError) {
connectionErrorGoToMain();
}
});
}
I have in this code 2 ValueEventListeners. I add the user information to the list in the inner one. The problem is that the list is empty at the end of this process.
I would like to fill a list view with this information using these lines:
UserListAdapter friendRequestsReceived_Adapter =
new UserListAdapter(getApplicationContext(),
R.layout.friend_requests_received_listview_row,
friendRequestsReceived_UserList);
friendRequestsReceived_ListView.setAdapter(friendRequestsReceived_Adapter);
When I put them inside the innner listener, it works fine, but I don't want to set the adapter for each user in the list, only after the for loop.
I'm attaching a screenshot with my database structure (I don't need to get all of the parameters):
The list is empty because you are declaring friendRequestsReceived_UserList outside the inner onDataChange() method. This is happening due the asynchronous behaviour of onDataChange() method which is called before you are adding those new objects to the list. So, in order to solve this, just move the declaration of the list inside the inner onDataChange() method like this:
if (senderEmail!=null){
mDatabase.child("Users").child(senderEmail).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
final List<User> friendRequestsReceived_UserList=new ArrayList<>(); //Moved here
Toast.makeText(getApplicationContext(), dataSnapshot.child("name").getValue(String.class), Toast.LENGTH_SHORT).show();
friendRequestsReceived_UserList.add(
new User(
senderEmail,
dataSnapshot.child("name").getValue(String.class),
dataSnapshot.child("level").getValue(Integer.class),
dataSnapshot.child("skill").getValue(Double.class)));
UserListAdapter friendRequestsReceived_Adapter =
new UserListAdapter(getApplicationContext(), R.layout.friend_requests_received_listview_row, friendRequestsReceived_UserList);
friendRequestsReceived_ListView.setAdapter(friendRequestsReceived_Adapter);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
As you probably see, i set the adapter also inside the inner method. If you want to use that list outside the onDataChange() i suggest you reading my answer from this post.
AskFirebase How to get the previous item values(POJO) in firebase recycler adapter without using database query.
// Set up FirebaseRecyclerAdapter with the Query
Query postsQuery = getQuery(mDatabase);
mAdapter = new FirebaseRecyclerAdapter<Post, PostViewHolder>(Post.class, R.layout.item_post,
PostViewHolder.class, postsQuery) {
#Override
protected void populateViewHolder(final PostViewHolder viewHolder, final Post model, final int position) {
final DatabaseReference postRef = getRef(position);
Log.e(TAG, "populateViewHolder: " + position);
// Set click listener for the whole post view
final String postKey = postRef.getKey();
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Launch PostDetailActivity
Intent intent = new Intent(getActivity(), PostDetailActivity.class);
intent.putExtra(PostDetailActivity.EXTRA_POST_KEY, postKey);
startActivity(intent);
}
});
// Determine if the current user has liked this post and set UI accordingly
if (model.stars.containsKey(getUid())) {
viewHolder.starView.setImageResource(R.drawable.ic_toggle_star_24);
} else {
viewHolder.starView.setImageResource(R.drawable.ic_toggle_star_outline_24);
}
// Bind Post to ViewHolder, setting OnClickListener for the star button
viewHolder.bindToPost(model, new View.OnClickListener() {
#Override
public void onClick(View starView) {
// Need to write to both places the post is stored
Log.e(TAG, "new: ");
DatabaseReference globalPostRef = mDatabase.child("posts").child(postRef.getKey());
DatabaseReference userPostRef = mDatabase.child("user-posts").child(model.uid).child(postRef.getKey());
// Run two transactions
onStarClicked(globalPostRef);
onStarClicked(userPostRef);
}
});
}
};
mRecycler.setAdapter(mAdapter);
Suppose their are five cell list whenever i am facing second cell in the list that time i want to put a condition based on first cell value. So how i can fatch the value of first cell?
I already try to using arraylist to store the POJO of Post . But the problem is whenever some item is deleted from firebase table that item onDataChange call but populateViewHolder doesn't call. Their is also a way to get previous data using database query that is
DatabaseReference ref = getRef(position-1);
ref.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
Log.e(TAG, "CHild exist: ");
} else {
Log.e(TAG, "no CHild exist: ");
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
But i don't want to use this database query is their any other way?
The Design Firebase data structure for topic Answer and Comment its like your problem.
gdtdg6765rf and hjgdhs567hd are unique key get by firebase
hjgdhs567hd is answer
gdtdg6765rf is comment to answer hjgdhs567hd
created is -1*UNIX Timestamp for ordering
date, time and toanswer was saved in comments by answer belong to
if to delete answer set all flags "deleted=1" where child "toanswer=deleted answer key" to populate again
#eurosecom above image is my layout where their is a recycler view which populate through FirebaseRecyclerAdapter . Now those green cell is my single cell. you see a red circle which denote the date. in case of position==0 I just simple visible the layout, and in case of position>0 i want to put the condition based on previous item date.
Now in my FirebaseRecyclerAdapter i have to put the condition so i have to fetch the previous position date. So as i am already doing a network oparetion using Query to fetch the msg list i don't want to put addListenerForSingleValueEvent in the populateview again as because it will again fetch the val from database. So is their any other way to get the previous item?