Firebase realtime database listing data with auto increment in Java - java

In Firebase, I list my data by auto increment. However, when i any data is deleted, i can't new data added. Updating is being made on the last added data. I need a solution for this.
Firebase
My Source:
public class MainActivity extends AppCompatActivity {
EditText name_et;
Button button_save;
FirebaseDatabase firebaseDatabase;
DatabaseReference databaseReference;
long autoincrementid =0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
name_et = findViewById(R.id.aaaa);
button_save = findViewById(R.id.btnsave);
databaseReference = firebaseDatabase.getInstance().getReference().child("Data");
databaseReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
if(snapshot.exists());
autoincrementid=(snapshot.getChildrenCount());
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
button_save.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String a = name_et.getText().toString();
databaseReference.child(String.valueOf(autoincrementid+1)).setValue(a);
Toast.makeText(MainActivity.this, "+++++", Toast.LENGTH_SHORT).show();
}
});
}
}

Right now you use the count of children to determine what the next number is. That works well if all sequential indexes are occupied, but (as you found) not when you delete one of more indexes in there.
The proper solution in that case depends on what you want to happen. I know of these general use-cases:
You want the list to behave like an array, which means that when you remove #3 in your JSON, the #4 and #5 actually get a new index. This will require you to change all other items when you remove one. For more on this see the answer I gave just now to another question: Firebase Remove with Javascript
You want to have an always increasing sequential index, typically referred to as a sequence or auto-increment values in relational databases. If you want this, you'll have to store the latest index that you handed out somewhere separate in the database and transactionally update that whenever you add an item to the list.
You want to put new items at the first available index, reusing the indexes of any deleted nodes. This seems to be your use-case, so we'll look at that further below.
Possible code for a solution that finds the first available id:
databaseReference = firebaseDatabase.getInstance().getReference().child("Data");
databaseReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
int autoincrementid = 0;
do {
autoincrementid++;
} while (snapshot.hasChild(""+autoincrementid));
snapshot.child(""+autoincrementid).setValue("New value");
})(
#Override
public void onCancelled(#NonNull DatabaseError error) {
throw error.toException(); // never ignore errors
}
});
A few notes on this code:
I didn't run it, so there may well be some minor errors in there. If you get any of those, please try to figure them out on your own and edit the answer to include the solution.
You probably should use a transaction here to ensure there are no conflicting updates. The logic of the loop will be the same though.

Related

Firebase Realtime Databse DataSnapshot only Read the last value in the child

I'm calling addValueEventListener inside the button click, but this method only reads the last item in the node. I want to read all the child value in the node What went wrong here?
btnorder.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
databaseReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for(DataSnapshot dataSnapshot1:dataSnapshot.getChildren())
{
SelectedItems si=dataSnapshot1.getValue(SelectedItems.class);
si.getItemname();
Toast.makeText(MyBookedItems.this, ""+si.getItemname(), Toast.LENGTH_SHORT).show();
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
});
I think you can create an array list and put all snapshot data into it, and then you can find the last data of the list with the value you find by subtracting one from the length of the list. Sorry for my bad English :( I have tried to explain in the solution in the code below)
ArrayList<SelectedItems> selectedItems = new ArrayList<SelectedItems>();
for(DataSnapshot dataSnapshot1:dataSnapshot.getChildren())
{
SelectedItems si=dataSnapshot1.getValue(SelectedItems.class);
selectedItems.add(si);
}
selectedItems.get(selectedItems.size());
It's read all the values but I got the output as a Toast message, because of that I only see the last value)

Android Firebase Sorting

I am building an app. I have a Firebase database with a bunch of products, each of them has a rating. I am displaying them in a ListView but want to sort them by their rating. Do I need to save them differently to get this to work ?
private ListView mTopRatedList;
ArrayList<String> mAllBeers = new ArrayList<>();
DatabaseReference Reference = FirebaseDatabase.getInstance().getReference().child("Beers");
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_top_rated);
mTopRatedList =(ListView)findViewById(R.id.topRatedList);
final ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,mAllBeers);
mTopRatedList.setAdapter(arrayAdapter);
arrayAdapter.clear();
Reference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
arrayAdapter.clear();
for(DataSnapshot snapshot : dataSnapshot.getChildren()){
//arrayAdapter.add(snapshot.getValue().toString());
Beers beers = snapshot.getValue(Beers.class);
String beerClass = beers.getmName();
Float beerRating = beers.getmRating();
//Collections.sort()
arrayAdapter.add(beerClass+ "User Rating is " + beerRating);
}
arrayAdapter.notifyDataSetChanged();
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
}
From the look of your code, each of your beers has a rating property in the database.
In that case you can show the beers ordered by rating by doing:
Reference.orderByChild("rating").addValueEventListener(new ValueEventListener() {
...
A few notes:
This will show the beers in ascending order of their rating, so with the lowest value first. If you want to show the highest rated beer first, Firebase does not have a built-in operator for descending sort. So you'll have to work around that by either reversing the results in your client-side code, or by including an inverted rating in the database too for this purpose. In the latter case you'd sort on the inverted rating, and then display the regular rating.
For this to work correctly it is important that the rating value is a number, and not stored as a string. When stored as a string it may work for values up to 9, but after that you'll run into cases where numerical ordering differs from the lexicographical ordering that Firebase will use for strings.
Please never leave onCancelled empty as it hides potential problems. Its minimal implementation is public void onCancelled(#NonNull DatabaseError databaseError) { throw databaseError.toException(); }.

How can I read values from Firebase Realtime Database by using a Java method in AndroidStudio?

My Firebase Realtime Database has been built by loading an object of the Java class HashMap. In my Android Studio app I'm trying to write a method that takes a String (the key) as input, searches through the database and if the string is found it returns the associated Float (the value), otherwise it returns 0. How can I do this? Any help would be really appreciated!
EDIT: I've tried to follow the suggestions, adapting them to my particular case, but I didn't manage to solve the problem yet.
I wrote the following code in MainActivity:
DatabaseReference myRef;
Float tempValue;
#Override
protected void onCreate(Bundle savedInstanceState) {
...
myRef = FirebaseDatabase.getInstance().getReference("myRoot");
tempValue=0f;
...
}
public void retrieveValueFromDatabase(String childName, final MainActivity activity){
myRef.child(childName).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Float value=dataSnapshot.getValue(Float.class);
if (value==null){
value=0f;
}
activity.tempValue=value;
//First Toast
//Toast.makeText(activity,"tempValue = "+tempValue.toString(), Toast.LENGTH_LONG).show();
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException();
}
});
}
public void useValues(){
retrieveValueFromDatabase(childName,this);
//Second Toast
//Toast.makeText(this,"tempValue = "+tempValue.toString(), Toast.LENGTH_LONG).show();
//code using tempValue from here
...
}
If I uncomment the first toast, the correct value inside tempValue is shown, but if I uncomment the second toast, the value of tempValue shown is the default one (0.0). What am I missing?
You need to use addValueEventListener to retrieve data from the database:
DatabaseReference ref = FirebaseDatabase.getInstance().getReference("myRoot").orderByChild("name").equalTo("peter");
ref.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Log.i("Database", dataSnapshot.child("floatValue").getValue(Long.class));
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException();
}
})
Here, you add a reference to the root node, then query using equalTo() to check if name = peter exists in the database and return the float value.
You should read the guide:
https://firebase.google.com/docs/database/android/read-and-write

How can i count a query / dataSnapshot? [duplicate]

This question already has answers here:
Default FirebaseApp is not initialized
(34 answers)
Closed 3 years ago.
I want to show the number of active users in my StartActivity. The users are divided into two groups: players and spectators. I tried it in my database with a Boolean value. How can I show / count the users?
Database:
Firebase Pic
StartActivity:
XML Pic
Here is my StartAytivity.class code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_start);
Button run = findViewById(R.id.button2);
run.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(StartActivity.this, MainActivity.class);
startActivity(intent);
}
});
FirebaseApp.initializeApp(this);
watcher = findViewById(R.id.textView3);
player = findViewById(R.id.textView4);
onlineCount();
}
Here is my OnlineCount code:
public void onlineCount(){
//watcher
refwatcher = FirebaseDatabase.getInstance()
.getReference("online_list")
.child("watcher")
.child(Objects.requireNonNull(FirebaseAuth.getInstance().getCurrentUser()).getUid()).child("online");
Query query = refwatcher.orderByChild("online").equalTo(true);
refwatcher.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
wcount = (int) dataSnapshot.getChildrenCount();
watcher.setText(getString(wcount) + getText(R.string._29_349_watchers_online));
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
//player
DatabaseReference refplayer = FirebaseDatabase.getInstance().getReference("online_list").child("player");
refplayer.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
player.setText("1" + getText(R.string._230_player_online));
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
Here is my debug code:
Caused by: java.lang.NullPointerException
at java.util.Objects.requireNonNull(Objects.java:203)
at com.bbinktattoo.nerve.StartActivity.onlineCount(StartActivity.java:65)
at com.bbinktattoo.nerve.StartActivity.onCreate(StartActivity.java:57)
at android.app.Activity.performCreate(Activity.java:7326)
at android.app.Activity.performCreate(Activity.java:7317)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
Your issue has nothing to do with the problem your question implies. You want to know how to count your database entries. Every element in firebase is a node and can have n children and has exactly 1 parent. The relation ship between a node and its parent is 1:1 while the relation ship between the node and its children is 1:n.
So if you want to count the amount of elements you have to call the getChildren() Method on the parent node, which will return n references to the n nodes.
However, it seems like you're not initializing the Firebase SDK as supposed to. Make sure you read and understand the related docs
https://firebase.google.com/docs/android/setup
I would guess that you have not set the correct configuration parameters
Your issue is not the counting itself but Firebase seems improperly setup in your app. Make sure to follow the official guide step by step. Also check Logcat for errors and warnings from Firebase, usually there is something useful.

Updating a List Size for all users

I am trying to make it so that when a user creates an open game, in my Firebase Database the name of the game is the size of the list. So the very first game created the name of it will = 0, and if another user creates a game then the game will be labeled 1 and so on.
I have it set up right now so that the games are labeled the size of the game list, but the list isn't really updating. The games keep getting called '0' because it thinks the list is empty even though I have visual confirmation in the app that there are items being added to the list.
So my question is: How can I make it so the list continuously updates each time a game is added, and how can I make it so that it updates for all users and not just the user who created the game?
This is what I have setup right now. Here are the variables I am using for the list and the integer getting the list size
ArrayList<String> openGames = new ArrayList<>();
int gameSlot = openGames.size();
Here is what I use to name the game when it is created.
gameMaker = new GameMaker(hp.uid, userName, wagerD, gameSlot);
FirebaseDatabase.getInstance().getReference("FCGames").child(Integer.toString(gameSlot))
.setValue(gameMaker).addOnCompleteListener...
And this is what I have to add the game to the list.
cgRef.child(Integer.toString(gameSlot)).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
openGames.add(userName);
adapter.notifyDataSetChanged();
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
So again my question is how can I make this list update correctly and how can I make it update for all users on the app?
Edit
Here is what I did with my onChangeData
cgRef.child(Integer.toString(gameSlot)).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
wager = (String) dataSnapshot.child("wager").getValue();
gameSlot = openGames.size();
adapter.notifyDataSetChanged();
}
and now the openGames.add is in my createGameLobby method.
FirebaseDatabase.getInstance().getReference("FCGames").child(Integer.toString(gameSlot))
.setValue(gameMaker).addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
openGames.add(userName);
Toast.makeText(FlipCoinLobby.this, "Game creation successful.", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(FlipCoinLobby.this, task.getException().getMessage(), Toast.LENGTH_SHORT).show();
}
}
});
^^ that is just the important snippit from the method. And then I have an onClickListener that creates that calls that method when a button is pressed
In below code segment, you did that openGames.add(username) in onDataChange listener. I think it is an incorrect use of this ondatachange function
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
openGames.add(userName);
adapter.notifyDataSetChanged();
}
please use this to get data from db and update your data. Then push your data to db. You didn't use snapshot value also. You can get most recently updated data from dataSnapshot . Use it to update user data then push it to db

Categories

Resources