-1

Okay so I got it to run showing the User ID but not score. I then began making some changes, forgot what I'd changed and now I'm back to null null again. I feel like I may have deleted something or misspelled something.

   dbref.addValueEventListener(new com.google.firebase.database.ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {

                ArrayList<String> list = new ArrayList<>();
                list.clear();

                for(DataSnapshot ds :dataSnapshot.getChildren()) {
                    Score Result = ds.getValue(Score.class);
                    String userId = String.valueOf(Result.getUserId());
                    String score = String.valueOf(Result.getScore());
                    list.add(userId);
                    list.add(score);

                }
                adapter = new ArrayAdapter<>(getActivity(), android.R.layout.simple_list_item_1, list);
                LvRanking.setAdapter(adapter);
            }

Here's my model:

public class Score {
private String userId, score;

public Score() {}

public Score(String userId, String score) {
    this.userId = userId;
    this.score = score;
}

public String getUserId() {
    return userId;
}

public String getScore() {
    return score;
}

@Override
public String toString() {
    return "Score{" +
            "userId='" + userId + '\'' +
            ", score='" + score + '\'' +
            '}';
}
 }

Database: Link to my database screenshot

coderob
  • 3
  • 1
  • 7
  • 3
    Could you put your "logcat print for when the crash happens? Normally the red text. LogCat in on the bottom of Android Studio or View > Tool Windows > LogCat – Canato Feb 05 '18 at 12:24
  • Nothing is written to the logcat when it crashes, I navigate to the fragment via bottom navigation bar. When I click the button the app just stops and the logcat doesn't say why. – coderob Feb 05 '18 at 12:28
  • 1
    It is a strange behaviour. You are using a emulator or a mobile connect with the Android Studio right? If yes, maybe you need the change the filter in the logcat. Where you see "Show Only Selected App" put "No Filter" and choose verbose on the third dropdown menu. – Canato Feb 05 '18 at 12:31
  • What is the error in your logcat? – Alex Mamo Feb 05 '18 at 12:39
  • See edit above, app is not crashing anymore just saying null:null . – coderob Feb 05 '18 at 12:47
  • Still no errors in log cat when I go to the fragment – coderob Feb 05 '18 at 12:48

2 Answers2

3

This is a classic issue with asynchronous APIs. In order to make it work, change your model class according to Java Naming Conventions. Your class should look like this:

public class Score {
    private String userId, score;

    public Score() {}

    public Score(String userId, String score) {
        this.userId = userId;
        this.score = score;
    }

    public String getUserId() {
        return userId;
    }

    public String getScore() {
        return score;
    }

    @Override
    public String toString() {
        return "Score{" +
                "userId='" + userId + '\'' +
                ", score='" + score + '\'' +
                '}';
    }
}

Also note that onDataChange() method has an asynchronous behavior, which means that is called even before you are trying to add those objects of Score class to the list. In other words, your list will always be empty outside that method. A quick fix would be to move the declaration of your list inside onDataChange() and do what you want to do with it or, if you want to dive into the asynchronous world and use my answer from this post.

Assuming the score node is a direct child of your Firebase root, to display the data using the String class, please use the following code:

ListView listView = (ListView) findViewById(R.id.list_view);
List<String> list = new ArrayList<>();
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, list);
listView.setAdapter(arrayAdapter);
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference scoreRef = rootRef.child("score");
ValueEventListener eventListener = new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        for(DataSnapshot ds : dataSnapshot.getChildren()) {
            String userId = ds.child("userId").getValue(String.class);
            String score = ds.child("score").getValue(String.class);
            list.add(userId + " / " +  score);
            Log.d("TAG", userId + " / " +  score);
        }
        arrayAdapter.notifyDataSetChanged();
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {
        Log.d(TAG, task.getException().getMessage());
    }
};
scoreRef.addListenerForSingleValueEvent(eventListener);

And this how you can display data using the Score class.

ValueEventListener eventListener = new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        for(DataSnapshot ds : dataSnapshot.getChildren()) {
            Score score = ds.getValue(Score.class);
            String userId = score.getUserId();
            String score = score.getScore();
            Log.d("TAG", userId + " / " +  score);
            list.add(score);
        }
        arrayAdapter.notifyDataSetChanged();
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {
        Log.d(TAG, task.getException().getMessage());
    }
};
scoreRef.addListenerForSingleValueEvent(eventListener);
Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
  • Are you creating the list inside the `onDataChange()` method and setting the adapter in same place? – Alex Mamo Feb 05 '18 at 14:10
  • @AlexMamo Yes everything is inside that method except for Listview LvRanking; and LvRanking = ...(R.id.LvRanking); – coderob Feb 05 '18 at 14:16
  • @AlexMamo edited now and added screenshot of database. Thanks very much for you help – coderob Feb 05 '18 at 14:24
  • Is not correct. You haven't changed the model class, is important to have your fields named correctly. See [here](https://stackoverflow.com/questions/48315793/how-to-sum-multiple-values-from-firebase/48316404#48316404) where to set the adapter. – Alex Mamo Feb 05 '18 at 14:25
  • @AlexMamo I'm not too sure what you mean.. I have only been using Android for about a month. I will try implement the link you sent. Thanks – coderob Feb 05 '18 at 14:33
  • Yes, give it a try and keep me posted. – Alex Mamo Feb 05 '18 at 14:34
  • @AlexMamo Not sure where I am going wrong Override public void onDataChange(DataSnapshot dataSnapshot) { list.clear(); for(DataSnapshot myitem: dataSnapshot.getChildren()) { Score score = myitem.getValue(Score.class); list.add(score.getUserID()); list.add(score.getScore()); } adapter = new ArrayAdapter(getActivity(), android.R.layout.simple_list_item_1, list); LvRanking.setAdapter(adapter); } – coderob Feb 05 '18 at 15:32
  • Use the model class the in the exact way I showed you. Use the code in the same way is in that post. But remember, delete old data. – Alex Mamo Feb 05 '18 at 16:19
  • @AlexMamo Sorry I have updated question, I know I'm so close so would appreciate the last bit of help from you as you've gotten me so far. – coderob Feb 05 '18 at 17:33
  • Please add you how your database looks like now, after those changes. Please add a more detailed one and I will write you some code. The model class looks fine now. – Alex Mamo Feb 05 '18 at 17:39
  • **As I said you earlier, delete the old data**. Your fields must be first letter lowercase. Add fresh data and use the code I provided in my updated answer. Does it work now? – Alex Mamo Feb 05 '18 at 17:59
  • @AlexMamo No, getting error 'Cannot resolve constructor' after '(getActivity...) ArrayAdapter arrayAdapter = new ArrayAdapter(getActivity(), android.R.layout.simple_list_item_1, list); – coderob Feb 05 '18 at 19:00
  • Updated the answer. – Alex Mamo Feb 05 '18 at 19:03
  • If it is not working change your ArraAdapter to be of type Score. Should work. – Alex Mamo Feb 05 '18 at 19:10
  • @AlexMamo App just crashing again, won't allow me to change the array type unless its to String. Don't think I'll ever get it working :( – coderob Feb 05 '18 at 19:14
  • Have you tried to use the first option, to use the String class? – Alex Mamo Feb 06 '18 at 08:26
  • @AlexMamo I finally got it to work - The issue was score was saved as an integer to the database and therefore crashed the app when I called it back as a string. Thank you so so much for all your help I am very grateful for the time you put into this. – coderob Feb 06 '18 at 16:36
  • Why should naming conventions play any role for asynchronous APIs? I usually can name my classes like I want. As the term "convention" suggests it is a "convention" and not a "rule"or "law". Maybe I just don't get the point... – The incredible Jan Oct 10 '18 at 09:58
  • @TheincredibleJan `Java Naming Conventions` has nothing to do with asynchronous APIs but can help you name your classes/methods/fields in a way other can understand. JavaBeans properties usually use [Introspection](https://docs.oracle.com/javase/7/docs/api/java/beans/Introspector.html), but yes, that does use reflection behind the scenes. Firebase follow JavaBean property naming conventions. – Alex Mamo Oct 10 '18 at 10:08
0

filepath.addOnSuccessListener(upload data first) then get it and use it....

Anil Atri
  • 68
  • 8