I'm loading data From firebase and I want to display it in recyclerview using MVVM
I retrieved data from firebase and it works fine.
But I want to use adapter.notifyDataSetChanged(); to update recyclerview in Repo class
this is my repo class:
public class CategoriesRepo {
private static CategoriesRepo instance;
private final ArrayList<Cat> categoriesModel = new ArrayList<>();
private DatabaseReference dbCategories;
public static CategoriesRepo getInstance() {
if (instance == null) {
instance = new CategoriesRepo();
}
return instance;
}
public MutableLiveData<ArrayList<Cat>> getCategories() {
loadCats();
MutableLiveData<ArrayList<Cat>> categories = new MutableLiveData<>();
categories.setValue(categoriesModel);
return categories;
}
private void loadCats() {
dbCategories = FirebaseDatabase.getInstance().getReference("categories");
dbCategories.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NotNull DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
for (DataSnapshot ds : dataSnapshot.getChildren()) {
String name = ds.getKey();
// this is not showing in recyclerview
categoriesModel.add(new Cat("Name", 1));
Log.d("TAGD", "onDataChange: " + ds.getKey() + " " + categoriesModel.size());
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
}
Is there any way to update recyclerview using MVVM?
LiveData will provide callback on value change i.e setValue or postValue . So you need to set the value after you get the data not before .
public class CategoriesRepo {
private static CategoriesRepo instance;
private DatabaseReference dbCategories;
private MutableLiveData<ArrayList<Cat>> categories = new MutableLiveData<>();
public static CategoriesRepo getInstance() {
if (instance == null) {
instance = new CategoriesRepo();
}
return instance;
}
public MutableLiveData<ArrayList<Cat>> getCategories() {
loadCats();
return categories;
}
private void loadCats() {
dbCategories = FirebaseDatabase.getInstance().getReference("categories");
dbCategories.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NotNull DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
ArrayList<Cat> categoriesModel = new ArrayList<>()
for (DataSnapshot ds : dataSnapshot.getChildren()) {
String name = ds.getKey();
categoriesModel.add(new Cat("Name", 1));
}
categories.setValue(categoriesModel);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
}
This should work . Also you have to handle Error state with data loading .
Go through This thread to handle all the states.
Related
I'm creating an app in Android Studio, which connects to Firebase realtime database. In the database I have the following structure:
First thing: I would have preferred a structure like:
users -> email -> {23, 13, 4, .., 5} but i found out that with Firebase I must have a pair key - value so I can't get this kind of structure. Am I right?!
Anyways.. I created a class like this:
public class ItemModel {
private int itemImg;
private int deleteFav;
private String itemLine;
public ItemModel(){}
public ItemModel(String itemLine) {
this.itemLine = itemLine;
}
public ItemModel(int itemImg, String itemLine, int deleteFav) {
this.itemImg = itemImg;
this.deleteFav = deleteFav;
this.itemLine = itemLine;
}
public int getItemImg() {
return itemImg;
}
public int getDeleteFav() {
return deleteFav;
}
public String getItemLine() {
return itemLine;
}
#Override
public String toString() {
return itemLine;
}
}
And this is my Firebase Database Helper class:
public class FirebaseDatabaseHelper {
private FirebaseDatabase firebaseDatabase;
private DatabaseReference databaseReferenceFavorites;
private List<ItemModel> favouriteList = new ArrayList<>();
private FirebaseAuth mAuth;
// In order to link our process we need to create interface
public interface DataStatus {
void DataIsLoaded(List<ItemModel> favourites, List<String> keys);
}
public FirebaseDatabaseHelper(){
mAuth = FirebaseAuth.getInstance();
firebaseDatabase = FirebaseDatabase.getInstance();
databaseReferenceFavorites = firebaseDatabase.getReference("users");
}
public void readFavoriteLines(final DataStatus dataStatus){
databaseReferenceFavorites.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) { // asincrono
favouriteList.clear();
List<String> keys = new ArrayList<>();
for (DataSnapshot keyNode : dataSnapshot.getChildren()){
keys.add(keyNode.getKey());
ItemModel favourite = keyNode.getValue(ItemModel.class);
favouriteList.add(favourite);
}
dataStatus.DataIsLoaded(favouriteList, keys);
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
}
static String encodeUserEmail(String userEmail) {
return userEmail.replace(".", ",");
}
}
When I run, from this class i get the error "No setter/field for test#email,com found on class com.example.x.y.ItemModel". I read other people having this error here but can't understand which part of my code is not ok. I tried to add setters and to change names of "v1" in database to "itemLine" but still having the same error.
The thing is that actually my item appear in the UI when I run but it miss one data, the itemLine. The row must be img1 - itemLine - img2 but it shows only img1 - nothing - img2. I need to take only the value of each test#email,com (itemLine) and put this value (23 for example) in my RecyclerView item. The row should look like this:
This will show nothing because you're trying to get value from users not test#email,com
Try this
Instead of this
databaseReferenceFavorites = firebaseDatabase.getReference("users");
this should fetch your data
Create database reference to the location
public FirebaseDatabaseHelper(){
mAuth = FirebaseAuth.getInstance();
firebaseDatabase = FirebaseDatabase.getInstance();
DatabaseReference databaseReferenceFavorites =
firebaseDatabase.getReference("users");
DatabaseReference databaseReferenceitemline =
databaseReferenceFavorites.child("test#gmail,com")
}
public void readFavoriteLines(final DataStatus dataStatus){
databaseReferenceitemline.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) { // asincrono
favouriteList.clear();
List<String> keys = new ArrayList<>();
for (DataSnapshot keyNode : dataSnapshot.getChildren()){
keys.add(keyNode.getKey());
ItemModel favourite = keyNode.getValue(ItemModel.class);
favouriteList.add(favourite);
}
dataStatus.DataIsLoaded(favouriteList, keys);
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
}
static String encodeUserEmail(String userEmail) {
return userEmail.replace(".", ",");
}
}
In this snippet
databaseReferenceFavorites points to user
databaseReferenceitemline points to test#gmail,com
I am making chat app ... but when i send a message it sent and i can see it on firebase but in emuloter and phone i can't ... why it happen ? i think problem may be in readMessages()
This is my chat activity class
userQuery.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot ds : dataSnapshot.getChildren()) {
String name = "" + ds.child("name").getValue();
hisImage = "" + ds.child("image").getValue();
nameTv.setText(name);
try {
Picasso.get().load(hisImage).placeholder(R.drawable.ic_defult_img_face).into(profileTv);
} catch (Exception e) {
Picasso.get().load(R.drawable.ic_defult_img_face).into(profileTv);
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
sendBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String message = messageEt.getText().toString().trim();
if (TextUtils.isEmpty(message)) {
Toast.makeText(ChatActivity.this, "Cannot send empty message...", Toast.LENGTH_SHORT).show();
} else {
sendMessage(message);
}
}
});
readMessages();
seenMessages();
}
read and send function
private void readMessages() {
chatList = new ArrayList<>();
DatabaseReference dbRef = FirebaseDatabase.getInstance().getReference("Chats");
dbRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
chatList.clear();
for (DataSnapshot ds : dataSnapshot.getChildren()) {
Modelchat chat = ds.getValue(Modelchat.class);
if (myUid.equals(chat.getReceiver()) && hisUid.equals(chat.getSender()) ||
hisUid.equals(chat.getReceiver()) && myUid.equals(chat.getSender())) {
chatList.add(chat);
}
adapterChat = new AdapterChat(ChatActivity.this, chatList, hisImage);
adapterChat.notifyDataSetChanged();
recyclerView.setAdapter(adapterChat);
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
private void sendMessage(String message) {
DatabaseReference databaseReference = FirebaseDatabase.getInstance().getReference();
String timeStamp = String.valueOf(System.currentTimeMillis());
HashMap<String, Object> hashMap = new HashMap<>();
hashMap.put("Sender", myUid);
hashMap.put("receiver", hisUid);
hashMap.put("message", message);
hashMap.put("timeStamp", timeStamp);
hashMap.put("isSeen", false);
databaseReference.child("Chats").push().setValue(hashMap);
messageEt.setText("");
}
anyone can help me ?
if you want more code or please comment and i will submit it.
I will give some hints:
Move this in your readMessages():
chatList.clear();
And place it under this:
chatList = new ArrayList<>();
//here...
Make sure that your Modelchat looks like this:
class Modelchat{
//the main thing is that these must be written exactly like the keys in your database
private String Sender;
private String receiver;
private String message;
private String timeStamp;
private boolean isSeen;
public Modelchat(String Sender,String receiver,String message,String timeStamp,boolean isSeen){
this.Sender=Sender;
this.receiver=receiver;
this.message=message;
this.timeStamp=timeStamp;
this.isSeen=isSeen;
}
//generate also getters and setters......
}
I have a Firebase databse structure like this :
the firebase database structure
Now I want to access the items in the node "comingSoonPages" to a model class. How can i get the reference to these different user specified items in that node?
The database reference :
mUpcomingDatabaseReference = mFirebaseDatabase.getReference().child("comingSoonPages").child("blrKoramangala")
now the listener to the reference is as :
mValueEventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot snapShot : dataSnapshot.getChildren()){
Log.e("snap" , String.valueOf(snapShot));
try{
UpcomingProperty property = snapShot.getValue(UpcomingProperty.class);
Log.e("name" , String.valueOf(property.getName()));
}catch (Exception ex){
ex.printStackTrace();
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
mUpcomingDatabaseReference.addValueEventListener(mValueEventListener);
when i try to log the names in each of the nodes , i get NPE and the value is null.
The model class in which i am mapping is :
public class UpcomingProperty implements Serializable {
//private Amenities amenities;
private List<String> amenities;
private Coordinates coordinates;
private Image image;
private String link;
private String location;
private String name;
private Text text;
private List<String> sortParameter;
private EarlyBird earlyBird;
public UpcomingProperty(){}
public List<String> getAmenities() {
return amenities;
}
public void setAmenities(List<String> amenities) {
this.amenities = amenities;
}
public Coordinates getCoordinates() {
return coordinates;
}
public void setCoordinates(Coordinates coordinates) {
this.coordinates = coordinates;
}
public Image getImage() {
return image;
}
public void setImage(Image image) {
this.image = image;
}
public String getLink() {
return link;
}
public void setLink(String link) {
this.link = link;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Text getText() {
return text;
}
public void setText(Text text) {
this.text = text;
}
public List<String> getSortParameter() {
return sortParameter;
}
public void setSortParameter(List<String> sortParameter) {
this.sortParameter = sortParameter;
}
public EarlyBird getEarlyBird() {
return earlyBird;
}
public void setEarlyBird(EarlyBird earlyBird) {
this.earlyBird = earlyBird;
}
}
Thanks.
the extended firebase node :
extended node values
To get that data, please use the following code:
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference comingSoonPagesRef = rootRef.child("comingSoonPages");
ValueEventListener eventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot ds : dataSnapshot.getChildren()) {
String name = ds.child("name").getValue(String.class);
Log.d("TAG", name);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
comingSoonPagesRef.addListenerForSingleValueEvent(eventListener);
The out will be the only the names. But you can get also all the other values in the same way.
If you want to use the model class, please use the following code:
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference comingSoonPagesRef = rootRef.child("comingSoonPages");
ValueEventListener eventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot ds : dataSnapshot.getChildren()) {
UpcomingProperty upcomingProperty = ds.getValue(UpcomingProperty.class);
Log.d("TAG", upcomingProperty.getName());
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
comingSoonPagesRef.addListenerForSingleValueEvent(eventListener);
You'll have the same output.
Assuming that comingSoonPages holds a list of item, You need to add a ChildEventListener on the comingSoonPages reference if you want to access all the child nodes of that.
DatabaseReference upcomingItemsRef = mFirebaseDatabase.getReference().child("comingSoonPages");
ChildEventListener childEventListener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
if(dataSnapshot.exists()) {
// Handle each individual node like blrKoramangala, cyberHub here.
String key = dataSnapshot.getKey();
// Get the UpcomingProperty object here.
UpcomingProperty property = dataSnapshot.getValue(UpcomingProperty.class);
Log.d(TAG, "property.getName():" + property.getName();
}
}
// Other methods of ChildEventListener go here
};
upcomingItemsRef.addChildEventListener(childEventListener);
You can read more about working with lists of data here.
However, if that's not a list, here's what you're doing wrong:
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// Don't do this. It will get the children of blrKoramangala and
// those won't be of UpcomingProperty type.
// Remove and replace with dataSnapshot.exists()
for (DataSnapshot snapShot : dataSnapshot.getChildren()){
...
}
}
I am trying to retrieve firebase data using addValueEventListener, but unfortunately i am not be able to get right data.
I have New_Deal_List.java class, and in this class i want to retrieve `public class New_Deal_List extends AppCompatActivity {
ListView lvDealList;
List<NewDeal_Database> dealList;
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference("Expert");
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.new__deal__list);
lvDealList = (ListView)findViewById(R.id.lvDealList);
dealList = new ArrayList<>();
}
#Override
protected void onStart() {
super.onStart();
rootRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
dealList.clear();
for(DataSnapshot dealSnapshot : dataSnapshot.getChildren()){
NewDeal_Database info = dealSnapshot.getValue(NewDeal_Database.class);
dealList.add(info);
}
DealList adapter = new DealList( New_Deal_List.this,dealList);
lvDealList.setAdapter(adapter);
}
#Override
public void onCancelled(DatabaseError databaseError) {
Toast.makeText(New_Deal_List.this,"Databse error",Toast.LENGTH_SHORT).show();
}
});
}
I am adding data through New_Deal.java by this method
private void AddNewDeal(){
int DealName = Integer.parseInt(etDealName.getText().toString());
String NewDealCategory = etNewDealCategory.getText().toString();
String DishName = etDishName.getText().toString();
String DealDescription = etDealDescription.getText().toString();
if(TextUtils.isEmpty(etDishName.getText().toString()) || TextUtils.isEmpty(etNewDealCategory.getText().toString()) || TextUtils.isEmpty(etDishName.getText().toString()) || TextUtils.isEmpty(etDealDescription.getText().toString())){
Toast.makeText(this,"All fileds must be filled.",Toast.LENGTH_SHORT).show();
}else{
DealId = keyRefrence.push().getKey();
//firebaseDatabase data = new firebaseDatabase(DealName,NewDealCategory,DishName,DealDescription);
//Contact_Info info = new Contact_Info( DealName, NewDealCategory, DealDescription);
NewDeal_Database info = new NewDeal_Database(DealName,NewDealCategory, DishName, DealDescription); keyRefrence.child(Cooker_Deal).child(DealId).child(Deal).setValue(info);
Toast.makeText(this,"Information Added",Toast.LENGTH_SHORT).show();
Intent intent = new Intent(New_Deal.this,New_Deal_Time.class);
startActivity(intent);
}
}
I am setting value using this NewDeal_Databse.java
public NewDeal_Database(int DealName,String NewDealCategory, String DishName, String DealDescription){
this.DealName = DealName;
this.NewDealCategory = NewDealCategory;
this.DishName = DishName;
this.DealDescription = DealDescription;
}
public int getDealName() {
return DealName;
}
public String getNewDealCategory() {
return NewDealCategory;
}
public String getDishName() {
return DishName;
}
public String getDealDescription() {
return DealDescription;
}
Also i DealList.java for array adapter
public class DealList extends ArrayAdapter <NewDeal_Database> {
private Activity context;
private List<NewDeal_Database> dealList;
public DealList(Activity context, List<NewDeal_Database> dealList){
super(context, R.layout.list_layout, dealList);
this.context = context;
this.dealList = dealList;
}
#NonNull
#Override
public View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
LayoutInflater inflater = context.getLayoutInflater();
View listViewItem = inflater.inflate(R.layout.list_layout,null,true);
TextView tvDealName = (TextView)listViewItem.findViewById(R.id.tvDealNamelayout);
TextView tvNewDealCategory = (TextView)listViewItem.findViewById(R.id.tvNewDealCategorylayout);
NewDeal_Database info = dealList.get(position);
tvDealName.setText(String.valueOf(info.getDealName()));
tvNewDealCategory.setText(info.getNewDealCategory());
return listViewItem;
}
}
I get these on values on output
Output the data
I this is firebase databse snapshot
firebase datasbe snapshot
updated firebase databse snapshot
Updated output
Problem solved using this code:
#Override
protected void onStart() {
super.onStart();
//dealList.clear();
rootRef.child(id).child(Cooker_Deal).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot dealSnapshot : dataSnapshot.getChildren()){
for(DataSnapshot datas : dealSnapshot.getChildren()){ //
NewDeal_Database info = datas.getValue(NewDeal_Database.class);
count++;
if(count>3){
dealList.add(info);
count=0;
}
}
}
DealList adapter = new DealList( New_Deal_List.this,dealList);
lvDealList.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
I think its nested too much but to answer your question, you need to remove this from your code:
dealList.clear();
and add it outside of the addValueEventListener()
like this:
protected void onStart() {
super.onStart();
dealList.clear();
rootRef.addValueEventListener(new ValueEventListener() {
Edit:
#Override
protected void onStart() {
super.onStart();
dealList.clear();
rootRef.child(expertId).child("Cooker Deals").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot dealSnapshot : dataSnapshot.getChildren()){
for(DataSnapshot datas : dealSnapshot.getChildren(){
NewDeal_Database info = datas.getValue(NewDeal_Database.class);
dealList.add(info);
}
DealList adapter = new DealList( New_Deal_List.this,dealList);
lvDealList.setAdapter(adapter);
adapter.notifydatasetchanged();
}
Assuming that faum-expert nide is a direct child of your Firebase database, to get that data, please use the following code:
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference ref = rootRef.child("faum-expert").child("Expert").child(expertId).child("Cooker Deals");
ValueEventListener eventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot ds : dataSnapshot.getChildren()) {
String dealName = ds.child("Deals Information").child("dealName").getValue(String.class);
String newDealCategory = ds.child("Deals Information").child("newDealCategory").getValue(String.class);
Log.d("TAG", dealName + " / " + newDealCategory);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
ref.addListenerForSingleValueEvent(eventListener);
The output will be:
4567 / kagj
But pay atention, the fields inside your model class are first letter uppercase and in your database are first letter lowercase. To solve this, add the following annotations in front of your fields like this:
#PropertyName("dealName")
private String DealName;
#PropertyName("newDealCategory")
private String NewDealCategory;
#PropertyName("dishName")
private String DishName;
#PropertyName("dealDescription")
private String DealDescription;
I have to return the response from the firebase to another function which will do further processing with that data.
public ArrayList<String> getDatas(String number) {
final ArrayList<String> requestList = new ArrayList<>();
final FirebaseDatabase database = FirebaseDatabase.getInstance();
final DatabaseReference reference = database.getReference("Users").child(number).child("request_list");
final DatabaseReference requestReference = database.getReference("Request");
reference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Log.i(TAG, "onDataChange: ");
for (final DataSnapshot data : dataSnapshot.getChildren()) {
Log.i(TAG, "onDataChange: data: " + data.getValue());
requestList.add(data.getValue().toString());
}
Log.i(TAG, "onDataChange: for ended");
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
return requestList;
}
Since the firebase operation is asynchronous I am not able to wait for the data to return. Can someone help me with this problem?
you can use a callback method
http://developer.android.com/reference/java/util/concurrent/Callable.html
By using Callable interfaces you can pass an argument as function I added a simple code snippet for understanding.
public class MainActivity<V> extends Activity {
Callable<String> doLogin=null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
doLogin=new Callable<String>() { //created but not called now.
#Override
public String call() throws Exception {
//make some piece of code
return "something"; //or false
}
};
CheckSession checkSession=new CheckSession("sessionName");
String sessionKey="";
try { //we are sending callable to the DAO or any class we want
sessionKey=checkSession.getSessionKey(doLogin);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class CheckSession{
String sessionName="";
Callable<String> func=null;
public CheckSession(String sessionName) {
super();
this.sessionName = sessionName;
}
public String getSessionKey(Callable<String> doLogin) throws Exception{
func=doLogin;
return (String) func.call();
}
}