I want to increment score by 1 when user clicks a button, but value of score is not updated. When I updated it manually from Firebase console it updates. I don't know what the problem is. Can anyone help me, please?
#Override
public void onClick(View view) {
if (view == add) {
score++;
databaseReference1 = databaseReference.child("score");
databaseReference1.setValue(String.valueOf(score));
databaseReference1.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
textView_earning.setText(score + " ");
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
}
You have to add the value event listener before changing the value.
#Override
public void onClick(View view) {
if (view == add) {
score++;
ref1 = ref.child("score");
ref1.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
textView_earning.setText(score + " ");
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
ref1.setValue(String.valueOf(score));
}
}
If you place addValueEventListener inside onClick(), you'll end up attaching one more listener into databaseReference1 every time the button clicked. It is not a best practice as you'll face more kind of problem if it is clicked multiple time.
You should place addValueEventListener somewhere else, like (not limited to) in activity's onCreate() method:
... onCreate(...) {
databaseReference1.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
And place score variable where it is easily got, and update it inside onDataChange() (for this example, I place it on scoreValue:
private String scoreValue;
... onCreate(...) {
databaseReference1.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
scoreValue = dataSnapshot.getValue(String.class)
}
...
And then inside onClick(), you just need to get scoreValue, increment it by 1, and save the value:
... onClick() {
if (view == add) {
scoreValue++;
databaseReference.child("score").setValue(scoreValue);
}
}
Then setValue(scoreValue) will update the value at the online database, and trigger onDataChange(). So don't worry if the value is changed online, scoreValue will always have updated value.
Hope this helps.
Note: all of this consider score value is at (your databaseReference
path)\score
you can use updateChildren for update single values.
Firebase ref = new Firebase(Constants.FIREBASE_URL);
HashMap<String, Object> hm = new HashMap<String, Object>();
hm.put("score", score++);
ref.child("YOUR_ROOT_NODE")
.updateChildren(hm);
thanks all for your answer,the problem is fixed,actually i make a stupid mistake as i do not set on click listener on my button
Related
I am using firebase and I have work for 6 months to get all I need to go ahead to start building my own apps but I was having a problem that I couldn't make a button that every time I click it increase the value of child, EX: When I click the value will be 0 the I click another click the value should be 1 and so on but I couldn't do that I have used these method's below but nothing work so I have to ask now to get the simple answer for my question.
The First Method:
mDatabase.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Long Value = (Long) dataSnapshot.child("Rank").getValue();
mDatabase.child("Rank").setValue(Value+1);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
And the second:
mDatabase.child("Rank").setValue(+1);
But all these doesn't work.
When we are talking about increasing a value i recomand you using transactions like this:
mDatabase.child("Rank").runTransaction(new Transaction.Handler() {
#Override
public Transaction.Result doTransaction(final MutableData currentData) {
if (currentData.getValue() == null) {
currentData.setValue("0");
} else {
String stringValue = (String) currentData.getValue();
int intValue = Integer.parseInt(stringValue);
int increasedIntValue = intValue + 1;
currentData.setValue(String.valueOf(increasedIntValue));
}
return Transaction.success(currentData);
}
#Override
public void onComplete(DatabaseError databaseError, boolean committed, DataSnapshot currentData) {
if (databaseError != null) {
System.out.println("Firebase counter increment failed!");
} else {
System.out.println("Firebase counter increment succeeded!");
}
}
});
Another approch, as 7uthaifah mentioned later is this:
mDatabaseMostActive.child("Rank").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Long Num =(Long) dataSnapshot.getValue();
Long NumAfter = Num+1;
dataSnapshot.getRef().setValue(NumAfter);}
#Override
public void onCancelled(DatabaseError databaseError) {}});
Hope it helps and future visitos will understand better this ways of solving the problem.
Firstly thanks for Alex Mamo for answering the question and the answer was perfect but I find a better way by using "addListenerForSingleValueEvent" method here's my fully method:
mDatabaseMostActive.child("Rank").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Long Num =(Long) dataSnapshot.getValue();
Long NumAfter = Num+1;
dataSnapshot.getRef().setValue(NumAfter);}
#Override
public void onCancelled(DatabaseError databaseError) {}});
hope it helps.
Hey I am trying to get the current TIMESTAMP in the app when clicking a button but the TIMESTAMP is incorrect. The TIMESTAMP sometimes shows the time ahead and sometimes 10 minutes before time. Here is the code
timestapmReference.addValueEventListener(new ValueEventListener() {
public void onDataChange(DataSnapshot dataSnapshot) {
final long timeStampLong = (long) dataSnapshot.child("time").child("timestampQuestionSeen").getValue();
final DatabaseReference questionSeenReference = FirebaseDatabase.getInstance().getReference().child("users").child(uid).child("questions").
child(imagename);
questionSeenReference.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (!dataSnapshot.hasChild("questionSeen")) {
questionSeenReference.child("questionSeenTime").setValue(timeStampLong);
questionSeenReference.child("questionSeen").setValue("1");
}
questionSeenReference.removeEventListener(this);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
timestapmReference.removeEventListener(this);
}
public void onCancelled(DatabaseError databaseError) {
}
});
timestapmReference.child("time").child("timestampQuestionSeen").setValue(ServerValue.TIMESTAMP);
Your problem is that you are setting those values insider the onDataChange method. There is no need to do such a thing. Move this 2 lines:
questionSeenReference.child("questionSeenTime").setValue(timeStampLong);
questionSeenReference.child("questionSeen").setValue("1");
outside that method and remove that listener because is useless. To set a value you only need to use the setValue() method directly on the reference.
Hope it helps.
I am trying to delete or set node values to null in my firebase database, but the the method inside my onClick listener is running without me pressing the button.
btn_cancel.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v) {
DatabaseReference myRef = database.getReference("Orders").child(user_id);
myRef.addValueEventListener(new ValueEventListener()
{
#Override
public void onDataChange(DataSnapshot dataSnapshot)
{
myRef.child("Session").setValue(null);
// myRef.child("Dabba Type").removeValue();
// myRef.child("Start date").removeValue();
// myRef.child("End date").removeValue();
// myRef.child("Address").removeValue();
}
#Override
public void onCancelled(DatabaseError databaseError)
{
}
});
If you only want to change/modify data, then setValue() alone is enough. You don't need to attach addValueEventListener. Like this:
public void onClick(View v) {
database.getReference("Orders").child(user_id).child).child("Session").setValue(null);
}
Additional info (maybe will not work if applied to the code in question, but this information likely to be useful)
If you also want to read the value first before committing any change (like confirming if is true then change it to false and otherwise), then you should consider using addListenerForSingleValueEvent instead of addValueEventListener. Because it only do your code once while the later is keep listening to any data change that happen until you remove/detach the listener. And in this case, you should do it like this:
#Override
public void onClick(View v) {
DatabaseReference myRef = database.getReference("Orders").child(user_id);
myRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.getValue(Boolean.class)) {
// if value is true, change data
myRef.setValue(false);
...
Hope this helps
I have to use custom RecyclerView because I don't want to update to list real time.
How do I get an id if I want to go into the details of the data? As in FirebaseRecyclerAdapter.
final String uid = getRef(position).getKey();
I added postId, my posts table, and I wrote the following code. But when click on the image, it goes to the last added image to the list. And when I click upVote, every item goes crazy and they click upVote too.
First, am I on the right track to update the list only when I want to? Second, why is everything going crazy?
PostAdapter
public PostRecyclerAdapter(Context context, Query query) {
this.context = context;
this.query = query;
query.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
posts.clear();
for (DataSnapshot data : dataSnapshot.getChildren()) {
posts.add(data.getValue(Post.class));
}
Collections.sort(posts, new Comparator<Post>() {
#Override
public int compare(Post o1, Post o2) {
Long a = o1.getCreatedDate();
Long b = o2.getCreatedDate();
if (a < b) {
return -1;
} else if (a == b) {
return 0;
} else {
return 1;
}
}
});
notifyDataSetChanged();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
#Override
public void onBindViewHolder(final PostViewHolder viewHolder, int position) {
model = posts.get(position);
postId = model.getPostId();
viewHolder.setTitle(model.getTitle());
viewHolder.setImage(context, model.getImage());
viewHolder.setUpVote(postId);
viewHolder.imvImage.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent(context, SinglePostActivity.class);
intent.putExtra(Enums.PostKeys.postId.getValue(), postId);
context.startActivity(intent);
}
});
viewHolder.imbUpVote.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (!checkAuthUser()) {
context.startActivity(new Intent(context, SignUpActivity.class));
return;
}
processVote = true;
Singleton.getDbPostDownVote(postId).child(postId).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (processVote == true) {
if (dataSnapshot.hasChild(getUserId())) {
Singleton.getDbPostDownVote(postId).child(postId).child(getUserId()).removeValue();
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Singleton.getDbPostUpVote(postId).child(postId).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (processVote == true) {
if (dataSnapshot.hasChild(getUserId())) {
Singleton.getDbPostUpVote(postId).child(postId).child(getUserId()).removeValue();
processVote = false;
} else {
Singleton.getDbPostUpVote(postId).child(postId).child(getUserId()).setValue(0);
processVote = false;
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
});
}
PostViewHolder:setUpVote
public void setUpVote(final String postId) {
Singleton.getDbPostUpVote(postId).child(postId).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.hasChild(getUid())) {
imbUpVote.setImageResource(R.drawable.vote_up_active);
} else {
imbUpVote.setImageResource(R.drawable.vote_up_passive);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
How do I get an id if I want to go into the details of the data?
Usually the id is a node in your db. As you can see in
final String uid = getRef(position).getKey();
getKey returns tha value of the node in db.
In your case to avoid sorting the list with comparator i would just structure the data like so:
20170111
title : some title
text : some text
20170112
title : some title
text : some text
This way data is going to be sorted by the nodes, which is the date, by Firebase. If you want to be more precise you can also add hours and minutes.
First, am I on the right track to update the list only when I want to?
No.
Calling addValueEventListener() is going to trigger the code inside the listener each time the value in your db changes. In other words, its realtime.
Use addListenerForSingleValueEvent() insted. It fires only once.
Second, why is everything going crazy?
Very important thing about onDataChange() is that it fires not only when the value changes but also the first time you set the listener. That is why everything is getting voted up when you click one item.
I am having some trouble knowing when my Firebase API call is finished. After reading the Firebase documentation I have found the following:
Value events are always triggered last and are guaranteed to contain updates from any other events which occurred before that snapshot was taken.
I understand this to mean that only after all the onChildAdded call is finished, then the ValueEventListener is called. As a result, I thought that I can populate my RecyclerView in the onChildAdded function and then the onSingleValueListener call, I can simply finish animating my loading screen (which has started animating before this function call) and proceed. However, I have run into an issue where I put some careful System.out.println statements and found that in my case, Test 1 is called before Test 2 is ever called. This causes problems because this is actually the opposite behavior of what I wanted: I wanted the onChildAdded function to finish and then call the onSingleValueListener function that prints out Test 1 to be called. Is there any reason why this is happening? Any way around this? I would appreciate an explanation on why this is happening. Thanks!
public void getComments(final String postId, final Activity activity, final View fragmentView, final View progressOverlay) {
final Firebase commentsRef = firebaseRef.child("/comments");
Firebase linkRef = firebaseRef.child("/posts/" + postId);
linkRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
System.out.println("Test 1");
if (progressOverlay.getVisibility() == View.VISIBLE) {
progressOverlay.setVisibility(View.GONE);
AndroidUtils.animateView(progressOverlay, View.GONE, 0, 200);
fragmentView.findViewById(R.id.rv_view_comments).setVisibility(View.VISIBLE);
}
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
linkRef.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
commentsRef.child(dataSnapshot.getKey()).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Comment comment = dataSnapshot.getValue(Comment.class);
System.out.println("Test 2");
application.getCommentsRecyclerViewAdapter().getCommentsList().add(comment);
application.getCommentsRecyclerViewAdapter().notifyDataSetChanged();
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
}
#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(FirebaseError firebaseError) {
}
});
}
You may want to use the **FirebaseRecyclerAdapter** class that the Firebase team makes available in FirebaseUI-Android (see https://github.com/firebase/FirebaseUI-Android/blob/master/database/src/main/java/com/firebase/ui/database/FirebaseRecyclerAdapter.java)
In your gradle file add the line below (check here for latest version number in the readme)
compile 'com.firebaseui:firebase-ui-database:0.4.3'
With this code "Firebase linkRef = firebaseRef.child("/posts/" + postId);" I could see that you're using legacy Firebase API. Its deprecated now!
Kindly update your code to new Firebase 3.x.x API.
Below two are independent async call; Based on your use-case, you can use either one of the listener to read your data.
1. linkRef.addListenerForSingleValueEvent(new ValueEventListener() {});
2. linkRef.addChildEventListener(new ChildEventListener() {});
You can refer the firebase document to get more information about database listeners.
https://firebase.google.com/docs/database/android/retrieve-data
With the following code snippet, you can retrieve and populate your list of comments.
public void getComments(final String postId, final Activity activity, final View fragmentView, final View progressOverlay) {
DatabaseReference commentsRef = firebaseRef.child("/comments");
DatabaseReference linkRef = commentsRef.child("/posts/" + postId);
linkRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// Iterate through data-snapshot, and update your Adapter dataset
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
Comment comment = snapshot.getValue(Comment.class);
application.getCommentsRecyclerViewAdapter().getCommentsList().add(comment);
}
application.getCommentsRecyclerViewAdapter().notifyDataSetChanged();
// Dismiss your loading progressbar
if (progressOverlay.getVisibility() == View.VISIBLE) {
progressOverlay.setVisibility(View.GONE);
AndroidUtils.animateView(progressOverlay, View.GONE, 0, 200);
fragmentView.findViewById(R.id.rv_view_comments).setVisibility(View.VISIBLE);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
// Handle fail case here
}
});
Hope this would help you!