Simple thing I would like to do (see in the picture)
Display a view with info coming from 2 different places in Firebase so that it behaves in a professional way scrolling UP and DOWN
I have a list of movies and on each of them I would like the user to specify a rating and see it
In DB I created 2 structures to have the list of movies on one side and the ratings per user on the other
Problem using FirebaseRecyclerAdapter
My problem is that scrolling fast up and down the list, the visualization of the information coming from the second reference (the rating) is loaded on a different time (asynchronous call) and this is not acceptable to see this (little) delay building the view. Is this a limitation of FirebaseRecyclerView?
Because viewHolders are reused in the recycleView I reset and reload each time in populateView() the rating values and this doesn't help. Once retrieved I'm oblidged to get them again if the user scroll the view (see the setOnlistener in populateView()
Setting a listener in populateView cause also to have as many listener as the number of times populateView() is executed (if you scroll UP and DOWN it's many times).
Solutions / Workaround ?
Is there a correct way to do it preventing the problem? Or is it a limitation?
What about performance with my implementation where the listener is inside populateView() and there are MANY listener created?
Below some things I'm thinking on:
Prevent viewHolders to be recycled and just load once?
Override some other methods of RecyclerView? I tried with parseSnapshot() but it's the same problem...
Change the DB structure to have all the info in one list (I don't think it's the good one because it means adding rating information of each user to movie list)
Add a loading spinner on the rating part so that the rating is displayed only when the asyncrhonous call to firebase is completed (don't like it) without the today effect of: "changing star color in front of the user".
My Implementation
From FirebaseRecyclerAdapter
#Override
protected void populateViewHolder(final MovieViewHolder viewHolder, final Movie movie, final int position) {
String movieId = this.getRef(position).getKey();
// Oblidged to show no rating at the beginning because otherwise
// if a viewHolder is reused it has the values from another movie
viewHolder.showNoRating();
//---------------------------------------------
// Set values in the viewHolder from the model
//---------------------------------------------
viewHolder.movieTitle.setText(movie.getTitle());
viewHolder.movieDescription.setText(movie.getDescription());
//-----------------------------------------------------
// Ratings info are in another DB location... get them
// but call is asynchronous so PROBLEM when SCROLLING!
//-----------------------------------------------------
DatabaseReference ratingMovieRef = mDbRef.child(Constants.FIREBASE_LOCATION_RATINGS).child(currentUserId).child(movieId);
ratingQuoteRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
RatingMovie ratingMovie = dataSnapshot.getValue(RatingMovie.class);
Rating rating = Rating.NO_RATING;
if (ratingMovie != null) {
rating = Rating.valueOf(ratingMovie.getRating());
}
// Set the rating in the viewholder (through anhelper method)
viewHolder.showActiveRating(rating);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
from MovieViewHolder
public class QuoteViewHolder extends RecyclerView.ViewHolder {
public CardView cardView;
public TextView movieTitle;
public TextView movieDescription;
public ImageView ratingOneStar;
public ImageView ratingTwoStar;
public ImageView ratingThreeStar;
public QuoteViewHolder(View itemView) {
super(itemView);
movieTitle = (TextView)itemView.findViewById(R.id.movie_title);
movieDescription = (TextView)itemView.findViewById(R.id.movie_descr);
// rating
ratingOneStar = (ImageView)itemView.findViewById(R.id.rating_one);
ratingTwoStar = (ImageView)itemView.findViewById(R.id.rating_two);
ratingThreeStar = (ImageView)itemView.findViewById(R.id.rating_three);
}
/**
* Helper to show the color on stars depending on rating value
*/
public void showActiveRating(Rating rating){
if (rating.equals(Rating.ONE)) {
// just set the good color on ratingOneStar and the others
...
}
else if (rating.equals(Rating.TWO)) {
// just set the good color
...
} else if (rating.equals(Rating.THREE)) {
// just set the good color
...
}
/**
* Initialize the rating icons to unselected.
* Important because the view holder can be reused and if not initalised values from other moviecan be seen
*/
public void initialiseNoRating(){
ratingOneStar.setColorFilter(ContextCompat.getColor(itemView.getContext(), R.color.light_grey));
ratingTwoStar.setColorFilter(....
ratingThreeStar.SetColorFilter(...
}
You can sort of cache the ratings using a ChildEventListener. Basically just create a separat one just for the Ratings node, and have it store the ratings in a Map. Then using the RecyclerAdapter you will retrieve from the Map if the rating is available, if it is not, have the rating listener update the recyclerview as soon as is has downloaded the rating. This is one strategy you could go about, doing it, you will have to manually copy/paste some classes from the FirebaseUI library and set some fields public for this to work.
Usage would be something like this
private MovieRatingConnection ratingConnection;
// inside onCreate
ratingConnection = new MovieRatingConnection(userId, new MovieRatingConnection.RatingChangeListener() {
#Override
public void onRatingChanged(DataSnapshot dataSnapshot) {
if (recyclerAdapter != null) {
if (dataSnapshot != null) {
int index = recyclerAdapter.snapshots.getIndexForKey(dataSnapshot.getKey());
recyclerAdapter.notifyItemChanged(index);
}
}
}
});
Query movieQuery = FirebaseDatabase.getInstance().getReference().child("Movies");
recyclerAdapter = new FirebaseRecyclerAdapter(movieQuery...) {
#Override
public void populateViewHolder(RecyclerView.ViewHolder viewHolder, Object model, int position) {
//...
final String key = getRef(position).getKey();
viewHolder.showActiveRating(ratingConnection.getRating(key));
}
};
and MovieRatingConnection would be a class like this
public class MovieRatingConnection {
private MovieRatingListener listener;
public MovieRatingConnection(String userId, RatingChangeListener changeListener) {
Query query = FirebaseDatabase.getInstance().getReference().child("MovieRatings").child(userId);
listener = new MovieRatingListener(query, changeListener);
}
public Rating getRating(String key) {
return listener.getRating(key);
}
public void cleanup() {
if (listener != null) {
listener.unregister();
}
}
public static class MovieRatingListener implements ChildEventListener {
public interface RatingChangeListener {
public void onRatingChanged(DataSnapshot snapshot);
}
private Query query;
private HashMap<String, Rating> ratingMap = new HashMap<>();
private RatingChangeListener changeListener;
public MovieRatingListener(Query query, RatingChangeListener changeListener) {
this.query = query;
this.changeListener = changeListener;
query.addChildEventListener(this);
}
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
if (dataSnapshot != null) {
ratingMap.put(dataSnapshot.getKey(), dataSnapshot.getValue(Rating.class));
changeListener.onRatingChanged(dataSnapshot);
}
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
if (dataSnapshot != null) {
ratingMap.put(dataSnapshot.getKey(), dataSnapshot.getValue(Rating.class));
changeListener.onRatingChanged(dataSnapshot);
}
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
ratingMap.remove(dataSnapshot.getKey());
changeListener.onRatingChanged(null);
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
public Rating getRating(String key) {
if (ratingMap.get(key) != null) {
return ratingMap.get(key);
} else {
return new Rating(); // default value/null object
}
}
public void unregister() {
query.removeEventListener(this);
}
}
}
Related
As refer in this link. I want to try in my coding apps. But it didnt retrieve any data from my firebase database. I just cant figure out how to fix it. Can someone please help me. Is there any idea, where did I miss?
My firebase database as show image below:-
Spinner coding:-
mDatabase = FirebaseDatabase.getInstance().getReference();
mDatabase.keepSynced(true);
mDatabase.child("Advertisement").child(mAuth.getUid()).addValueEventListener(new ValueEventListener()
{
#Override
public void onDataChange(DataSnapshot dataSnapshot)
{
//final List<String> name = new ArrayList<String>();
ArrayList<String> identity = new ArrayList<>();
identity.add(0, "Choose Tuition Centre");
for (DataSnapshot nameSnapshot: dataSnapshot.getChildren())
{
String tuitionName = nameSnapshot.child("adstuitioname").getValue(String.class);
identity.add(tuitionName);
}
ArrayAdapter<String> nameAdapter = new ArrayAdapter<String>(RatingActivity.this, android.R.layout.simple_spinner_item, identity);
nameAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mName.setAdapter(nameAdapter);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
mName.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener()
{
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id)
{
if(position==0)
{
//Toast.makeText(getApplicationContext(),"No Item Selected",Toast.LENGTH_LONG).show();
}
else
{
Toast.makeText(getApplicationContext(),parent.getItemAtPosition(position) +" Selected",Toast.LENGTH_SHORT).show();
}
}
#Override
public void onNothingSelected(AdapterView<?> parent)
{
// TODO Auto-generated method stub
}
});
Add listener as below :
DatabaseReference mDatabaseRef =
FirebaseDatabase.getInstance().getReference();
ArrayList<String> list=new ArrayList<>();
mDatabaseRef.child("Advertisement")
.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot snapshot :dataSnapshot.getChildren())
{
Advertisementject advertisement=
snapshot.getValue(Advertisement.class);
//replace Object.class with your class name
//get your key value here from your "Custom class"
// which contains "adstutioname"
// add in list
list.add(advertisement.getAdstutuioname());
}
//set adapter
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
generate class like this
public class Advertisement{
#SerializedName("adstutuioname")
#Expose
private String adstutuioname;
public String getAdstutuioname() {
return adstutuioname;
}
public void setAdstutuioname(String adstutuioname) {
this.adstutuioname = adstutuioname;
}
}
Your class contains all your params and .
Replace Object with this class
I hope the problem is with your getValue() method. Firebase getValue method returns an Object, and you are expecting a String. Please modify this line to convert your Object to String -
String tuitionName = nameSnapshot.child("adstuitioname").getValue(String.class).toString();
It's not clear if you're trying to read once, or watch changes Difference between addValueEventListener() and addListenerForSingleValueEvent() of firebase
First things first, look if you are getting a database error
#Override
public void onCancelled(DatabaseError databaseError) {
// Toast or print the databaseError, don't ignore it
}
Then look where you're adding a listener - mDatabase.child("Advertisement").child(mAuth.getUid())., which from your picture is every element at the level of -LNZ6....
For that level, you have a list of object's, so you can need to use a loop, and as shown from some examples on Firebase site, it's recommended that you make an object, but you still can use child method for a certain field as you're trying to do
But if a field doesn't exist, you get null value, and Adapters cannot hold nulls, so you must ignore or set some default value.
ArrayList<String> identity = new ArrayList<>();
identity.add(0, "Choose Tuition Centre");
for (DataSnapshot nameSnapshot: dataSnapshot.getChildren()) {
String tuitionName = nameSnapshot.child("adstuitioname").getValue(String.class);
Log.d("DEBUG TUITION", String.valueOf(tuitionName); // look for this in your logs
identity.add(tuitionName == null ? "none" : tuitionName);
}
ArrayAdapter<String> nameAdapter =...
I'm using firebase for chat app and I'm implementing to fetch messages from firebase database and update recycler view.
When I click the "send" button, it acts like below.
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
final ChatModel.Comment comment = new ChatModel.Comment();
comment.uid = uid;
comment.message = editText.getText().toString();
FirebaseDatabase.getInstance().getReference().child("chatrooms")
.child(chatroomUid).push().setValue(comment).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
FirebaseDatabase.getInstance().getReference().child("chatrooms")
.child(chatroomUid).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
comments.clear();
for(DataSnapshot item : dataSnapshot.getChildren()) {
if(item.getKey().compareTo("users")!=0) {
comments.add(item.getValue(ChatModel.Comment.class));
}
}
adapter.notifyDataSetChanged();
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
});
}
});
Process is like this.
Store message data into firebase database using "Comment" object form.
If stage 1 is succeeded, fetch that message and call notifyDataSetChanged()
But it is not working, I have global adpater object and above method is called by this variable.
Adapter code is like below.
private class messageRecyclerveiwAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_message, viewGroup, false);
return new MessageViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder viewHolder, int i) {
((MessageViewHolder)viewHolder).textView.setText(comments.get(i).message);
}
#Override
public int getItemCount() {
return comments.size();
}
}
How can I fix it?
You need to create an object of your Adapter in the same class as you have the setOnClickListener:
Something like this:
final MessageRecyclerViewAdapter adapter = new MessageRecyclerViewAdapter(getActivity(), messageArrayList);
Then you need to pass the adapter to the method you're using to get the messages. Right now, you're calling notifyDataSetChanged() in the adapter which is wrong. First you need a specific method to get those messages from Firebase. For example:
public void getLiveChatMessages(final ArrayList<ChatMessageClass> messageArrayList, final MessageRecyclerViewAdapter adapter) {
I'm guessing you have a class that returns different part of the messages.. That's what I've named "ChatMessageClass" here, but you may call it something else. Within this method you get the messages from Firebase, then afterward you call as the last thing in the method:
adapter.notifyDataSetChanged();}
within the getLiveChatMessages method. Here you also pass in the adapter so that you can use it. This is the way to call the notifyDataSetChanged() within the same method as where you get the messages.
Good luck!
I'm a long-time follower, first-time poster, so I hope I get this right. I'm trying to get into android-firebase development, and figured that the best way for that was to pick a project and try to find my way. I've been trying to develop a simple app for work (I'm a doctor) that'll allow our team to view brief progress notes about our in-patients, and will also allow us to post tasks needed to be performed on each patient. I've gotten most of it working, however, I'm stuck at one point. After I open a patient's record, A detail page loads, that includes a "comments" section, and a "tasks" section. Both of these are RecyclerViews containing many-to-one entries. The problem I'm facing has to do with trying to delete one of the "tasks" items when it's clicked (so it's removed from the list, and is no longer pending). I keep getting a Null Pointer Exception. The problem is, I'm not entirely sure if my code is correct. I'm still learning, so my code includes a lot of sections from different places that I've played with(I started with the android-firebase database sample from github, along with many other online sources), and re-written (after a lot of breaking and fixing) to get it working. Sorry for the long introduction. So, here's what I've got so far (not the complete code, just the sections I think are significant). I hope someone could help me out.
postDetailActivity.java
private static class TaskViewHolder extends RecyclerView.ViewHolder {
public TextView dateView;
public TextView bodyView;
public ImageButton checkView;
public TaskViewHolder(View itemView, final TaskAdapter mTaskAdapter) {
super(itemView);
dateView = (TextView) itemView.findViewById(R.id.task_date);
bodyView = (TextView) itemView.findViewById(R.id.task_body);
checkView = (ImageButton) itemView.findViewById(R.id.check);
checkView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d(TAG, "Adapter Position:" + getAdapterPosition());
mTaskAdapter.removeItem(getAdapterPosition());
}
});
}
private static class TaskAdapter extends RecyclerView.Adapter<TaskViewHolder> {
private Context mContext;
private DatabaseReference mDatabaseReference;
private DatabaseReference mTaskReference;
private ChildEventListener mChildEventListener;
private FirebaseRecyclerAdapter<Task, TaskViewHolder> mAdapter;
private List<String> mTaskIds = new ArrayList<>();
private List<Task> mTasks = new ArrayList<>();
public TaskAdapter(final Context context, DatabaseReference ref) {
mContext = context;
mDatabaseReference = ref;
// Create child event listener
// [START child_event_listener_recycler]
ChildEventListener childEventListener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
Log.d(TAG, "onChildAdded:" + dataSnapshot.getKey());
// A new task has been added, add it to the displayed list
Task task = dataSnapshot.getValue(Task.class);
// [START_EXCLUDE]
// Update RecyclerView
mTaskIds.add(dataSnapshot.getKey());
mTasks.add(task);
notifyItemInserted(mTasks.size() - 1);
// [END_EXCLUDE]
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) {
Log.d(TAG, "onChildChanged:" + dataSnapshot.getKey());
// A task has changed, use the key to determine if we are displaying this
// task and if so displayed the changed task.
Task newTask = dataSnapshot.getValue(Task.class);
String taskKey = dataSnapshot.getKey();
// [START_EXCLUDE]
int taskIndex = mTaskIds.indexOf(taskKey);
if (taskIndex > -1) {
// Replace with the new data
mTasks.set(taskIndex, newTask);
// Update the RecyclerView
notifyItemChanged(taskIndex);
} else {
Log.w(TAG, "onChildChanged:unknown_child:" + taskKey);
}
// [END_EXCLUDE]
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
Log.d(TAG, "onChildRemoved:" + dataSnapshot.getKey());
// A task has changed, use the key to determine if we are displaying this
// task and if so remove it.
String taskKey = dataSnapshot.getKey();
// [START_EXCLUDE]
int taskIndex = mTaskIds.indexOf(taskKey);
if (taskIndex > -1) {
// Remove data from the list
mTaskIds.remove(taskIndex);
mTasks.remove(taskIndex);
// Update the RecyclerView
notifyItemRemoved(taskIndex);
} else {
Log.w(TAG, "onChildRemoved:unknown_child:" + taskKey);
}
// [END_EXCLUDE]
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
Log.d(TAG, "onChildMoved:" + dataSnapshot.getKey());
// A task has changed position, use the key to determine if we are
// displaying this task and if so move it.
Task movedTask = dataSnapshot.getValue(Task.class);
String taskKey = dataSnapshot.getKey();
// ...
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "postTasks:onCancelled", databaseError.toException());
Toast.makeText(mContext, "Failed to load tasks.",
Toast.LENGTH_SHORT).show();
}
};
ref.addChildEventListener(childEventListener);
// [END child_event_listener_recycler]
// Store reference to listener so it can be removed on app stop
mChildEventListener = childEventListener;
}
#Override
public TaskViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(mContext);
View view = inflater.inflate(R.layout.item_task, parent, false);
return new TaskViewHolder(view, this);
}
#Override
public void onBindViewHolder(TaskViewHolder holder, int position) {
Task task = mTasks.get(position);
holder.dateView.setText(task.date);
holder.bodyView.setText(task.text);
}
#Override
public int getItemCount() {
return mTasks.size();
}
public void cleanupListener() {
if (mChildEventListener != null) {
mDatabaseReference.removeEventListener(mChildEventListener);
}
}
public void removeItem(int position) {
mAdapter.getRef(position).removeValue();
mAdapter.notifyItemRemoved(position);
Error Log
java.lang.NullPointerException: Attempt to invoke virtual method 'com.google.firebase.database.DatabaseReference com.firebase.ui.database.FirebaseRecyclerAdapter.getRef(int)' on a null object reference
at com.hanykasem.omfsrounds.PostDetailActivity$TaskAdapter.removeItem(PostDetailActivity.java:606)
at com.hanykasem.omfsrounds.PostDetailActivity$TaskViewHolder$1.onClick(PostDetailActivity.java:451)
at android.view.View.performClick(View.java:5198)
at android.view.View$PerformClick.run(View.java:21147)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Like I said, I'm not really sure where the problem is, and I've tried a lot to get it sorted out, but couldn't. Any help would be highly appreciated.
Thank you very much.
I am not expert in firebase, but from your code I do not see are you assigning anything to your mAdapter field. That is why you are having NPE later on when accessing it in your removeItem method.
Example on how firebase adapter can be used:
http://www.coderefer.com/firebaseui-android-firebase-database/
I'm trying to update a RecyclerView with information from Firebase. I've successfully been able to update the TextViews in my view from Firebase but, in doing so, my current code won't update the getChildCount() of the RecyclerView with the amount of children from the database without infinitely looping.
My current code: (CardAdapter.java)
public int getChildCount() {
mDatabase = FirebaseDatabase.getInstance();
mReference = mDatabase.getReference();
final String userID = FirebaseAuth.getInstance().getCurrentUser().getUid();
ValueEventListener listener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot snap :
dataSnapshot.child("pet").child("users").child(userID).getChildren()) {
mChildCount = (int) snap.getChildrenCount();
}
notifyDataSetChanged();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
mReference.addListenerForSingleValueEvent(listener);
return mChildCount;
}
This code works, but it infinitely loops due to the notifyDataSetChanged(); method that updates the RecyclerView.
What can I do to make it so the RecyclerView getChildCount() is dynamically updated from Firebase without infinitely looping?
You should not place your code to acquire data inside getChildrenCount(). I myself, usually place that in my Activity or inside constructor of Adapter. Like this:
public class YourAdapter extends RecyclerView.Adapter<YourAdapter.ViewHolder> {
private List<YourObject> yourObjectList;
public YourAdapter() {
ValueEventListener listener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot snap : dataSnapshot.child("pet").child("users").child(userID).getChildren()) {
...
// add to list
yourObjectList.add(snap.getValue(YourObject));
...
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
mReference.addListenerForSingleValueEvent(listener);
...
}
#Override
public void onBindViewHolder(YourAdapter.ViewHolder holder, int position) {
YourObject yourObject = yourObjectList.get(position);
...
}
#Override
public int getItemCount() {
return yourObjectList.size();
}
}
Hope this help.
I am having trouble retrieving a List from the Firebase. I have no trouble storing it, but as soon as I try to cast dataSnapshot.getValue() to ArrayList my app crashes, giving an exception:
HashMap cannot be casted to ArrayList
But when I tried to cast it to a HashMap, it also crashes, giving exception:
ArrayList can't be casted to hashmap
Need help please! Here is the code that is creating the problem:
Fire.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
List<TaskDes> td = (ArrayList<TaskDes>) dataSnapshot.getValue()
notifyDataSetChanged();
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
I want to retrieve all the data in the Firebase as one List. The class TaskDes contains three fields:
class TaskDes { // definition
boolean done
String taskDescription
String taskTitle
}
You need to create a GenericTypeIndicator object to pass as DataSnapshot.getValue() parameter.
Code:
GenericTypeIndicator<List<String>> t = new GenericTypeIndicator<List<String>>() {};
List<String> yourStringArray = dataSnapshot.getValue(t);
Your Model
public class TaskDes {
private boolean done;
private String taskDescription;
private String taskTitle;
public TaskDes() {
}
public boolean isDone() {
return done;
}
public void setDone(boolean done) {
this.done = done;
}
public String getTaskDescription() {
return taskDescription;
}
public void setTaskDescription(String taskDescription) {
this.taskDescription = taskDescription;
}
public String getTaskTitle() {
return taskTitle;
}
public void setTaskTitle(String taskTitle) {
this.taskTitle = taskTitle;
}
}
You need to create a GenericTypeIndicator object to pass as DataSnapshot.getValue() parameter.
In Activity
private static final String TAG=MainActivity.class.getSimpleName();
private FirebaseDatabase database;
private DatabaseReference myRef=null;
#Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
database=FirebaseDatabase.getInstance();
myRef=database.getReference("ADD_YOUR_REFERECE");
myRef.addValueEventListener(new ValueEventListener(){
#Override
public void onDataChange(DataSnapshot dataSnapshot){
/* This method is called once with the initial value and again whenever data at this location is updated.*/
long value=dataSnapshot.getChildrenCount();
Log.d(TAG,"no of children: "+value);
GenericTypeIndicator<List<TaskDes>> genericTypeIndicator =new GenericTypeIndicator<List<TaskDes>>(){};
List<TaskDes> taskDesList=dataSnapshot.getValue(genericTypeIndicator);
for(int i=0;i<taskDesList.size();i++){
Toast.makeText(MainActivity.this,"TaskTitle = "+taskDesList.get(i).getTaskTitle(),Toast.LENGTH_LONG).show();
}
}
#Override
public void onCancelled(DatabaseError error){
// Failed to read value
Log.w(TAG,"Failed to read value.",error.toException());
}
});
}
Make another item that contains a list for your item:
This is your item:
class TaskDes { // definition
boolean done
String taskDescription
String taskTitle
}
This is the list item
class TaskDesList { // definition
private ArreyList<TaskDes> yourlist
}
public TaskDesList(){
}
public ArrayList<TaskDes> getYourlist() {
return yourlist;
}
and when calling an EventListener
ref.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
yourlist.clear();
taskDesList=dataSnapshot.getValue(TaskDesList.class);
if (taskDesList!=null) {
yourlist= taskDesList.getYourlist();
}
}
and now "yourlist" is a list that contains all of your "TaskDes" items
A bit late, but in case any one else needs this.
IF the list is inside another object.
The object
public class Question {
public Date date;
public String questionNumber;
public String questionText;
public QuestionType questionType;
public String multipleSelection1;
public String multipleSelection2;
public String multipleSelection3;
public Question() {
// Default constructor required for calls to DataSnapshot.getValue(User.class)
}
}
Then to get your array of question objects
GenericTypeIndicator<List<Question>> t = new GenericTypeIndicator<List<Question>>() {};
List<Question> questionList = dataSnapshot.getValue(t);
Apparently, the GenericTypeIndicator doesn't work for all List objects particularly when the object contains none primitive types like maps. So, if it didn't work for your use case as it didn't for me, try this alternate solution:
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
List<TaskDes> tDlist = new ArrayList<>();
for (DataSnapshot d: dataSnapshot.getChildren()){
TaskDes tD = d.getValue(TaskDes.class);
tDlist.add(tD);
}
notifyDataSetChanged();
}
As mentioned in the previous answers make sure your class( like TaskDes in this case) has a public constructor which is empty so the getValue method can deserialize correctly to your java class.