If my database is still loading, then show loading dialog. That what I want to make my chatapp works. I found out that my problem will solve by overriding onDataChanged() in my FirebaseRecyclerAdapter like this code below
new FirebaseRecyclerAdapter(...) {
#Override
protected void populateViewHolder(...) {...}
#Override
protected void onDataChanged() {
}
};
but the problem is I can't override the onDataChanged() to my Adapter.
How can i fix this problem? please see my Screenshot below.
UPDATED:
This is my whole code related to my problem (other codes are not necessary to post).
public void btn_discussion() {
FRRA = new FirebaseRecyclerAdapter<Message, MessageViewHolder>(
Message.class,
R.layout.discussion_q_and_answer,
ActivityDiscussion.MessageViewHolder.class,
MyDatabase
) {
#Override
protected void populateViewHolder(ActivityDiscussion.MessageViewHolder viewHolder, Message model, int position) {
viewHolder.setContent(model.getContent());
viewHolder.setUsername(model.getUsername());
viewHolder.setTime(myConstructor.LongToDate(model.getTime()));
}
#Override
protected void onDataChanged() {
//Write something here
}
};
msgList.setAdapter(FRRA);
msgList.smoothScrollToPosition(FRRA.getItemCount());
}
RecyclerView.ViewHolder
public static class MessageViewHolder extends RecyclerView.ViewHolder {
View view;
public MessageViewHolder(View itemView) {
super(itemView);
this.view = itemView;
}
private void setContent(String content) {
TextView msg = view.findViewById(R.id.comment);
msg.setText(content);
}
private void setUsername(String username) {
TextView msg = view.findViewById(R.id.tv_user);
msg.setText(username);
}
private void setTime(String time) {
TextView msg = view.findViewById(R.id.tv_time);
msg.setText(time);
}
}
Getter and Setter
Message.java
public class Message {
private String content;
private String Username;
private String time;
public Message() {}
public Message(String mcontent, String musername, String mtime) {
this.content = mcontent;
this.Username = musername;
this.time = mtime;
}
public String getContent() {
return content;
}
public void setContent(String message) {
this.content = message;
}
public String getUsername() {
return Username;
}
public void setUsername(String Username) {
this.Username = Username;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
}
You are using an older version of firebaseui and in the older versions onDataChanged was not in the override methods. You can see that by clicking ALT+ INS to see all override methods belonging to this class.
You need to use the latest one which is this:
implementation 'com.firebaseui:firebase-ui-database:3.1.1'
and then you can use onDataChanged and other methods. When you upgrade you will have to use FirebaseRecyclerOptions also, for more info check this guide here:
FirebaseUI for Realtime Database
Related
I'm making a simple app that has to make a call to an API that returns an object with some attributes and is shown in a RecyclerView.
The call is being made to https://jsonplaceholder.typicode.com/photos?_start=0&_limit=5
The app doesn't crash, the recyclerview is being generated but it is empty. I used the debugger and saw that the list in the adapter of the recyclerview is empty (the size is 0).
I believe the issue is with the structure of the java objects I made but I can't confirm it for sure and I can't seem to modify my object structure to match that of the returned object. I'm not seeing an object with other objects inside of like with other apis I've worked on (when I check the above link with a json online reader).
I usually make my object and another object container (which has a list of the first object). My suspicion is that the issue is there, please help me find the problem.
Below the main activity, object, object container, adapter, retrofit object, object dao and object controller.
Activity:
public class PhotoActivity extends AppCompatActivity implements AdapterPhotoRecyclerView.SelectedPhotoListener {
private AdapterPhotoRecyclerView adapterPhotoRecyclerView;
private RecyclerView recyclerView;
private ProgressBar progressBar;
private LinearLayoutManager linearLayoutManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_photo);
linearLayoutManager = new LinearLayoutManager(this, RecyclerView.VERTICAL, false);
progressBar = findViewById(R.id.photo_activity_progress_bar);
makeCall("photos?_start=0&_limit=5");
adapterPhotoRecyclerView = new AdapterPhotoRecyclerView(this);
recyclerView = findViewById(R.id.photo_activity_recyclerview);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setAdapter(adapterPhotoRecyclerView);
}
public void makeCall(String fixedUrl) {
MyPhotoController myPhotoController = new MyPhotoController();
myPhotoController.getPhotos(fixedUrl, new ResultListener<MyPhotoContainer>() {
#Override
public void finish(MyPhotoContainer result) {
progressBar.setVisibility(View.VISIBLE);
adapterPhotoRecyclerView.setMyPhotoList(result.getmPhotoList());
progressBar.setVisibility(View.GONE);
}
});
}
#Override
public void selectePhoto(Integer position, List<MyPhoto> myPhotoList) {
MyPhoto clickedPhoto = myPhotoList.get(position);
Toast.makeText(this, clickedPhoto.getTitle(), Toast.LENGTH_SHORT).show();
}
}
Adapter of the RecyclerView
public class AdapterPhotoRecyclerView extends RecyclerView.Adapter<AdapterPhotoRecyclerView.PhotoViewHolder> {
private List<MyPhoto> myPhotoList;
private SelectedPhotoListener selectedPhotoListener;
public AdapterPhotoRecyclerView(SelectedPhotoListener selectedPhotoListener) {
myPhotoList = new ArrayList<>();
this.selectedPhotoListener = selectedPhotoListener;
}
public void setMyPhotoList(List<MyPhoto> myPhotoList) {
this.myPhotoList = myPhotoList;
notifyDataSetChanged();
}
public List<MyPhoto> getMyPhotoList() {
return myPhotoList;
}
#NonNull
#Override
public PhotoViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_cell_photo, parent, false);
PhotoViewHolder photoViewHolder = new PhotoViewHolder(view);
return photoViewHolder;
}
#Override
public void onBindViewHolder(#NonNull PhotoViewHolder holder, int position) {
MyPhoto myPhoto = myPhotoList.get(position);
holder.bindPhoto(myPhoto);
}
#Override
public int getItemCount() {
if (myPhotoList == null){
return 0;
} else {
return myPhotoList.size();
}
}
public class PhotoViewHolder extends RecyclerView.ViewHolder {
private ImageView thumbnail;
private TextView title;
public PhotoViewHolder(#NonNull View itemView) {
super(itemView);
this.thumbnail = itemView.findViewById(R.id.recyclerview_cell_photo_thumbnail);
this.title = itemView.findViewById(R.id.recyclerview_cell_photo_title);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
selectedPhotoListener.selectePhoto(getAdapterPosition(), myPhotoList);
}
});
}
public void bindPhoto(MyPhoto myPhoto) {
Glide.with(itemView).load(myPhoto.getThumbnailUrl()).placeholder(R.mipmap.ic_launcher).into(thumbnail);
title.setText(myPhoto.getTitle());
}
}
public interface SelectedPhotoListener {
public void selectePhoto(Integer position, List<MyPhoto> myPhotoList);
}
}
Object dao
public class MyPhotoDao extends MyRetrofit {
private JsonPlaceholderService service;
public MyPhotoDao() {
super("https://jsonplaceholder.typicode.com/");
service = retrofit.create(JsonPlaceholderService.class);
}
public void getPhotos(String fixedUrl, final ResultListener<MyPhotoContainer> listenerOfTheController) {
Call<MyPhotoContainer> call = service.jsonPlaceholderPhoto(fixedUrl);
call.enqueue(new Callback<MyPhotoContainer>() {
#Override
public void onResponse(Call<MyPhotoContainer> call, Response<MyPhotoContainer> response) {
MyPhotoContainer myPhotoContainer = response.body();
listenerOfTheController.finish(myPhotoContainer);
}
#Override
public void onFailure(Call<MyPhotoContainer> call, Throwable t) {
}
});
}
public void getAlbum(String fixedUrl, final ResultListener<List<Album>> listenerOfTheController){
Call<List<Album>> call = service.jsonPlaceholderAlbum(fixedUrl);
call.enqueue(new Callback<List<Album>>() {
#Override
public void onResponse(Call<List<Album>> call, Response<List<Album>> response) {
List<Album> albumList = response.body();
listenerOfTheController.finish(albumList);
}
#Override
public void onFailure(Call<List<Album>> call, Throwable t) {
}
});
}
}
Object controller
public class MyPhotoController {
public void getPhotos(String fixedUrl, final ResultListener<MyPhotoContainer> listenerOfTheView) {
MyPhotoDao myPhotoDao = new MyPhotoDao();
myPhotoDao.getPhotos(fixedUrl, new ResultListener<MyPhotoContainer>() {
#Override
public void finish(MyPhotoContainer result) {
listenerOfTheView.finish(result);
}
});
}
public void getAlbums(String fixedUrl, final ResultListener<List<Album>> listenerOfTheView){
MyPhotoDao myPhotoDao = new MyPhotoDao();
myPhotoDao.getAlbum(fixedUrl, new ResultListener<List<Album>>() {
#Override
public void finish(List<Album> result) {
listenerOfTheView.finish(result);
}
});
}
}
Retrofit object
public abstract class MyRetrofit {
protected Retrofit retrofit;
public MyRetrofit(String baseUrl) {
OkHttpClient.Builder okHttpClient = new OkHttpClient.Builder();
Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl(baseUrl)
.client(okHttpClient.build())
.addConverterFactory(GsonConverterFactory.create());
retrofit = builder.build();
}
}
Object I'm trying to GET
public class MyPhoto implements Serializable {
#SerializedName("AlbumId")
private Integer albumNumber;
#SerializedName("id")
private Integer photoId;
private String title;
#SerializedName("url")
private String photoUrl;
private String thumbnailUrl;
public Integer getAlbumNumber() {
return albumNumber;
}
public Integer getPhotoId() {
return photoId;
}
public String getTitle() {
return title;
}
public String getPhotoUrl() {
return photoUrl;
}
public String getThumbnailUrl() {
return thumbnailUrl;
}
}
Object container
public class MyPhotoContainer implements Serializable {
#SerializedName("array")
private List<MyPhoto> mPhotoList;
public List<MyPhoto> getmPhotoList() {
return mPhotoList;
}
}
If there is anything missing please let me know.
Any help and comments are apreciated!
JSON payload does not fit to POJO classes. You do not need to use MyPhotoContainer class at all. Response JSON is a JSON Array with directly placed JSON Objects. getPhotos method should look similar to getAlbum method. Try:
public void getPhotos(String fixedUrl, final ResultListener<List<MyPhoto>> listenerOfTheView)
I am trying to get a hang of the Android Architecture Library and i have been trying to display texts from retrofit via a ViewModel using Mutable LiveData on my Main Activity but i cant seem to do it, i would really appreciate some assistance.
Here is my Model Class
public class User {
#SerializedName("name")
#Expose
private String name;
#SerializedName("email")
#Expose
private String email;
#SerializedName("phone")
#Expose
private String phone;
public User() {
}
public String getUserName() {
return name;
}
public void setUserName(String name) {
this.name = name;
}
public String getUserEmail() {
return email;
}
public void setUserEmail(String email) {
this.email = email;
}
public String getUserPhone() {
return phone;
}
public void setUserPhone(String phone) {
this.phone = phone;
}
}
My View model
public class UserViewModel extends AndroidViewModel {
private NodeAuthService api;
private SharedPreferences pref;
private static MutableLiveData<List<User>> userDetails = new
MutableLiveData<>();
private Call<List<User>> call;
public UserViewModel(#NonNull Application application) {
super(application);
api = AuthRetrofitClient.getInstance().create(NodeAuthService.class);
}
private String email = pref.getString("email", "");
public void loadUser(){
call = api.getUser(email);
call.enqueue(new Callback<List<User>>() {
#Override
public void onResponse(Call<List<User>> call, Response<List<User>>
response) {
List<User> users = response.body();
}
#Override
public void onFailure(Call<List<User>> call, Throwable t) {
Log.d("USER",t.getMessage());
}
});
}
public MutableLiveData<List<User>> getUserDetails(){
return userDetails;
}
}
Simplified version of my MainActivity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
TextView navName = (TextView) findViewById(R.id.navigation_name);
TextView navEmail = (TextView) findViewById(R.id.navigation_email);
}
Kindly assist
In your viewModel
public void loadUser(){
call = api.getUser(email);
call.enqueue(new Callback<List<User>>() {
#Override
public void onResponse(Call<List<User>> call, Response<List<User>>
response) {
userDetails.postValue(response.body())
}
#Override
public void onFailure(Call<List<User>> call, Throwable t) {
Log.d("USER",t.getMessage());
}
});
}
And in your Activity observe the changes
yourVm.getUserDetails().observe(this, models -> {
if (models != null) {
for(int = 0; i<models.size();i++){
/*you will get your list here now iterate through list and get
your email_id here.*/
}
}
});
instead of this
List<User> users = response.body();
use this
userDetails.postValue(response.body())
also, remove static before livedata
inside activity oncreate() init your viewModel instance.
then viewModel.getUserDEtails().obsever(this, data-> // your stuff);
I struggle to get data from Firebase for Android.
Please see my code.
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
Logger.log("onChildAdded:" + previousChildName);
// Logger.log( dataSnapshot.child("topicid").getValue(String.class));
//dataSnapshot.child("topicid").getValue();
mComment = new ArticleComment();
mComment = dataSnapshot.getValue(ArticleComment.class);
}
Here is ArticleComment class
public class ArticleComment {
private Date mCommentTime;
private String mComment;
private String mUID;
private String mName;
private int mColorRed;
private int mColorGreen;
private int mColorBlue;
public ArticleComment(){
}
public ArticleComment(Date time, String comment,String name,String uid,int[] color){
this.mCommentTime = time;
this.mComment = comment;
this.mName = name;
this.mUID = uid;
this.mColorRed=color[0];
this.mColorGreen=color[1];
this.mColorBlue=color[2];
}
public Date getTime(){
return mCommentTime;
}
public void setTime(Date time){
mCommentTime =time;
}
public String getComment(){
return mComment;
}
public void setComment(String comment){
mComment =comment;
}
public String getName(){
return mName;
}
public void setName(String name){
mName=name;
}
public String getUID(){
return mUID;
}
public void setUID(String uid){
mUID=uid;
}
public int getColorRed(){
return mColorRed;
}
public int getColorGreen(){
return mColorGreen;
}
public int getColorBlue(){
return mColorBlue;
}
public void setColor(int red,int green, int blue)
{
mColorRed=red;
mColorGreen=green;
mColorBlue=blue;
}
#Exclude
public Map<String, Object> toMap(){
HashMap<String, Object> hashmap = new HashMap<>();
hashmap.put("time", mCommentTime);
hashmap.put("UID", mUID);
hashmap.put("name", mName);
hashmap.put("comment", mComment);
hashmap.put("colorRed", mColorRed);
hashmap.put("colorBlue", mColorBlue);
hashmap.put("colorGreen", mColorGreen);
return hashmap;
}
}
And here is my DB information.
I could get only
mCommentTime;
mComment;
mName;
But I can't get
mUID;
mColorRed;
mColorGreen;
mColorBlue;
Is there something wrong with my code?
Actually datasnapshot has data but it didn't copy to mComment
Hi! Thank you, friends, gave me feedback here is all code.
public class CommentsActivity extends AppCompatActivity {
private DatabaseReference myRef;
private DatabaseReference mTopicRef;
private RecyclerView mRecyclerView;
private RecyclerArticleAdapter mAdapter;
private RecyclerView.LayoutManager mLayoutManager;
private List<ArticleComment> myDataset = new ArrayList<ArticleComment>();
private ArticleComment mComment;
private Topic mTopic;
private EditText mTitleEditText;
private String mUserName;
private String mUid;
private int mRedColor=100;
private int mBlueColor=100;
private int mGreenolor=100;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_comments);
//Add action button
Intent intent =getIntent();
String id = intent.getStringExtra("ID");
String date = intent.getStringExtra("Date");
String title = intent.getStringExtra("Title");
String topic = intent.getStringExtra("Topic");
mUserName = intent.getStringExtra("UserName");
mUid = intent.getStringExtra("UID");
SharedPreferences pref=PreferenceManager.getDefaultSharedPreferences(this);
mRedColor= pref.getInt("ColorRed",255);
mGreenolor=pref.getInt("ColorGreen",255);
mBlueColor=pref.getInt("ColorBlue",255);
mRecyclerView = findViewById(R.id.recyclerView_article);
mRecyclerView.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
//Reference DB
FirebaseDatabase database = FirebaseDatabase.getInstance();
myRef = database.getReference("Main").child("Comments").child(id);
mTopicRef = database.getReference("Main").child("Topics").child(id);
List<String> topicinfo=new ArrayList<String >();
topicinfo.add(date);
topicinfo.add(title);
topicinfo.add(topic);
// Set TestAdapter as the adapter for RecyclerView.
mRecyclerView.setAdapter(mAdapter);
mAdapter = new RecyclerArticleAdapter(myDataset,topicinfo){
/* #Override
protected void onCheckedChangedRecycle(CompoundButton comButton, final boolean isChecked){
mTopicRef.runTransaction(new Transaction.Handler() {
#Override
public Transaction.Result doTransaction(MutableData mutableData) {
Topic t = mutableData.getValue(Topic.class);
String id = mutableData.child("topicid").getValue(String.class);
t.setTopicID(id);
if (t == null) {
return Transaction.success(mutableData);
}
if (isChecked==true) {
// Unstar the post and remove self from stars
t.setRate(t.getRate()+1);
} else {
// Star the post and add self to stars
t.setRate(t.getRate()-1);
}
// Set value and report transaction success
mutableData.setValue(t);
return Transaction.success(mutableData);
}
#Override
public void onComplete(DatabaseError databaseError, boolean b,
DataSnapshot dataSnapshot) {
// Transaction completed
}
});
} */
};
mRecyclerView.setAdapter(mAdapter);
findViewById(R.id.button2).setOnClickListener(button1ClickListener);
mTitleEditText = findViewById(R.id.editText2);
ChildEventListener childEventListener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
mComment = new ArticleComment();
mComment = dataSnapshot.getValue(ArticleComment.class);
Logger.log("onChildAdded:addItem" + previousChildName);
mAdapter.addItem(mAdapter.getItemCount()-1, mComment);
Logger.log("onChildAdded:scrollToPosition" + previousChildName);
mAdapter.updateItem(mAdapter.getItemCount()-1,mComment);
mLayoutManager.scrollToPosition(mAdapter.getItemCount()-1);
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
myRef.addChildEventListener(childEventListener);
}
View.OnClickListener button1ClickListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
// finish();
Logger.log("onClick");
if(mTitleEditText.getText().toString().equals("")){
return;
}
//setting color
int color[]=new int[3];
color[0]= mRedColor;
color[1]=mGreenolor;
color[2]=mBlueColor;
mComment = new ArticleComment(new Date(),mTitleEditText.getText().toString(),mUserName,mUid,color);
sendTopic(mComment,myRef);
//Delete all text
mTitleEditText.setText("");
}
};
// Sending topic to DB
public void sendTopic(ArticleComment test,DatabaseReference ref) {
String key = ref.push().getKey();
Map<String, Object> map = new HashMap<>();
map.put(key, test.toMap());
ref.updateChildren(map);
}
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
finish();
return true;
}
return true;
}
Is it enough information?
Firebase follows JavaBean naming conventions to determine the name of the JSON property from the Java code. And in that convention getUID and setUID map to a property uID with a lowercase u.
To make the Firebase client adopt your naming convention, annotate the getter and setter with a #PropertyName:
#PropertyName("UID")
public String getUID(){
return mUID;
}
#PropertyName("UID")
public void setUID(String uid){
mUID=uid;
}
I'm not immediately sure why the other properties don't work. When this happens, I find it most useful to write an object of the type of Firebase, to see what it generates.
**
If you want to store values in a model .Then your variable name should
be same as Firebase node or else you need to typeConvert it .
**
private Date mCommentTime;
private String mComment;
private String mUID;
private String mName;
private int mColorRed;
private int mColorGreen;
private int mColorBlue;
Replace above with
private int time;
private String comment;
private String UID;
private String name;
private int colorRed;
private int colorBlue;
private int colorGreen;
now make getters,setters,constructor etc. using above nodes. (Now you will be able to get all the values ) .
Hope its gonna help you :)
This is the event object that I want to inflate with RecyclerView:
public class Event {
private String mName;
private String mId;
private String mDate;
private String mPlace;
private User mUser;
private Category mCat;
private String mDescription;
public void setmEventCat(Map<String, Category> mEventCat) {
this.mEventCat = mEventCat;
}
public Map<String, Category> getmEventCat() {
return mEventCat;
}
private Map<String,Category> mEventCat;
public String getmDescription() {
return mDescription;
}
public void setDescription(String mDescription) {
this.mDescription = mDescription;
}
public Category getCat() {
return mCat;
}
public void setCat(Category mCat) {
this.mCat = mCat;
}
public String getDate() {
return mDate;
}
public String getPlace() {
return mPlace;
}
private ArrayList<User> mList;
public Event() {
}
public String getName() {
return mName;
}
public void setName(String mName) {
this.mName = mName;
}
public void setDate(String mDate) {
this.mDate = mDate;
}
public void setPlace(String mPlace) {
this.mPlace = mPlace;
}
public String getId() {
return mId;
}
public void setId(String mId) {
this.mId = mId;
}
}
The nested Category object:
public class Category implements Serializable{
private String mCatName;
private String mCatID;
public Category() {
}
public Category(String mCatName) {
this.mCatName = mCatName;
}
public String getCatName() {
return mCatName;
}
public String getCatID() {
return mCatID;
}
public void setCatName(String mCatName) {
this.mCatName = mCatName;
}
public void setCatID(String mCatID) {
this.mCatID = mCatID;
}
}
How I retrieve the data from firebase:
mDataBase = FirebaseDatabase.getInstance().getReference("Event");
mEvents = new ArrayList<Event>();
mEvent=new Event();
mRecyclerView = (RecyclerView) rootView.findViewById(R.id.recycler_view);
mDataBase.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot != null && dataSnapshot.getValue() != null) {
try {
for (DataSnapshot eventSnapshot : dataSnapshot.getChildren()) {
Event event = eventSnapshot.getValue(Event.class);
String id = eventSnapshot.getKey();
mEvents.add(event);
mRecyclerView.scrollToPosition(mEvents.size() - 1);
mAdapter.notifyItemInserted(mEvents.size() - 1);
}
}
catch (Exception ex) {
Log.e("ERROR", ex.getMessage());
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
mAdapter = new EventAdapter(mContext, mEvents);
mRecyclerView.setAdapter(mAdapter);
mRecyclerView.setLayoutManager(new LinearLayoutManager(mContext));
My EventAdapter:
#Override
public void onBindViewHolder(EventAdapter.ViewHolder holder, int position) {
mEvent = mEvents.get(position);
holder.mEventName.setText(mEvent.getName());
//Every time I tried to add this line to set category name
//the NullPointerException error occurs
holder.mEventCategory.setText(mEvent.getCat().getCatName());
holder.mEventDate.setText(mEvent.getDate());
}
The ViewHolder Class:
public class ViewHolder extends RecyclerView.ViewHolder {
private TextView mEventName, mEventDate,mEventCategory;
public ViewHolder(View itemView) {
super(itemView);
mEventName = itemView.findViewById(R.id.eventName_tv);
mEventDate = itemView.findViewById(R.id.date_tv);
mEventCategory = itemView.findViewById(R.id.categoryTv);
}
}
#Override
public EventAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.search_event, parent, false);
return new ViewHolder(view);
}
The problem is the Event is displayed as I expected, but I cannot get the category inside the event and bind it to my widget by simply calling the getCat(). I know this may caused by asynchronous Firebase API. How can I set up the TextView with the nested category object.
This is my FINAL piece of my application, any hints would be a great help.
Thanks in advance!
I figure out the question that may be useful for other people. Before adding the event object to the list, I retrieve the nested object by key and assign it to category object. Finally, linking it with event...
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot != null && dataSnapshot.getValue() != null) {
try {
for (DataSnapshot eventSnapshot : dataSnapshot.getChildren()) {
Event event = eventSnapshot.getValue(Event.class);
String id = eventSnapshot.getKey();
mCategory = eventSnapshot.child(id).getValue(Category.class);
event.setCat(mCategory);
mEvents.add(event);
mRecyclerView.scrollToPosition(mEvents.size() - 1);
mAdapter.notifyItemInserted(mEvents.size() - 1);
}
}
catch (Exception ex) {
Log.e("ERROR", ex.getMessage());
}
}
}
Then binding the desired data with widget by calling the pojo getter method (nothing's changed here).
#Override
public void onBindViewHolder(EventAdapter.ViewHolder holder, int position) {
mEvent = mEvents.get(position);
holder.mEventName.setText(mEvent.getName());
holder.mEventCategory.setText(mEvent.getCat().getCatName());
holder.mEventDate.setText(mEvent.getDate());
}
I'm trying to retrieve values from the database to display on views but im getting this crash right here
FATAL EXCEPTION: main
Process: com.example.ahmad.carrental, PID: 15975
com.google.firebase.database.DatabaseException: Failed to convert a value of type java.lang.String to long
at com.google.android.gms.internal.zzear.zzb(Unknown Source)
at com.google.android.gms.internal.zzear.zza(Unknown Source)
at com.google.android.gms.internal.zzear.zzb(Unknown Source)
at com.google.android.gms.internal.zzeas.zze(Unknown Source)
at com.google.android.gms.internal.zzear.zzb(Unknown Source)
at com.google.android.gms.internal.zzear.zza(Unknown Source)
at com.google.firebase.database.DataSnapshot.getValue(Unknown Source)
at com.example.ahmad.carrental.Utilities.FirebaseUtilities.getCarData(FirebaseUtilities.java:178)
at com.example.ahmad.carrental.CarPost.CreatePostActivity$1$1.onDataChange(CreatePostActivity.java:100)
at com.google.android.gms.internal.zzduz.zza(Unknown Source)
at com.google.android.gms.internal.zzdwu.zzbvb(Unknown Source)
at com.google.android.gms.internal.zzdxa.run(Unknown Source)
at android.os.Handler.handleCallback(Handler.java:761)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:156)
at android.app.ActivityThread.main(ActivityThread.java:6605)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:999)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:889)
The classes are as follows
Car Model
public class Car {
private String brand;
private String id;
private int price;
private String model;
private long distance;
private String status;
private String picture;
private String location;
private String description;
public Car() {
}
public Car(String brand, String id, int price, String model, long distance, String status, String picture, String location, String description) {
this.brand = brand;
this.id = id;
this.price = price;
this.model = model;
this.distance = distance;
this.status = status;
this.picture = picture;
this.location = location;
this.description = description;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public long getDistance() {
return distance;
}
public void setDistance(long distance) {
this.distance = distance;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getPicture() {
return picture;
}
public void setPicture(String picture) {
this.picture = picture;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
User Model
public class User {
private String email;
private String id;
private String name;
private int phonenumber;
public User(String email, String id, String name,int phonenumber) {
this.email = email;
this.id = id;
this.name = name;
this.phonenumber = phonenumber;
}
public User(){
}
public int getPhonenumber() {
return phonenumber;
}
public void setPhonenumber(int phonenumber) {
this.phonenumber = phonenumber;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Database Querying Function
public Car getCarData(DataSnapshot dataSnapshot) {
Log.i(TAG,"getCardData: Getting car data from database");
Car data = new Car();
for(DataSnapshot ds :dataSnapshot.getChildren()){
if(ds.getKey().equals(context.getString(R.string.dbname_car_post))){
try{
data.setId(ds.child(userID).getValue(Car.class).getId());
data.setBrand(ds.child(userID).getValue(Car.class).getBrand());
data.setDescription(ds.child(userID).getValue(Car.class).getDescription());
data.setModel(ds.child(userID).getValue(Car.class).getModel());
data.setDistance(ds.child(userID).getValue(Car.class).getDistance());
data.setPicture(ds.child(userID).getValue(Car.class).getPicture());
data.setStatus(ds.child(userID).getValue(Car.class).getStatus());
data.setLocation(ds.child(userID).getValue(Car.class).getLocation());
data.setPrice(ds.child(userID).getValue(Car.class).getPrice());
}catch (NullPointerException e){
Log.d(TAG, "getCarData: NullPointerException : " + e.getMessage());
}
}
}
return data;
}
Activity that im retrieving the data from
CreateCarPost Activity
public class CreatePostActivity extends AppCompatActivity implements CreatePostView,AdapterView.OnItemSelectedListener{
//Activity Tag
private static final String TAG ="CreateCarPost";
//Spinners
Spinner statusSpinner;
Spinner brandSpinner;
//Adapter of spinners
ArrayAdapter mArrayAdapter;
ArrayAdapter mArrayAdapter2;
//views
private TextView tvDistance;
private EditText etDistance;
private AutoCompleteTextView etCarLocation;
private CircleImageView civPicture;
private EditText etPrice;
private EditText etDescription;
private EditText etModel;
private ImageView checkButton;
//Strings
private String carLoactionStr;
private String carBrandStr;
private String carStatusStr;
//layout containg the views
private LinearLayout layoutContainer;
//To adjust dynamic views margins
LinearLayout.LayoutParams layoutParamsTv,layoutParamsEt;
//Firebase
private FirebaseAuth mAuth;
private FirebaseAuth.AuthStateListener mAuthStateListener;
private FirebaseDatabase mFirebaseDatabase;
private DatabaseReference mDatabaseReference;
private ValueEventListener singleValueEventListener;
private FirebaseUtilities mFirebaseUtilities;
Context mContext;
CreatePostPresenter createPostPresenter;
private Car car;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_create_post);
initialization();
setupFirebaseAuth();
setUpLocationSpinner();
//Assigning Car object with its data from database.
io.reactivex.Observable.create(new ObservableOnSubscribe() {
#Override
public void subscribe(ObservableEmitter emitter) throws Exception {
singleValueEventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
car = mFirebaseUtilities.getCarData(dataSnapshot);
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.e(TAG, "CANCELLED.");
}
};
mDatabaseReference.addValueEventListener(singleValueEventListener);
}
}).unsubscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe();
checkButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
createPostPresenter.onSaveChanges(car);
}
});
}
//initalizing everything necessary here
public void initialization(){
mContext = getApplicationContext();
createPostPresenter = new CreatePostPresenter(this,this);
//Adapter set up for spinners
mArrayAdapter2 = ArrayAdapter.createFromResource(this,R.array.car_brands,android.R.layout.simple_spinner_item);
mArrayAdapter = ArrayAdapter.createFromResource(this,R.array.car_status_array,android.R.layout.simple_spinner_item);
//Status spinner set up
statusSpinner = findViewById(R.id.createPostCarStatusSpinner_ID);
statusSpinner.setAdapter(mArrayAdapter);
statusSpinner.setOnItemSelectedListener(this);
//Brand spinner set up
brandSpinner = findViewById(R.id.createPostCarBrandSpinner_ID);
brandSpinner.setAdapter(mArrayAdapter2);
brandSpinner.setOnItemSelectedListener(this);
layoutContainer = findViewById(R.id.createPostLinearLayout_ID);
tvDistance = new TextView(this);
tvDistance.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
tvDistance.setText("Distance Travelled");
etDistance = new EditText(this);
etDistance.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
//margin settings editText
layoutParamsEt = (LinearLayout.LayoutParams)etDistance.getLayoutParams();
layoutParamsEt.setMargins(0,10,0,0);
etDistance.setLayoutParams(layoutParamsEt);
//margin settings textView
layoutParamsTv = (LinearLayout.LayoutParams)tvDistance.getLayoutParams();
layoutParamsTv.setMargins(0,10,0,0);
tvDistance.setLayoutParams(layoutParamsTv);
etCarLocation = findViewById(R.id.createPostCarLocation_ID);
etDescription = findViewById(R.id.createPostCarDes_ID);
etPrice = findViewById(R.id.createPostCarPrice_ID);
etModel = findViewById(R.id.createPostCarModel_ID);
checkButton = findViewById(R.id.check_ID);
mFirebaseUtilities = new FirebaseUtilities(this);
}
private void setUpLocationSpinner() {
ArrayAdapter<String> listOfCities = new ArrayAdapter<>(getBaseContext(),
android.R.layout.simple_list_item_1, getResources().getStringArray(R.array.TR_cities));
//--- to ensure user is restricted to selections from drop-down menu
etCarLocation.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
carLoactionStr = etCarLocation.getAdapter().getItem(position).toString();
}
});
etCarLocation.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
for (int i = 0; i < etCarLocation.getAdapter().getCount(); i++) {
if (etCarLocation.getText().toString().equals(etCarLocation.getAdapter().getItem(i))) {
carLoactionStr = etCarLocation.getAdapter().getItem(i).toString();
} else
carLoactionStr = null;
}
}
#Override
public void afterTextChanged(Editable s) {
}
});
//start autocomplete after 1 letter
etCarLocation.setThreshold(1);
etCarLocation.performCompletion();
etCarLocation.setAdapter(listOfCities);
}
/**
* Listener for car status spinner
* #param parent
* #param view
* #param position
* #param id
*/
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
Spinner spinner = (Spinner)parent;
if(spinner.getId() == R.id.createPostCarStatusSpinner_ID){
TextView textView = (TextView) view;
carStatusStr = textView.getText().toString();
addDynamicViews(position);
}
else if(spinner.getId() == R.id.createPostCarBrandSpinner_ID){
TextView textView = (TextView) view;
carBrandStr = textView.getText().toString();
}
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
/**
* Dynamic views creation done by handling user spinner selection for first hand or second hand car status.
*#param position: position of selected value from spinner
*/
public void addDynamicViews(int position){
if(position == 1){
layoutContainer.addView(tvDistance);
layoutContainer.addView(etDistance);
}
else if(position == 0){
mFirebaseUtilities.removeNodeDynamically();
layoutContainer.removeView(tvDistance);
layoutContainer.removeView(etDistance);
}
}
#Override
public void setBrand(String brand) {
}
#Override
public void setPrice(int price) {
}
#Override
public void setLocation(String location) {
}
#Override
public void setDescription(String description) {
}
#Override
public void setModel(String model) {
}
#Override
public void setDistance(long distance) {
}
#Override
public void setStatus(String status) {
}
#Override
public void setPicture(String picture) {
}
#Override
public String getBrand() {
return carBrandStr;
}
#Override
public String getDescription() {
return etDescription.getText().toString();
}
#Override
public String getLocation() {
return carLoactionStr;
}
#Override
public String getModel() {
return etModel.getText().toString();
}
#Override
public String getStatus() {
return carStatusStr;
}
#Override
public String getPicture() {
return null;
}
#Override
public int getPrice() {
String priceViewTemp = etPrice.getText().toString();
if (priceViewTemp.equals("")) {
return 0;
} else {
return Integer.valueOf(etPrice.getText().toString());
}
}
#Override
public long getDistance() {
String distanceViewTemp = etDistance.getText().toString();
if (distanceViewTemp.equals("")) {
return 0;
} else {
return Integer.valueOf(etDistance.getText().toString());
}
}
/*************************************** Firebase *******************************************/
private void setupFirebaseAuth() {
mAuth = FirebaseAuth.getInstance();
mFirebaseDatabase = FirebaseDatabase.getInstance();
mDatabaseReference = mFirebaseDatabase.getReference();
mAuthStateListener = new FirebaseAuth.AuthStateListener() {
#Override
public void onAuthStateChanged(#NonNull FirebaseAuth firebaseAuth) {
FirebaseUser user = firebaseAuth.getCurrentUser();
if (user != null) {
//User is signed in
Log.d(TAG, "onAuthStateChanged: user signed in : " + user.getUid());
} else {
//User is signed out
Log.d(TAG, "onAuthStateChanged: user signed out");
}
}
};
mDatabaseReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
#Override
public void onPause() {
super.onPause();
if (singleValueEventListener != null) {
mDatabaseReference.removeEventListener(singleValueEventListener);
}
}
#Override
public void onResume(){
super.onResume();
mDatabaseReference.addListenerForSingleValueEvent(singleValueEventListener);
}
}
Firebase Structure
Firebase Structure
Haven't read if much, but i first noticed something here.
for(DataSnapshot ds :dataSnapshot.getChildren()){
if(ds.getKey().equals(context.getString(R.string.dbname_car_post))){
try{ }
The key you're trying to retrieve is an Object. So i would propose we convert it string first, sample down here.
for(DataSnapshot ds :dataSnapshot.getChildren()){
Object myKey=ds.getKey;
if(myKey.toString().equals(context.getString(R.string.dbname_car_post))){
try{ }
}}
Let me know what happens next!