0

So, I am using Firebase UI to get data from Database. It looks like this: Database

I want to iterate through "messages" to get every child, and show it as message at my app. This is how my UserMessage class looks alike:

public class UserMessage {

private String mUserLogin;
private String mUserMessage;
private String mUID;

UserMessage(String userLogin, String userMessage, String uid) {
    mUserLogin = userLogin;
    mUserMessage = userMessage;
    mUID = uid;
}

public String getmUserLogin() {
    return mUserLogin;
}

public void setmUserLogin(String mUserLogin) {
    this.mUserLogin = mUserLogin;
}

public String getmUserMessage() {
    return mUserMessage;
}

public String getmUID() {
    return mUID;
}

public void setmUID(String mUID) {
    this.mUID = mUID;
}


public void setmUserMessage(String mUserMessage) {
    this.mUserMessage = mUserMessage;
}

}

And i want to get it through FirebaseRecyclerOptions:

FirebaseRecyclerOptions<UserMessage> options =
            new FirebaseRecyclerOptions.Builder<UserMessage>()
            .setQuery(firebaseDatabaseModel.getQuery(), new SnapshotParser<UserMessage>() {
                @NonNull
                @Override
                public UserMessage parseSnapshot(@NonNull DataSnapshot snapshot) {
                    Log.d("snapshot length", String.valueOf(snapshot.getChildrenCount()));
                    return snapshot.getValue(UserMessage.class);
                }
            })
            .build();

And here is how my firebaseDatabaseModel.getQuery() method looks like:

 public Query getQuery() {
DatabaseReference messageReference =    database.child("messages");
    return messageReference.limitToLast(50);
}

And this is how I want to set data in my FirebaseRecyclerAdapter

class ChatMessagesAdapter extends FirebaseRecyclerAdapter<UserMessage, ChatMessagesAdapter.ChatHolder> {
private UserMessage[] userMessages;

/**
 * Initialize a {@link RecyclerView.Adapter} that listens to a Firebase query. See
 * {@link FirebaseRecyclerOptions} for configuration options.
 *
 * @param options
 */
public ChatMessagesAdapter(@NonNull FirebaseRecyclerOptions<UserMessage> options) {
    super(options);
    userMessages = (UserMessage[]) options.getSnapshots().toArray();
       }

@Override
protected void onBindViewHolder(@NonNull ChatHolder holder, int position, @NonNull UserMessage model) {

    holder.message.setText(userMessages[position].getmUserMessage());
    holder.user.setText(userMessages[position].getmUserLogin());

}

@NonNull
@Override
public ChatHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.chat_message, parent, false);
    return new ChatHolder(view);
}

static class ChatHolder extends RecyclerView.ViewHolder {

    @BindView(R.id.user_message)
    TextView message;
    @BindView(R.id.user_login)
    TextView user;

    private ChatHolder(View itemView) {
        super(itemView);
        ButterKnife.bind(itemView);

    }
}

}

The thing is, not matter how I'm trying to send options (User Messages) through array to FirebaseRecyclerAdapter, it always has 0 count. Probably the problem is lying under custom parser (I have to compose it properly), but my debugger is just skipping this part of code:

 public UserMessage parseSnapshot(@NonNull DataSnapshot snapshot) {
                Log.d("snapshot length", String.valueOf(snapshot.getChildrenCount()));
                return snapshot.getValue(UserMessage.class);
            }

So I cannot even check what I'm doing wrong here. I've tried to figure it out for few hours already, and I just really don't know how to deal with it.

//Edit 1: Due to @Alex Malmo suggestions, I've changed database keys to: enter image description here

And UserMessage.class to:

public class UserMessage {
private String userLogin, userMessage, uid;

public UserMessage() {}

public UserMessage(String userLogin, String userMessage, String uid) {
    this.userLogin = userLogin;
    this.userMessage = userMessage;
    this.uid = uid;
}

public String getUserLogin() { return userLogin; }
public String getUserMessage() { return userMessage; }
public String getUid() { return uid; }

}

It looks like the thing is still in FirebaseRecyclerOptions creation. I've tried those two methods:

FirebaseRecyclerOptions<UserMessage> options = new FirebaseRecyclerOptions.Builder<UserMessage>()
            .setQuery(query,
                    UserMessage.class)
            .build();

and

    FirebaseRecyclerOptions<UserMessage> options = new FirebaseRecyclerOptions.Builder<UserMessage>()
            .setQuery(query,
                    new SnapshotParser<UserMessage>() {
                        @NonNull
                        @Override
                        public UserMessage parseSnapshot(@NonNull DataSnapshot snapshot) {
                            return snapshot.getValue(UserMessage.class);
                        }
                    })
            .build();

In both cases - second argument in setQuery method (parsing part) seems to be ignored (compilator just skips that part - it gets correct query, and goes straight to .build()), which seems to be a whole problem with empty data in adapter.

  • 1
    At first place your are missing an empty constructor in model class: `public UserMessage() { }` which is needed for Firebase UI adapter – Yupi Sep 13 '18 at 19:57
  • @Yupi thank your for that suggestion. I've already done that. But then, It still doesn't help in whole parser problem (at least that's what I am thinking?). I'm basing my knowledge on https://github.com/firebase/FirebaseUI-Android/blob/master/database/README.md and some other problems with that on Stack, but I just cannot find proper resolve for my problem. – Damian Ziółkowski Sep 13 '18 at 20:06
  • Does your `DatabaseRefrence` has correct path to `messages` node? – Yupi Sep 13 '18 at 20:07
  • I am pushing my messages by the same reference (like this - messageReference.push().setValue(userMessage); ) and it works just fine. – Damian Ziółkowski Sep 13 '18 at 20:11
  • Maybe you are missing `this` inside: `ButterKnife.bind(this, itemView);` also class needs to be `public` – Yupi Sep 13 '18 at 20:23
  • @Yupi I think that's not the case, at least for now. The problem is that "options" is empty at adapter, so the problem is probably in creating/passing those FirebaseRecyclerOptions. Binder doesn't even have anythng to bind right now. – Damian Ziółkowski Sep 13 '18 at 20:42

2 Answers2

1

The problem in your code is that you are using wrong getters for your fields. In your model class fields that start with a lowercase letter. When you are using a field named mUserLogin, Firebase is looking for a getter named getMUserLogin() and not getmUserLogin(). See the capital letter M vs. lowercase m letter? This is probably the most likely reason why you are getting nothing. The correct way your model class should look like is this:

public class UserMessage {
    private String userLogin, userMessage, uid;

    public UserMessage() {}

    public UserMessage(String userLogin, String userMessage, String uid) {
        this.userLogin = userLogin;
        this.userMessage = userMessage;
        this.uid = uid;
    }

    public String getUserLogin() { return userLogin; }
    public String getUserMessage() { return userMessage; }
    public String getUid() { return uid; }
}

To have correct data in your database, just remove the old data and add fresh one. Your code should work now.

You can also take a look at Java Naming Conventions.

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
  • Thank you for suggestions @Alex Mamo . I've checked it. Unfortunately, still the thing is that on creating FirebaseRecyclerOptions, it's just skipping parsing part (it get's query, then it goes straight to .build part, skipping UserMessage.class, or creating new SnapshotParser), just like there would be something wrong in querying. – Damian Ziółkowski Sep 14 '18 at 10:16
  • Have your also simply tried to use this `FirestoreRecyclerOptions options = new FirestoreRecyclerOptions.Builder().setQuery(firebaseDatabaseModel.getQuery(), UserMessage.class).build();`? – Alex Mamo Sep 14 '18 at 10:32
  • Please also add the modified code and your new data that you have added in the database along with the changes. – Alex Mamo Sep 14 '18 at 10:33
  • Have you also start listening for changes as in my answer from this [post](https://stackoverflow.com/questions/48846631/i-cant-see-any-firebaserecycleradapter-items-on-my-layout/48851402)? – Alex Mamo Sep 14 '18 at 10:59
0

Okay, I have finally resolved it. The problem was, that I've completely misunderstood FirebaseRecyclerOptions functions. I was completely sure that it's passing data inside it, and whole thing was that I was trying to get that data, without letting whole method do its work.

So I have changed adapter construction from:

 class ChatMessagesAdapter extends FirebaseRecyclerAdapter<UserMessage, ChatMessagesAdapter.ChatHolder> {
private UserMessage[] userMessages;

/**
 * Initialize a {@link RecyclerView.Adapter} that listens to a Firebase query. See
 * {@link FirebaseRecyclerOptions} for configuration options.
 *
 * @param options
 */
public ChatMessagesAdapter(@NonNull FirebaseRecyclerOptions<UserMessage> options) {
    super(options);
    userMessages = (UserMessage[]) options.getSnapshots().toArray();
       }

@Override
protected void onBindViewHolder(@NonNull ChatHolder holder, int position, @NonNull UserMessage model) {

    holder.message.setText(userMessages[position].getmUserMessage());
    holder.user.setText(userMessages[position].getmUserLogin());

}

@NonNull
@Override
public ChatHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.chat_message, parent, false);
    return new ChatHolder(view);
}

static class ChatHolder extends RecyclerView.ViewHolder {

    @BindView(R.id.user_message)
    TextView message;
    @BindView(R.id.user_login)
    TextView user;

    private ChatHolder(View itemView) {
        super(itemView);
        ButterKnife.bind(itemView);

    }
}

To:

class ChatMessagesAdapter extends FirebaseRecyclerAdapter<UserMessage, ChatMessagesAdapter.ChatHolder> {
    private ArrayList<UserMessage> messagesList = new ArrayList<>();

    /**
     * Initialize a {@link RecyclerView.Adapter} that listens to a Firebase query. See
     * {@link FirebaseRecyclerOptions} for configuration options.
     *
     * @param options
     */
    ChatMessagesAdapter(@NonNull FirebaseRecyclerOptions<UserMessage> options) {
        super(options);           }

    @Override
    protected void onBindViewHolder(@NonNull ChatHolder holder, int position, @NonNull UserMessage model) {
        messagesList.add(model);
        holder.message.setText(messagesList.get(position).getUserMessage());
        holder.user.setText(messagesList.get(position).getUserLogin());

    }

    @NonNull
    @Override
    public ChatHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.chat_message, parent, false);
        return new ChatHolder(this,view);
    }


    static class ChatHolder extends RecyclerView.ViewHolder {

        @BindView(R.id.user_message)
        TextView message;
        @BindView(R.id.user_login)
        TextView user;

        private ChatHolder(ChatMessagesAdapter chatMessagesAdapter, View itemView) {
            super(itemView);
            ButterKnife.bind(this, itemView);

        }
    }

}

And it works perfectly fine. I've based that on this example: https://www.programcreek.com/java-api-examples/?code=panzerama/Dispatch/Dispatch-master/app/src/main/java/com/indexyear/jd/dispatch/activities/DispatchTeamActivity.java

So if you would have any problem with whole FirebaseUI thing, the above is proper example in my opinion.

Thanks to @Alex Malmo and @Yupi for suggestions!