I have two activities, one to create an Assessment object, and another to list the Assessment objects in Firebase, however I've encountered an issue: the POJO is able to serialize to JSON just fine, however the FirebaseRecyclerAdapter doesn't seem to be able to return the JSON data to the object type.
Here is my object file:
#IgnoreExtraProperties
public class Assessment {
private String company;
private String industry;
private String location;
private String product;
private String ownerId;
private String ownerName;
private boolean isDraft;
private boolean isQuickWalk;
private Map<String, Category> categories;
public Assessment() {
// Default constructor required for calls to DataSnapshot.getValue()
}
public Assessment(String companyName, String industryName, String locationName,
String productName, String ownerId, String ownerName, boolean isDraft, boolean isQuickWalk, Map<String, Category> categories) {
this.company = companyName;
this.industry = industryName;
this.location = locationName;
this.product = productName;
this.ownerId = ownerId;
this.ownerName = ownerName;
this.isDraft = isDraft;
this.isQuickWalk = isQuickWalk;
this.categories = categories;
}
public String getCompanyName() {
return company;
}
public String getIndustryName() {
return industry;
}
public String getLocationName() {
return location;
}
public String getProductName() {
return product;
}
public String getOwnerName() {
return ownerName;
}
public boolean getIsDraft() {
return isDraft;
}
public boolean getIsQuickWalk() {
return isQuickWalk;
}
Based on this, I use this code to write to Firebase and it all works fine:
String key = mDatabaseRef.child(FIREBASE_ASSESSMENTS).push().getKey();
Assessment assessment = new Assessment(editCompanyText, editLocationText,
editIndustryText, editProductText, uid,
currentUser.getEmail(), isDraft, isQuickWalk, null);
Map<String, Object> childUpdates = new HashMap<>();
childUpdates.put("/" + FIREBASE_ASSESSMENTS + "/" + key,
assessment);
mDatabaseRef.updateChildren(childUpdates);
However, when I try to read from the database I believe this is where the problem lies:
FirebaseRecyclerAdapter<Assessment, ViewDraftsViewHolder> adapter;
mRecyclerView = (RecyclerView) findViewById(R.id.draft_recycler_view);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
Query query = mEvalRef;
FirebaseRecyclerOptions<Assessment> options = new FirebaseRecyclerOptions.Builder<Assessment>()
.setQuery(query, Assessment.class)
.build();
adapter = new FirebaseRecyclerAdapter<Assessment, ViewDraftsViewHolder>(options) {
#Override
protected void onBindViewHolder(ViewDraftsViewHolder holder, int position, Assessment model) {
if(model.getIsDraft()) {
holder.companyName.setText(model.getCompanyName());
holder.industryName.setText(model.getIndustryName());
holder.locationName.setText(model.getLocationName());
holder.productName.setText(model.getProductName());
holder.ownerName.setText(model.getOwnerName());
if(model.getIsQuickWalk()) {
holder.isQuickWalk.setText("Quick Walkthrough");
} else {
holder.isQuickWalk.setText("Full Questionnaire");
}
}
}
#Override
public ViewDraftsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.completed_and_drafts_list_item, parent, false);
return new ViewDraftsViewHolder(view);
}
};
mRecyclerView.setAdapter(adapter);
private static class ViewDraftsViewHolder extends RecyclerView.ViewHolder {
TextView companyName;
TextView industryName;
TextView locationName;
TextView productName;
TextView ownerName;
TextView isQuickWalk;
public ViewDraftsViewHolder(View itemView) {
super(itemView);
companyName = (TextView) itemView.findViewById(R.id.companyView);
industryName = (TextView) itemView.findViewById(R.id.industryView);
locationName = (TextView) itemView.findViewById(R.id.locationView);
productName = (TextView) itemView.findViewById(R.id.productView);
ownerName = (TextView) itemView.findViewById(R.id.ownerName);
isQuickWalk = (TextView) itemView.findViewById(R.id.isQuickWalkView);
}
}
I'm not exactly sure where I'm going wrong that the FirebaseRecyclerAdapter is unable to convert the data back to an Assessment Object. Did i not set the object up correctly? Did I not set the RecyclerAdapter up correctly? This is how the data looks in Firebase
"-KzB39l1KH6MyEsgr6Ke" : {
"companyName" : "testing ",
"industryName" : "at",
"isDraft" : true,
"isQuickWalk" : true,
"locationName" : "five fifteen",
"ownerName" : "test#email.com",
"productName" : "please ignore"
},
any help you can give me would be excellent, I have been struggling with wrapping my head around this for a few days. Thanks!!
At this line:
Query query = mEvalRef;
I assume mEvalRef is a database reference, can you show it because its not clear how you query the data from the database.
just add (mEvalRef), in order to find the problem.
Edit
in your (Assessment) class , I noticed that there is no setter methods for the variable, add setter for each variable in the class and see if this helps.
EDIT 2
in your code where you try to read the values.
remove this
FirebaseRecyclerOptions<Assessment> options = new FirebaseRecyclerOptions.Builder<Assessment>()
.setQuery(query, Assessment.class)
.build();
and in this line or part of the adapter initialization:
adapter = new FirebaseRecyclerAdapter<Assessment, ViewDraftsViewHolder>(options)
remove the (options) parameter and change the above line of code to this:
adapter = new FirebaseRecyclerAdapter<Assessment, ViewDraftsViewHolder>(Assessment.class, R.layout.completed_and_drafts_list_item, ViewDraftsViewHolder.class, query )
next remove this code:
#Override
public ViewDraftsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.completed_and_drafts_list_item, parent, false);
return new ViewDraftsViewHolder(view);
}
and finally change the (private) to (public) in this:
private static class ViewDraftsViewHolder extends RecyclerView.ViewHolder {
and don't forget to keep the setters for variables in assessment class and make the variables public as I told you.
EDIT 3
now forget about EDIT 2 and retype your code the way you originally did it and just do this after you configured everything:
in the method where you initialize the adapter, add before this:
mRecyclerView.setAdapter(adapter);
this:
adapter.startListening();
Related
Context
I've been attempting to display data from Firestore in a FirestoreRecyclerAdapter, however this data is basically nested Maps, so a direct approach using the Query function is not ideal. Here is an image of my data structure:
Notice that ServiceOrder, client and vehicle are all Maps. In my Java code, ServiceOrder is made up of a Client and Vehicle objects.
So, if I were to use .setQuery(query, ServiceOrder.class), it would attempt to Map all of the data into ServiceOrder objects. But since my document is structured the way it is, that is not possible.
Issue
I suppose this could be fixed by mapping all documents into an object of a new class, similar to what is done here: https://medium.com/firebase-tips-tricks/how-to-map-an-array-of-objects-from-cloud-firestore-to-a-list-of-objects-122e579eae10.
Even though I can see how it could be done using a normal RecyclerView and using a custom adapter, could the same solution be used in FirestoreRecyclerAdapter? Because I did try to create something akin to the solution in the link, but couldn't get it to work.
My code
Here is where I'm setting up the RecyclerView and Querying the data from Firestore:
private void setupRecyclerView() {
RecyclerView recyclerView = findViewById(R.id.recyclerViewOs);
Query query = osRef.orderBy("ServiceOrder",
Query.Direction.ASCENDING); //This is the issue.
//How could I map the documents here?
FirestoreRecyclerOptions<ServiceOrder> options =
new FirestoreRecyclerOptions.Builder<ServiceOrder>()
.setQuery(query, ServiceOrder.class)
.build();
listAdapter = new FirestoreAdapter(options);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(listAdapter);
}
My FirestoreRecyclerAdapter, where I'm binding my Views. The onBindViewHolder returns NPE for every View. This is the problem with the nested Maps described early.
public class FirestoreAdapter extends FirestoreRecyclerAdapter<ServiceOrder, FirestoreAdapter.ViewHolder> {
#Override
protected void onBindViewHolder(#NonNull ViewHolder holder, int position, #NonNull ServiceOrder model) {
holder.osIdItem.setText(String.valueOf(model.getId()));
holder.osClientItem.setText(model.getPaymentForm());
holder.osDateItem.setText(model.getPaymentForm());
holder.osValueItem.setText(String.valueOf(model.getTotalValue()));
}
And finally, my ServiceOrder class. Getters/Setters were removed to increase readability.
public class ServiceOrder {
public Client client;
public Vehicle vehicle;
private String service;
private String observation;
private String paymentForm;
private String date;
private double totalValue;
private int id;
public ServiceOrder() {
}
private ServiceOrder(ServiceOrderBuilder serviceOrderBuilder){
this.client = serviceOrderBuilder.client;
this.vehicle = serviceOrderBuilder.vehicle;
this.service = serviceOrderBuilder.service;
this.paymentForm = serviceOrderBuilder.paymentForm;
this.observation = serviceOrderBuilder.observation;
this.totalValue = serviceOrderBuilder.value;
this.date = serviceOrderBuilder.date;
}
public static class ServiceOrderBuilder {
private Vehicle vehicle;
private Client client;
private final String service;
private final String paymentForm;
private final int id;
private final double value;
private final String date;
private String observation;
public ServiceOrderBuilder(Client client, Vehicle vehicle,
String service, String paymentForm,
int id, double value, String date) {
this.client = client;
this.vehicle = vehicle;
this.service = service;
this.paymentForm = paymentForm;
this.id = id;
this.value = value;
this.date = date;
}
public ServiceOrder.ServiceOrderBuilder observation(String observation) {
this.observation = observation;
return this;
}
public ServiceOrder build() {
ServiceOrder serviceOrder = new ServiceOrder(this);
return serviceOrder;
}
}
}
My attempt
As suggested in another post, I attempted to create a new ServiceOrderDocument in order to map all documents into an object of this class. The class:
public class ServiceOrderDocument {
ServiceOrder serviceOrder;
public ServiceOrderDocument() {}
public ServiceOrderDocument(ServiceOrder serviceOrder) {
this.serviceOrder = serviceOrder;
}
#PropertyName("ServiceOrder")
public ServiceOrder getServiceOrder() {
return serviceOrder;
}
}
Ànd pass this into the Adapter found in the private void setupRecyclerView(). However, the Adapter expects a QuerySnapshot, so I feel like I'm stuck here.
Reproducing the issue
If you'd like to try it out yourself, the best way would be to have three Classes, with one of them having objects from the other two. A example would be a Sale class having objects from Salesman and Product.
Proceed to write a Sale object into your Firestore database, and see how it creates a nested document. Then, try to display that Sale in a RecyclerView using FirestoreRecyclerAdapter. Your onBindViewHolder should have a Sale model that would get the data from it's getters.
Edit
So using a List to get the content seems to work at a first glance, by using Cast I could pass it as a adapter for the FirestoreRecyclerAdapter, however, it does not work for the startListening() methods. Here's what I did:
private void setupRecyclerView() {
docRef.get().addOnCompleteListener(task -> {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document.exists()) {
services = document.toObject(ServiceOrderDocument.class).services;
RecyclerView recyclerView = findViewById(R.id.recyclerViewOs);
Query query = osRef.orderBy("ServiceOrder",
Query.Direction.ASCENDING);
FirestoreRecyclerOptions<ServiceOrder> options =
new FirestoreRecyclerOptions.Builder<ServiceOrder>()
.setQuery(query, ServiceOrder.class)
.build();
// listAdapter = new FirestoreAdapter(options);
services = (List<ServiceOrder>) new FirestoreAdapter(options);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter((RecyclerView.Adapter) services);
}
}
});
}
However, the following issue is created:
#Override
protected void onStart() {
super.onStart();
listAdapter.startListening();//NPE Error
services.startListening();//Can't use method
}
For those looking for an answer, read Alex Mamo comments and his post at:
https://medium.com/firebase-tips-tricks/how-to-map-an-array-of-objects-from-cloud-firestore-to-a-list-of-objects-122e579eae10.
For my solution, I did need a ServiceDocument class to help me map my documents so I could use it in my Adapter. This is what the class looks like:
public class ServiceDocument {
public ServiceOrder serviceOrder;
public ServiceDocument() {
}
#PropertyName("serviceOrder")
public ServiceOrder getServiceOrder() {
return serviceOrder;
}
}
Then, in your Activity or wherever you are managing your Recyler/Adapter, you would need something like this
Query query = osRef.orderBy("serviceOrder",
Query.Direction.ASCENDING);
FirestoreRecyclerOptions<ServiceDocument> options =
new FirestoreRecyclerOptions.Builder<ServiceDocument>()
.setQuery(query, ServiceDocument.class)
.build();
listAdapter = new FirestoreAdapter(options);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
recyclerView.setAdapter(listAdapter);
Last but not least, make sure your Adapter class has been adapted for your new class.
public class FirestoreAdapter extends FirestoreRecyclerAdapter<ServiceDocument, FirestoreAdapter.ViewHolder> {
public FirestoreAdapter(#NonNull FirestoreRecyclerOptions<ServiceDocument> options) {
super(options);
}
#Override
protected void onBindViewHolder(#NonNull ViewHolder holder, int position, #NonNull ServiceDocument model) {
holder.osIdItem.setText(String.valueOf(model.getServiceOrder().getId()));
holder.osClientItem.setText(model.getServiceOrder().getClient().getName());
holder.osDateItem.setText(model.getServiceOrder().getDate());
holder.osValueItem.setText(String.valueOf(model.getServiceOrder().getTotalValue()));
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
View view = layoutInflater.inflate(R.layout.layout_os_item, parent, false);
return new FirestoreAdapter.ViewHolder(view);
}
I am using MVVM structure and my query in Dao looks like:
#Query("SELECT * FROM Sorted WHERE date LIKE :date AND categoryChart = :category")
LiveData<List<Sorted>> getSortedDiagramData(String date, String category);
In Repository I need to execute this method in AsyncTask, but I don't understand how to do it.
What I've tried:
public LiveData<List<Sorted>> getSortedDiagramData(String date, String category){
String[] array = new String[2];
array[0] = date;
array[1] = category;
return new GetSortedDiagramDataAsyncTask(sortedDao).execute(array);
}
And then:
private static class GetSortedDiagramDataAsyncTask extends AsyncTask<String[], Void, LiveData<List<Sorted>>> {
private SortedDao sortedDao;
private GetSortedDiagramDataAsyncTask(SortedDao sortedDao){
this.sortedDao = sortedDao;
}
#Override
protected LiveData<List<Sorted>> doInBackground(String[] ... strings) {
String date1 = String.valueOf(strings[0]);
String category1 = String.valueOf(strings[1]);
LiveData<List<Sorted>> list = sortedDao.getSortedDiagramData(date1, category1);
return list;
}
}
But when I pass "array" to execute() there is an error "Incompatible types".
Could you please suggest how I can solve this problem? Thanks for any help.
You can pass it in the constructor:
private String date, category;
private SortedDao sortedDao;
public GetSortedDiagramDataAsyncTask(SortedDao sortedDao, String date, String category) {
this.date = date;
this.category = category;
this.sortedDao = sortedDao;
}
#Override
protected LiveData<List<Sorted>> doInBackground(String[]... strings) {
LiveData<List<Sorted>> list = sortedDao.getSortedDiagramData(date, category);
return list;
}
Call it as:
new GetSortedDiagramDataAsyncTask(sortedDao, "date", "category").execute();
Another way would be to use this:
GetSortedDiagramDataAsyncTask(sortedDao).execute(date,category);
Aim
Save data within the Group Chat node
Current Database Tree
Within the Group Chats node, the user's messages will be sent and stored within the user's selected group chat.
As shown in the database tree, the user has sent a message saying "Hello" and his name is retrieved from the Users node
Desired Database Tree
The ---Msg ID--- is automatically generated
The ---User ID--- is retreived from the Users node
Problem
The function to allow the user to send message Only Worked Once which was the FIRST time. The following times that the app was deployed, it crashed when the user logged in.
Error - Logcat
com.google.firebase.database.DatabaseException: Failed to convert
value of type java.util.HashMap to String
ChatActivity Class - Send Chat Message function
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
jGroupChatName = getIntent().getExtras().get("groupChatName").toString();
jUserName = getIntent().getExtras().get("groupUserName").toString();
jUserNeighbourhood = getIntent().getExtras().get("groupUserHome").toString();
jChatToolbar = (Toolbar) findViewById(R.id.allUSersToolBar);
jFirebaseCurrentUser = FirebaseAuth.getInstance().getCurrentUser();
/*UserID Start - I was trying to retrieve the current user ID and add it to the Group Chat node,
under the selected Group Chat */
assert jFirebaseCurrentUser != null;
final String currentUserID = jFirebaseCurrentUser.getUid();
jChatRoot = FirebaseDatabase.getInstance().getReference().child("Group Chats").child(jGroupChatName).child(currentUserID);
/*UserID End*/
jChatMessageText = (EditText) findViewById(R.id.chatMessageText);
jChatSendTextBtn = (ImageButton) findViewById(R.id.chatSendTextBtn);
jChatSendTextBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Map<String, Object> groupMap = new HashMap<String, Object>();
jGroupKey = jChatRoot.push().getKey();
jChatRoot.updateChildren(groupMap);
// Based on the UserID code at the top, the "currentUserID" is added as shown below
// My intentions are to have the UserID saved under the name of the Group Chat
// jGroupKey = (Name of selected Group Chat)
DatabaseReference groupMessageRoot = jChatRoot.child(jGroupKey).child(currentUserID);
Map<String, Object> groupMap2 = new HashMap<String, Object>();
groupMap2.put("name", jUserName);
groupMap2.put("msg", jChatMessageText.getText().toString());
groupMessageRoot.updateChildren(groupMap2);
}
});
}
Linked Question
Android - Firebase - Send Users to Chat Room
Future Implementations
In the future, I intend to allow the user to send pictures and video recordings by writing a code to allow the user to access their phone camera or gallery and send the pics and vids data into the Group Chats node, under the selected Group name. I would also like to retrieve data after sending, but of course, I will leave those in future questions if I am unable to solve them.
Branching of your desired database is unnecessary. You are creating an unnecessary level using UID of users. Instead, merge this layer down, i.e. combine uid, message and name. You can then use someRef.orderByKey("uid").equalTo("<user_uid>") in case you need specific user's records within that path, for example chat messages.
So, create a class called ChatMessage:
public class ChatMessage {
private String uid;
private String name;
private String message;
private Long creationTime;
public ChatMessage() {
// Default constructor required for calls to DataSnapshot.getValue(ChatMessage.class)
}
public ChatMessage(String uid, String name, String message) {
this.uid = uid;
this.name = name;
this.message = message;
}
public String getUid() {
return uid;
}
public void setUid(String uid) {
this.uid = uid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public java.util.Map<String, String> getCreationTime() {
return ServerValue.TIMESTAMP;
}
#Exclude
public Long getCreationTimeLong() {
return creationTime;
}
public void setCreationTime(Long creationTime) {
this.creationTime = creationTime;
}
}
Then update your code as follows:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
chatGroupName = getIntent().getStringExtra(CHAT_GROUP_NAME);
userKey = getIntent().getStringExtra(USER_KEY);
user = getIntent().getParcelableExtra(USER);
jChatToolbar = (Toolbar) findViewById(R.id.allUSersToolBar);
jChatMessageText = (EditText) findViewById(R.id.chatMessageText);
jChatSendTextBtn = (ImageButton) findViewById(R.id.chatSendTextBtn);
jChatSendTextBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
sendMessage();
}
});
}
private void sendMessage() {
String userMessage = jChatMessageText.getText().toString();
chatGroupRef = FirebaseDatabase.getInstance().getReference().child("Group Chats").child(chatGroupName);
if (!TextUtils.isEmpty(userMessage)) {
String newChatMessageKey = chatGroupRef.push().getKey();
ChatMessage newChatMessage = new ChatMessage(userKey, user.getName(), userMessage);
chatGroupRef.child(newChatMessageKey).setValue(newChatMessage);
}
}
In my main activity I have two lists:
private List<Feed> feedItems;
private ArrayList<Object> adItems() {
ArrayList<Object> adItems = new ArrayList<>();
adItems.add("image");
adItems.add("image");
adItems.add("image");
return adItems;
}
The first list represents my feedItems, which are taken from my network model. The second list is simply a list of static images. I'm trying to set up native advertisements but I'm taking it a step back and simply supplying my feed with images to prove that overriding my RecyclerAdapter to produce multiple views works.
feedRecyclerAdapter = new FeedRecyclerAdapter(this, feedItems, new ImageLoader(new FeedItemFileCache(this)), adItems());
Here I pass my adItems() list of objects in the my FeedRecyclerAdapter as the last paramater, and in the adapter class itself I call it in the constructor:
public FeedRecyclerAdapter(Context context, List<Feed> feedItems, ImageLoader feedItemImageLoader, List<Object> adItems) {
this.context = context;
this.feedItems = feedItems;
this.feedItemImageLoader = feedItemImageLoader;
feedItems.addAll(adItems);
}
This last line feedItems.addAll(adItems); is where I get the following error message:
Error:(43, 18) error: no suitable method found for addAll(List<Object>)
method Collection.addAll(Collection<? extends Feed>) is not applicable
(argument mismatch; List<Object> cannot be converted to Collection<? extends Feed>)
method List.addAll(Collection<? extends Feed>) is not applicable
(argument mismatch; List<Object> cannot be converted to Collection<? extends Feed>)
My Feed object looks like this:
public class Feed {
public static final String CLASS_NAME = "Feed";
public static final String MARKET_CLASS_NAME = "MarketFeed";
private String objectId;
private String createdAt;
/**
* Can be a ParsePointer but we are using a relational include query to get the whole object
*/
private Design designId;
// Not part of the model, calculated at run time.
private String timestampText;
private boolean isInPersonalGallery;
private boolean isLiked;
private List<Comment> comments;
// Related to Market Feed Items
private DisplayedSide displayedSide = DisplayedSide.FRONT;
public enum DisplayedSide {
FRONT,
BACK
}
public String getObjectId() {
return objectId;
}
public Design getDesign() {
return designId;
}
public Date getCreatedAt() {
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault());
Date date = null;
try {
date = dateFormat.parse(createdAt);
} catch (ParseException e) {
e.printStackTrace();
Log.d("Feed", "Couldn't parse createdAt date when requested " + createdAt);
}
return date;
}
public String getTimestampText() {
return timestampText;
}
public void setTimestampText(String timestampText) {
this.timestampText = timestampText;
}
public boolean isInPersonalGallery() {
return isInPersonalGallery;
}
public void setInPersonalGallery(boolean inPersonalGallery) {
isInPersonalGallery = inPersonalGallery;
}
public boolean isLiked() {
return isLiked;
}
public void setLiked(boolean liked) {
isLiked = liked;
}
public List<Comment> getComments() {
return comments;
}
public void setComments(List<Comment> comments) {
this.comments = comments;
}
public DisplayedSide getDisplayedSide() {
return displayedSide;
}
public void setDisplayedSide(DisplayedSide displayedSide) {
this.displayedSide = displayedSide;
}
}
In summary, I'm trying to sprinkle some images, or adItems, between the main objects in my Collection, feedItems. Why can't I simply attach them to my main list? And what can I do instead?
you are set in wrong array
feedItems.addAll(adItems);//you are set adItems in wrong arraylist (feedItems is a feetItem array list not a adItem list)
public FeedRecyclerAdapter(Context context, List<Feed> feedItems, ImageLoader feedItemImageLoader, List<Object> adItems) {
this.context = context;
this.feedItems = feedItems;
this.feedItemImageLoader = feedItemImageLoader;
feedItems.addAll(adItems);
}`
In the onCreate method of my main activity I set adItems to be a Feed list, then passed it to my RecyclerAdapter constructor:
List<Feed> adItems = new ArrayList<>();
feedRecyclerAdapter = new FeedRecyclerAdapter(this, feedItems, new ImageLoader(new FeedItemFileCache(this)), adItems);
In my RecyclerAdapter class I added all adItems to the feedItems list so that I could operate over a single list:
public FeedRecyclerAdapter(Context context, List<Feed> feedItems, ImageLoader feedItemImageLoader, List<Feed> adItems) {
this.context = context;
this.feedItems = feedItems;
this.feedItemImageLoader = feedItemImageLoader;
feedItems.addAll(adItems);
}
I'm receiving these errors when I tried to populate a list view with firebase adapter using firebase UI
com.google.firebase.database.DatabaseException: Failed to convert
value of type java.util.HashMap to String
com.google.firebase.database.DatabaseException: Class
java.util.HashMap has generic type parameters, please use
GenericTypeIndicator instead
Here Is the code
DatabaseReference ref = FirebaseDatabase.getInstance().getReference();
DatabaseReference a = ref.child("info");
final FirebaseListAdapter<String> adapter =
new FirebaseListAdapter<String>(this,String.class,android.R.layout.simple_list_item_1,a) {
#Override
protected void populateView(View v, String model, int position) {
TextView text = (TextView) findViewById(android.R.id.text1);
text.setText(model);
}
here is an example of json data
"info" : {
"KSTUJILdwPN305Fs7ujhga4knlG3" : {
"File Info" : {
"-JFRkA855rfOU7GtcK4" : {
"Name" : "John",
"Adress" : "Test Adress",
"Favourite_food" : "Bread",
},
info node does not refer to your data model. It may contain children nodes. So to reach the model, you should use a reference like this.
DatabaseReference a = ref.child("info").child(info_id).child("File Info").child(file_id);
and you should have a FileInfo model instead of String model to use in
populateView(View v, FileInfo model, int position):
and model
public class FileInfo {
private String Name;
private String Adress;
private String Favourite_food;
public FileInfo() {
}
public FileInfo(String Name, String Adress, String Favourite_food) {
this.Name = Name;
this.Adress = Adress;
this.Favourite_food = Favourite_food;
}
public String getName() {
return Name;
}
public void setName(String Name) {
this.Name = Name;
}
public String getAdress() {
return Adress;
}
public void setAdress(String Adress) {
this.Adress = Adress;
}
public String getFavourite_food() {
return Favourite_food;
}
public void setFavourite_food(String Favourite_food) {
this.Favourite_food = Favourite_food;
}
}
final FirebaseListAdapter<String> adapter = new FirebaseListAdapter<String>(this,String.class,android.R.layout.simple_list_item_1,a)
public FirebaseListAdapter(Activity activity,
java.lang.Class<T> modelClass,
int modelLayout,
com.firebase.client.Firebase ref)
modelClass - Firebase will marshall the data at a location into an instance of a class that you provide
replace the constructor's 2nd parameter (String.class) with a POJO with the fields from info (name, address, favouriteFood)
FirebaseListAdapter
Edit:
You should also use getters in the populateView method