I have a NavigationDrawer activity which is using fragments. In one of my fragments I want to load data from the Firebase database to a RecyclerView.
So I have an ArrayList for the objects I have stored in the database. Then I add a ValueEventListener to my database reference where I add the objects from the database to the array list. And then I give the ArrayList to the RecyclerView Adapter which should create the list with my objects.
When I use addListenerForSingleValueEvent it is never working, I always get an empty RecyclerView. When I use addValueEventListener it is working neither at the start, but when I switch the orientation of my phone to landscape the data suddenly appears. And if I go back to the normal orientation it is still there. But not at the start when I open the fragment.
Here is my code of the fragment class:
public class LogFragment extends Fragment {
private RecyclerView recyclerView;
private RecyclerView.Adapter rvAdapter;
private RecyclerView.LayoutManager rvLayoutManager;
private ArrayList<LogEntry> entries;
private DatabaseReference databaseReference;
private DatabaseReference logReference;
private FirebaseAuth firebaseAuth;
private FirebaseUser firebaseUser;
public LogFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_log, container, false);
databaseReference = FirebaseDatabase.getInstance().getReference();
firebaseAuth = FirebaseAuth.getInstance();
firebaseUser = firebaseAuth.getCurrentUser();
loadEntries();
recyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerView);
rvLayoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(rvLayoutManager);
rvAdapter = new RvAdapter(entries);
recyclerView.setAdapter(rvAdapter);
// Inflate the layout for this fragment
return rootView;
}
private void loadEntries(){
entries = new ArrayList<>();
logReference = databaseReference.child("log").child(firebaseUser.getUid());
ValueEventListener valueEventListener = new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot ds : dataSnapshot.getChildren()){
entries.add(ds.getValue(LogEntry.class));
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
Log.w("LogFragment", "loadLog:onCancelled", databaseError.toException());
}
};
logbuchReference.addValueEventListener(valueEventListener);
}
}
And here is my RvAdapter.java:
public class RvAdapter extends RecyclerView.Adapter<RvAdapter.MyViewHolder> {
private ArrayList<LogEntry> entries;
public RvAdapter(ArrayList<LogEntry> entries){
this.entries = entries;
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.log_entry_item, null);
return new MyViewHolder(itemView);
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy");
SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm");
holder.tvAction.setText(entries.get(position).action);
holder.tvDate.setText(dateFormat.format(entries.get(position).date)+"\n"+timeFormat.format(entries.get(position).date));
holder.ivIcon.setImageResource(entries.get(position).icon);
holder.itemView.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
// Do something
}
});
}
@Override
public int getItemCount() {
return entries.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
TextView tvAction, tvDate;
ImageView ivIcon;
public MyViewHolder(View itemView) {
super(itemView);
tvAction = (TextView) itemView.findViewById(R.id.tvAction);
tvDate = (TextView) itemView.findViewById(R.id.tvDate);
ivIcon = (ImageView) itemView.findViewById(R.id.ivIcon);
}
}
}
And here my LogEntry.java:
public class LogEntry {
public String action;
public Date date;
public int icon;
public String message;
public LogEntry() {}
public LogEntry(String action, Date date, int icon, String message) {
this.action = action;
this.date = date;
this.icon = icon;
this.message = message;
}
}
Someone know what could be the problem?
EDIT:
When I System.out.prinln the ArrayList right after the for() I get the data. So loading the data from the database is working. But presenting the data is not working.
Firebase is asynchronous. Set or notify the adapter from within the callback.
ValueEventListener valueEventListener = new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot ds : dataSnapshot.getChildren()){
entries.add(ds.getValue(LogEntry.class));
}
// Declare adapter and set here
// OR... adapter.notifyDataSetChanged();
}
@Override
public void onCancelled(DatabaseError databaseError) {
Log.w("LogFragment", "loadLog:onCancelled", databaseError.toException());
}
};
In more detail,
loadEntries(); // ... Doing stuff in the background
recyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerView);
rvLayoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(rvLayoutManager);
rvAdapter = new RvAdapter(entries); // This is still empty, probably
recyclerView.setAdapter(rvAdapter);
// entries.add() called later, but adapter never notified
You can use https://github.com/firebase/FirebaseUI-Android to solve this problem.
follow a little code to help you...
ListView messagesView = (ListView) findViewById(R.id.messages_list);
DatabaseReference ref = FirebaseDatabase.getInstance().getReference();
mAdapter = new FirebaseListAdapter<Chat>(this, Chat.class, android.R.layout.two_line_list_item, ref) {
@Override
protected void populateView(View view, Chat chatMessage, int position) {
((TextView)view.findViewById(android.R.id.text1)).setText(chatMessage.getName());
((TextView)view.findViewById(android.R.id.text2)).setText(chatMessage.getText());
}
};
messagesView.setAdapter(mAdapter);
Good luck!!!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With