Title: What is a good way to recyle views in a ListView from a Firebase Database
Tags: android;listview;firebase-realtime-database;baseadapter;android-recyclerview
Question: Objective: I have a ListView with a BaseAdapter I want to populate with values from my Firebase Database.
This is my BaseAdapter I named MessagesAdapter.java:
public class MessagesAdapter extends BaseAdapter {
private Context context;
private List<Message&gt; messages;
private LayoutInflater inflater;
boolean showMoreClicked = false;
public MessagesAdapter(Context context, List<Message&gt; messages) {
this.context = context;
this.messages = messages;
inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
@Override
public int getCount() {
return messages.size();
@Override
public Object getItem(int position) {
return null;
@Override
public long getItemId(int position) {
return 0;
public View getView(final int position, final View convertView,final ViewGroup parent) {
View itemView = convertView;
MessagesAdapter.ViewHolder holder;
if (convertView == null) {
itemView = inflater.inflate(R.layout.item_message, null);
holder = new ViewHolder();
// set image based on selected text
holder.message = itemView.findViewById(R.id.tv_msg);
holder.showMoreBtn = itemView.findViewById(R.id.bt_show_more);
holder.datetime = itemView.findViewById(R.id.tv_ride_time);
itemView.setTag(holder);
} else
holder = (MessagesAdapter.ViewHolder) itemView.getTag();
final Message msg = messages.get(position);
/* editing item views */
holder.datetime.setText(msg.getDatetime());
holder.message.setText(msg.getContent());
holder.showMoreBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context, "All Message: "+msg.getContent(), Toast.LENGTH_SHORT).show();
showMoreClicked = !showMoreClicked;
}
});
DatabaseReference profileRef = FirebaseDatabase.getInstance().getReference("profile");
profileRef.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
Profile sender = dataSnapshot.child(msg.getSender_id()+"").getValue(Profile.class);
ViewHolder h = new ViewHolder();
h.userImg = parent.getChildAt(position).findViewById(R.id.iv_profile_image);
h.userName = parent.getChildAt(position).findViewById(R.id.tv_sender_name);
h.userName.setText(sender.getFirstname()+" "+sender.getLastname()); /*** Error shows up here ***/
getImageFromDB(sender.getPicpath(), h.userImg);
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
});
return itemView;
private static class ViewHolder {
CircleImageView userImg;
TextView userName;
TextView datetime;
TextView message;
Button showMoreBtn;
private void getImageFromDB(String firebasePath, ImageView imageView) {
StorageReference mStorageRef = FirebaseStorage.getInstance()
.getReferenceFromUrl("gs://iveridemap.appspot.com")
.child(firebasePath);
// Load the image using Glide
Glide.with(context)
.using(new FirebaseImageLoader()) // cannot resolve method using!
.load(mStorageRef)
.into(imageView);
This adapter is used to fetch messages sent to the current user from firebase and show them in my ListView.
I instantiate this BaseAdapter and ListView inside a fragment I called MessagesFragment.java:
public class MessagesAdapter extends BaseAdapter {
private Context context;
private List<Message&gt; messages;
private LayoutInflater inflater;
boolean showMoreClicked = false;
public MessagesAdapter(Context context, List<Message&gt; messages) {
this.context = context;
this.messages = messages;
inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
@Override
public int getCount() {
return messages.size();
@Override
public Object getItem(int position) {
return null;
@Override
public long getItemId(int position) {
return 0;
public View getView(final int position, final View convertView,final ViewGroup parent) {
View itemView = convertView;
MessagesAdapter.ViewHolder holder;
if (convertView == null) {
itemView = inflater.inflate(R.layout.item_message, null);
holder = new ViewHolder();
// set image based on selected text
holder.message = itemView.findViewById(R.id.tv_msg);
holder.showMoreBtn = itemView.findViewById(R.id.bt_show_more);
holder.datetime = itemView.findViewById(R.id.tv_ride_time);
itemView.setTag(holder);
} else
holder = (MessagesAdapter.ViewHolder) itemView.getTag();
final Message msg = messages.get(position);
/* editing item views */
holder.datetime.setText(msg.getDatetime());
holder.message.setText(msg.getContent());
holder.showMoreBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context, "All Message: "+msg.getContent(), Toast.LENGTH_SHORT).show();
showMoreClicked = !showMoreClicked;
}
});
DatabaseReference profileRef = FirebaseDatabase.getInstance().getReference("profile");
profileRef.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
Profile sender = dataSnapshot.child(msg.getSender_id()+"").getValue(Profile.class);
ViewHolder h = new ViewHolder();
h.userImg = parent.getChildAt(position).findViewById(R.id.iv_profile_image);
h.userName = parent.getChildAt(position).findViewById(R.id.tv_sender_name);
h.userName.setText(sender.getFirstname()+" "+sender.getLastname());
getImageFromDB(sender.getPicpath(), h.userImg);
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
});
return itemView;
private static class ViewHolder {
CircleImageView userImg;
TextView userName;
TextView datetime;
TextView message;
Button showMoreBtn;
private void getImageFromDB(String firebasePath, ImageView imageView) {
StorageReference mStorageRef = FirebaseStorage.getInstance()
.getReferenceFromUrl("gs://iveridemap.appspot.com")
.child(firebasePath);
// Load the image using Glide
Glide.with(context)
.using(new FirebaseImageLoader()) // cannot resolve method using!
.load(mStorageRef)
.into(imageView);
Problem: When I scroll down my ListView my code returns a NullPointerException at the line:
h.userName.setText(sender.getFirstname()+" "+sender.getLastname());
In the BaseAdapter, I pointed it out in the code above. Apprently something is wrong with initiating my profile instance I fetch from Firebase.
More Info: This error doesn't show up when there is fewer items in the list and I don't have to scroll down..
I am ready to post more code upon demand, if anyone needs to see my XML file or database structure just say so!
Thanks alot for your time.
Comment: @Pavneet_Singh didn't work


Here is another answer: I recommend using a RecyclerView, to use firebase items there is some great libraries to make everything more simple
Here is a great library to parcel your items
compile 'org.parceler:parceler-api:1.1.9'
With parceler you can make your activity like this example
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.Menu;
import android.view.MenuItem;
import com.firebase.client.Firebase;
import com.firebase.client.Query;
import org.parceler.Parcels;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
private final static String SAVED_ADAPTER_ITEMS = "SAVED_ADAPTER_ITEMS";
private final static String SAVED_ADAPTER_KEYS = "SAVED_ADAPTER_KEYS";
private Query mQuery;
private MyAdapter mMyAdapter;
private ArrayList<MyItem&gt; mAdapterItems;
private ArrayList<String&gt; mAdapterKeys;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
handleInstanceState(savedInstanceState);
setupFirebase();
setupRecyclerview();
// Restoring the item list and the keys of the items: they will be passed to the adapter
private void handleInstanceState(Bundle savedInstanceState) {
if (savedInstanceState != null &amp;&amp;
savedInstanceState.containsKey(SAVED_ADAPTER_ITEMS) &amp;&amp;
savedInstanceState.containsKey(SAVED_ADAPTER_KEYS)) {
mAdapterItems = Parcels.unwrap(savedInstanceState.getParcelable(SAVED_ADAPTER_ITEMS));
mAdapterKeys = savedInstanceState.getStringArrayList(SAVED_ADAPTER_KEYS);
} else {
mAdapterItems = new ArrayList<MyItem&gt;();
mAdapterKeys = new ArrayList<String&gt;();
}
private void setupFirebase() {
Firebase.setAndroidContext(this);
String firebaseLocation = getResources().getString(R.string.firebase_location);
mQuery = new Firebase(firebaseLocation);
private void setupRecyclerview() {
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
mMyAdapter = new MyAdapter(mQuery, MyItem.class, mAdapterItems, mAdapterKeys);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(mMyAdapter);
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
@Override
public boolean onOptionsItemSelected(MenuItem item) {
return item.getItemId() == R.id.action_settings || super.onOptionsItemSelected(item);
// Saving the list of items and keys of the items on rotation
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable(SAVED_ADAPTER_ITEMS, Parcels.wrap(mMyAdapter.getItems()));
outState.putStringArrayList(SAVED_ADAPTER_KEYS, mMyAdapter.getKeys());
@Override
protected void onDestroy() {
super.onDestroy();
mMyAdapter.destroy();
Here is the item Model
import org.parceler.Parcel;
@Parcel
public class MyItem {
String name;
long age;
public MyItem() {
public String getName() {
return name;
public void setName(String name) {
this.name = name;
public long getAge() {
return age;
public void setAge(long age) {
this.age = age;
Here is the adapter
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.firebase.client.Query;
import java.util.ArrayList;
import java.util.List;
public class MyAdapter extends FirebaseRecyclerAdapter<MyAdapter.ViewHolder, MyItem&gt; {
public static class ViewHolder extends RecyclerView.ViewHolder {
TextView textViewName;
TextView textViewAge;
public ViewHolder(View view) {
super(view);
textViewName = (TextView) view.findViewById(R.id.textview_name);
textViewAge = (TextView) view.findViewById(R.id.textview_age);
}
public MyAdapter(Query query, @Nullable ArrayList<MyItem&gt; items,
@Nullable ArrayList<String&gt; keys) {
super(query, items, keys);
@Override public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item, parent, false);
return new ViewHolder(view);
@Override public void onBindViewHolder(MyAdapter.ViewHolder holder, int position) {
MyItem item = getItem(position);
holder.textViewName.setText(item.getName());
holder.textViewAge.setText(String.valueOf(item.getAge()));
@Override protected void itemAdded(MyItem item, String key, int position) {
Log.d("MyAdapter", "Added a new item to the adapter.");
@Override protected void itemChanged(MyItem oldItem, MyItem newItem, String key, int position) {
Log.d("MyAdapter", "Changed an item.");
@Override protected void itemRemoved(MyItem item, String key, int position) {
Log.d("MyAdapter", "Removed an item from the adapter.");
@Override protected void itemMoved(MyItem item, String key, int oldPosition, int newPosition) {
Log.d("MyAdapter", "Moved an item.");
And here is the adapter that is extending the main adapter so it can work with firebase
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.ViewGroup;
import com.google.firebase.database.ChildEventListener;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.Query;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
/**https://firebase.google.com/support/guides/firebase-android.
* <p&gt;
* This class is a generic way of backing an Android RecyclerView with a Firebase location.
* It handles all of the child events at the given Firebase location.
* It marshals received data into the given class type.
* Extend this class and provide an implementation of the abstract methods, which will notify when
* the adapter list changes.
* <p&gt;
* This class also simplifies the management of configuration change (e.g.: device rotation)
* allowing the restore of the list.
* @param <T&gt; The class type to use as a model for the data contained in the children of the
*            given Firebase location
public abstract class FirebaseRecyclerAdapter<ViewHolder extends RecyclerView.ViewHolder, T&gt; extends RecyclerView.Adapter<ViewHolder&gt; {
private Query mQuery;
private ArrayList<T&gt; mItems;
private ArrayList<String&gt; mKeys;
/**
* @param query The Firebase location to watch for data changes.
*              Can also be a slice of a location, using some combination of
*              <code&gt;limit()</code&gt;, <code&gt;startAt()</code&gt;, and <code&gt;endAt()</code&gt;.
*/
public FirebaseRecyclerAdapter(Query query) {
this(query, null, null);
/**
* @param query The Firebase location to watch for data changes.
*              Can also be a slice of a location, using some combination of
*              <code&gt;limit()</code&gt;, <code&gt;startAt()</code&gt;, and <code&gt;endAt()</code&gt;.
* @param items List of items that will load the adapter before starting the listener.
*              Generally null or empty, but this can be useful when dealing with a
*              configuration change (e.g.: reloading the adapter after a device rotation).
*              Be careful: keys must be coherent with this list.
* @param keys  List of keys of items that will load the adapter before starting the listener.
*              Generally null or empty, but this can be useful when dealing with a
*              configuration change (e.g.: reloading the adapter after a device rotation).
*              Be careful: items must be coherent with this list.
*/
public FirebaseRecyclerAdapter(Query query,
@Nullable ArrayList<T&gt; items,
@Nullable ArrayList<String&gt; keys) {
this.mQuery = query;
if (items != null &amp;&amp; keys != null) {
this.mItems = items;
this.mKeys = keys;
} else {
mItems = new ArrayList<T&gt;();
mKeys = new ArrayList<String&gt;();
}
query.addChildEventListener(mListener);
private ChildEventListener mListener = new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
String key = dataSnapshot.getKey();
if (!mKeys.contains(key)) {
T item = getConvertedObject(dataSnapshot);
int insertedPosition;
if (previousChildName == null) {
mItems.add(0, item);
mKeys.add(0, key);
insertedPosition = 0;
} else {
int previousIndex = mKeys.indexOf(previousChildName);
int nextIndex = previousIndex + 1;
if (nextIndex == mItems.size()) {
mItems.add(item);
mKeys.add(key);
} else {
mItems.add(nextIndex, item);
mKeys.add(nextIndex, key);
}
insertedPosition = nextIndex;
}
notifyItemInserted(insertedPosition);
itemAdded(item, key, insertedPosition);
}
}
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
String key = dataSnapshot.getKey();
if (mKeys.contains(key)) {
int index = mKeys.indexOf(key);
T oldItem = mItems.get(index);
T newItem = getConvertedObject(dataSnapshot);
mItems.set(index, newItem);
notifyItemChanged(index);
itemChanged(oldItem, newItem, key, index);
}
}
@Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
String key = dataSnapshot.getKey();
if (mKeys.contains(key)) {
int index = mKeys.indexOf(key);
T item = mItems.get(index);
mKeys.remove(index);
mItems.remove(index);
notifyItemRemoved(index);
itemRemoved(item, key, index);
}
}
@Override
public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
String key = dataSnapshot.getKey();
int index = mKeys.indexOf(key);
T item = getConvertedObject(dataSnapshot);
mItems.remove(index);
mKeys.remove(index);
int newPosition;
if (previousChildName == null) {
mItems.add(0, item);
mKeys.add(0, key);
newPosition = 0;
} else {
int previousIndex = mKeys.indexOf(previousChildName);
int nextIndex = previousIndex + 1;
if (nextIndex == mItems.size()) {
mItems.add(item);
mKeys.add(key);
} else {
mItems.add(nextIndex, item);
mKeys.add(nextIndex, key);
}
newPosition = nextIndex;
}
notifyItemMoved(index, newPosition);
itemMoved(item, key, index, newPosition);
}
@Override
public void onCancelled(DatabaseError databaseError) {
Log.e("FirebaseListAdapter", "Listen was cancelled, no more updates will occur.");
}
};
@Override
public abstract ViewHolder onCreateViewHolder(ViewGroup parent, int viewType);
@Override
public abstract void onBindViewHolder(ViewHolder holder, final int position);
@Override
public int getItemCount() {
return (mItems != null) ? mItems.size() : 0;
/**
* Clean the adapter.
* ALWAYS call this method before destroying the adapter to remove the listener.
*/
public void destroy() {
mQuery.removeEventListener(mListener);
/**
* Returns the list of items of the adapter: can be useful when dealing with a configuration
* change (e.g.: a device rotation).
* Just save this list before destroying the adapter and pass it to the new adapter (in the
* constructor).
*
* @return the list of items of the adapter
*/
public ArrayList<T&gt; getItems() {
return mItems;
/**
* Returns the list of keys of the items of the adapter: can be useful when dealing with a
* configuration change (e.g.: a device rotation).
* Just save this list before destroying the adapter and pass it to the new adapter (in the
* constructor).
*
* @return the list of keys of the items of the adapter
*/
public ArrayList<String&gt; getKeys() {
return mKeys;
/**
* Returns the item in the specified position
*
* @param position Position of the item in the adapter
* @return the item
*/
public T getItem(int position) {
return mItems.get(position);
/**
* Returns the position of the item in the adapter
*
* @param item Item to be searched
* @return the position in the adapter if found, -1 otherwise
*/
public int getPositionForItem(T item) {
return mItems != null &amp;&amp; mItems.size() &gt; 0 ? mItems.indexOf(item) : -1;
/**
* Check if the searched item is in the adapter
*
* @param item Item to be searched
* @return true if the item is in the adapter, false otherwise
*/
public boolean contains(T item) {
return mItems != null &amp;&amp; mItems.contains(item);
/**
* ABSTRACT METHODS THAT MUST BE IMPLEMENTED BY THE EXTENDING ADAPTER.
*/
/**
* Called after an item has been added to the adapter
*
* @param item     Added item
* @param key      Key of the added item
* @param position Position of the added item in the adapter
*/
protected void itemAdded(T item, String key, int position) {
/**
* Called after an item changed
*
* @param oldItem  Old version of the changed item
* @param newItem  Current version of the changed item
* @param key      Key of the changed item
* @param position Position of the changed item in the adapter
*/
protected void itemChanged(T oldItem, T newItem, String key, int position) {
/**
* Called after an item has been removed from the adapter
*
* @param item     Removed item
* @param key      Key of the removed item
* @param position Position of the removed item in the adapter
*/
protected void itemRemoved(T item, String key, int position) {
/**
* Called after an item changed position
*
* @param item        Moved item
* @param key         Key of the moved item
* @param oldPosition Old position of the changed item in the adapter
* @param newPosition New position of the changed item in the adapter
*/
protected void itemMoved(T item, String key, int oldPosition, int newPosition) {
/**
* Converts the data snapshot to generic object
*
* @param snapshot Result
* @return Data converted
*/
protected T getConvertedObject(DataSnapshot snapshot) {
return snapshot.getValue(getGenericClass());
/**
* Returns a class reference from generic T.
*/
@SuppressWarnings("unchecked")
private Class<T&gt; getGenericClass() {
return (Class<T&gt;) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[1];
Useful links:
Generic Firebase recyclerview list for Android
Hope it helps
Comment for this answer: Hey I'm sorry I didn't answer sooner I took a week off.
This seems good except that I dont know how to set it all up for a fragment not an activity
Comment for this answer: how to generate the com.firebase.client stuff? which dependency shall I use in the gradle??

{'source': 'StackExchangeDataset', 'subset': 'stackoverflow.com', 'metadata': {'AnswerCount': 1, 'CommentCount': 1, 'ContentLicense': 'CC BY-SA 3.0', 'CreationDate': '2017:00:09/14/17', 'FavoriteCount': None, 'PostTypeId': 1, 'Tags': '<android><listview><firebase-realtime-database><baseadapter><android-recyclerview>', 'Title': 'What is a good way to recyle views in a ListView from a Firebase Database', 'ViewCount': 108, 'id': 46225687, 'user_AboutMe': '<p>Graduate developer. We live to learn.</p>\n', 'user_AccountId': 4514540.0, 'user_DisplayName': 'siriuseteor77', 'user_Reputation': 994.0, 'user_Views': 160.0, 'user_id': 3669770.0}}