I am working on an android application, and I am using Firebase database,
this database showing an Items list.
This is my code.
private DatabaseReference root;
ListView elementList;
ArrayAdapter<mdlItem> adapter;
ArrayList<mdlItem> itemsList;
Context context;
private void loadFireDataBase() {
root = FirebaseDatabase.getInstance().getReference().child("Offers");
root.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Add_Chat(dataSnapshot, "Add");
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
Add_Chat(dataSnapshot, "Edit");
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
Add_Chat(dataSnapshot , "Delete");
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
//Log.i(tag, "onCreateView");
return creatList(inflater, container);
}
private View creatList(LayoutInflater inflater, #Nullable ViewGroup container) {
loadFireDataBase();
context = getActivity();
itemsList = new ArrayList<mdlItem>();
View view;
view = inflater.inflate(R.layout.freg_main_content, container, false);
elementList = (ListView) view.findViewById(R.id.customListView);
Collections.sort(itemsList, new Comparator<mdlItem>() {
#Override
public int compare(mdlItem data1, mdlItem data2) {
if (data1.getOfferIndex() > data2.getOfferIndex())
return 1;
else
return 0;
}
});
adapter = new offersArrayAdapter(context, R.layout.item_list_layout, itemsList);
elementList.setAdapter(adapter);
elementList.setOnItemClickListener(this);
return view;
}
private void Add_Chat(DataSnapshot dataSnapshot, String theCase) {
Map<String, Object> question = null;
try {
String theOfferCode = dataSnapshot.getKey();
question = (Map<String, Object>) dataSnapshot.getValue();
mdlItem mdl = new mdlItem();
mdl.setOfferCode(theOfferCode);
mdl.setRestCode(Integer.parseInt(String.valueOf(question.get("itemCode"))));
mdl.setRestName(question.get("itmeName").toString());
switch (theCase) {
case "Add":
itemsList.add(mdl);
break;
case "Delete":
itemsList.remove(mdl);
break;
case "Edit":
//??
break;
}
adapter.notifyDataSetChanged();
} catch (Exception ex) {
Toast.makeText(context, ex.getMessage(), Toast.LENGTH_LONG).show();
}
}
The problem is I can't get any node index through 'dataSnapshot' object, to control the list when I make Delete and Update.
And another thing how to use Add_Chat method to add the mdl item in the top of other items list.
I already catch the Edit, delete and add in the database root but I can't control how to use these events in the right way
I think I need to get the 'dataSnapshot' object to remove it or set an update on it, or if there is another solution
Can any one help me in this.
Firebase snapshots are not index-based. If you need a mapping from the key in the database to the index in your adapter, you will need to maintain this mapping yourself.
For a simple version of this, have a look at the FirebaseArray class in the FirebaseUI library. It maintains a list of all snapshots that are currently in the array:
private final List<DataSnapshot> mSnapshots = new ArrayList<>();
And then in the onChildAdded methods adds, determines where the new item fits in the list and adds it:
public void onChildAdded(#NonNull DataSnapshot snapshot, #Nullable String previousChildKey) {
int index = 0;
if (previousChildKey != null) {
index = getIndexForKey(previousChildKey) + 1;
}
mSnapshots.add(index, snapshot);
notifyOnChildChanged(ChangeEventType.ADDED, snapshot, index, -1);
}
It does the equivalent for all other onChild... methods, so that it can look up the index for a specific key when needed with its getIndexForKey method:
private int getIndexForKey(#NonNull String key) {
int index = 0;
for (DataSnapshot snapshot : mSnapshots) {
if (snapshot.getKey().equals(key)) {
return index;
} else {
index++;
}
}
throw new IllegalArgumentException("Key not found");
}
Just For recording, This is the correct solution
int index = -1;
for (mdlItem item: itemsList) {
if (item.getIndex() == mdl.getIndex()) {
index = itemsList.indexOf(item);
}
}
Related
I'm creating a simple chat app wherein every chatbubbles will be shown in a RecyclerView, now I noticed that every time ill enter a new data coming from Firebase RealTime Database, the old data's / or let's say the old chat bubbles will disappear and reappear once the newly added data has been displayed. I would like the old chat bubbles to not behave just like that, I would like it to remain appeared the whole time.
Here's my method to load every chatbubbles:
private void LoadChat() {
Query orderPosts = ChatRef.orderByChild("servertimestamp");
options = new FirebaseRecyclerOptions.Builder<Chat>().setQuery(orderPosts, Chat.class).build();
adapter = new FirebaseRecyclerAdapter<Chat, MyViewHolder12>(options) {
#Override
protected void onBindViewHolder(#NonNull MyViewHolder12 holder, int position, #NonNull Chat model) {
final String userpower = model.getPower();
final String pow = "Admin";
if (userpower.equals(pow)){
holder.chat_userpower.setVisibility(View.VISIBLE);
holder.chat_userpower.setText(model.getPower());
}
else{
holder.chat_userpower.setVisibility(View.GONE);
}
final String quotedc = model.getQuotedchat();
final String quotedn = model.getQuotedname();
if (quotedc == null){
holder.quotedchatbox.setVisibility(View.GONE);
holder.quotedchatboxlayout.setVisibility(View.GONE);
holder.quotedchatdescription.setVisibility(View.GONE);
}
else{
holder.quotedchatboxlayout.setVisibility(View.VISIBLE);
holder.quotedchatbox.setVisibility(View.VISIBLE);
holder.quotedchatdescription.setVisibility(View.VISIBLE);
holder.quotedchatdescription.setText("Quoted "+ model.getQuotedname() +" " + model.getQuotedchat());
}
holder.chat_usercomment.setText(model.getChat());
Picasso.get().load(model.getProfileimage()).placeholder(R.drawable.profile).into(holder.chat_userimage);
holder.chat_userdep.setText(model.getDep());
holder.chat_date.setText(model.getDate());
holder.chat_username.setText(model.getUsername());
holder.nestedchat_reply.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
quote = true;
quotedname = model.getUsername();
//CommentKey = getRef(holder.getAdapterPosition()).getKey();
quoting.setVisibility(View.VISIBLE);
quotedchat = model.getChat();
quoting.setText("Quoting "+ quotedname + ": " + model.getChat());
quoting.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
quote = false;
quoting.setVisibility(View.GONE);
}
});
}
});
}
#NonNull
#Override
public MyViewHolder12 onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.allchatlayout, parent, false);
return new MyViewHolder12(view);
}
};
adapter.startListening();
allchatlist.setAdapter(adapter);
}
here's my layoutmanager:
LinearLayoutManager lm = new LinearLayoutManager(this);
lm.setReverseLayout(false);
lm.setStackFromEnd(false);
allchatlist.setNestedScrollingEnabled(false);
allchatlist.setLayoutManager(lm);
here's my code calling the method:
ChatRef = FirebaseDatabase.getInstance().getReference().child("Forums").child(ChatRoomNameKey).child("Forum ChatRoom");
ChatRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
if (snapshot.exists()){
LoadChat();
}
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
To achieve that you will have to use RecyclerView DiffUtill class, more info here:
https://developer.android.com/reference/androidx/recyclerview/widget/DiffUtil
In a nutshell you have to create a diff util class:
class CustomItemDiffUtils(
private val oldList: List<CustomItem>,
private val newList: List<CustomItem>
) : DiffUtil.Callback() {
override fun getOldListSize(): Int = oldList.size
override fun getNewListSize(): Int = newList.size
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition] == newList[newItemPosition]
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition].data == newList[newItemPosition].data
}
}
And use this diff class in your adapter fro example with a method which can be called from the view:
fun updateList(newList: List<CustomItem>) {
val diffResult = DiffUtil.calculateDiff(CustomItemDiffUtils(oldList, newList))
oldList = newList
diffResult.dispatchUpdatesTo(this)
}
Hope this helps.
I fixed the problem by removing the line:
Query orderPosts = ChatRef.orderByChild("servertimestamp");
options = new FirebaseRecyclerOptions.Builder<Chat>().setQuery(orderPosts, Chat.class).build();
Removing that 2 lines of code from that method and putting it somewhere else inside the Activity fixed the blinking problem of my app when a new data has been added.
I wanted to know how to load more data in recylcer view using firestore.
Query query = FirebaseFirestore.getInstance()
.collection("ie").limit(5);
adapter=new InterviewAdapter(this,query);
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
Adapter class looks like this:
public class InterviewAdapter extends FireStoreAdapter<InterviewAdapter.ViewHolder> {
public interface OnInterviewSelectedListener {
void onInterviewSelected(DocumentSnapshot interview);
}
private InterviewAdapter.OnInterviewSelectedListener mListener;
public InterviewAdapter(Query query, OnInterviewSelectedListener listener) {
super(query);
mListener = listener;
}
#Override
public InterviewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
return new InterviewAdapter.ViewHolder(inflater.inflate(R.layout.ie, parent, false));
}
#Override
public void onBindViewHolder(InterviewAdapter.ViewHolder holder, int position) {
holder.bind(getSnapshot(position), mListener);
}
static class ViewHolder extends RecyclerView.ViewHolder {
TextView title,companyName,username,views,isHired;
public ViewHolder(View itemView) {
super(itemView);
title= (TextView) itemView.findViewById(R.id.title);
companyName= (TextView) itemView.findViewById(R.id.companyName);
username= (TextView) itemView.findViewById(R.id.username);
views= (TextView) itemView.findViewById(R.id.views);
isHired= (TextView) itemView.findViewById(R.id.isHired);
}
public void bind(final DocumentSnapshot snapshot,
final OnInterviewSelectedListener listener) {
InterviewExperience experience;
String companyName=snapshot.getString("companyName");
boolean isHired=Boolean.valueOf(snapshot.getBoolean("isHired"));
String username=snapshot.getString("username");
long views=new Double(Double.valueOf(snapshot.getDouble("views"))).longValue();
String id=snapshot.getId();
String title=snapshot.getString("title");
experience=new InterviewExperience(id,title,companyName,username,isHired,views,null,null);
this.title.setText(experience.getTitle());
this.companyName.setText("Company Name: "+experience.getCompanyName());
this.isHired.setText("Hired: "+experience.isHired());
this.views.setText("Views: "+experience.getViews()+"");
this.username.setText("Created By: "+experience.getUsername());
// Click listener
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (listener != null) {
listener.onInterviewSelected(snapshot);
}
}
});
}
}
}
public abstract class FireStoreAdapter<VH extends RecyclerView.ViewHolder>
extends RecyclerView.Adapter<VH>
implements EventListener<QuerySnapshot> {
private static final String TAG = "FirestoreAdapter";
private Query mQuery;
private ListenerRegistration mRegistration;
private ArrayList<DocumentSnapshot> mSnapshots = new ArrayList<>();
public FireStoreAdapter(Query query) {
mQuery = query;
}
#Override
public void onEvent(QuerySnapshot documentSnapshots, FirebaseFirestoreException e) {
if (e != null) {
Log.w(TAG, "onEvent:error", e);
onError(e);
return;
}
// Dispatch the event
Log.d(TAG, "onEvent:numChanges:" + documentSnapshots.getDocumentChanges().size());
for (DocumentChange change : documentSnapshots.getDocumentChanges()) {
switch (change.getType()) {
case ADDED:
onDocumentAdded(change);
break;
case MODIFIED:
onDocumentModified(change);
break;
case REMOVED:
onDocumentRemoved(change);
break;
}
}
onDataChanged();
}
public void startListening() {
if (mQuery != null && mRegistration == null) {
mRegistration = mQuery.addSnapshotListener(this);
}
}
public void stopListening() {
if (mRegistration != null) {
mRegistration.remove();
mRegistration = null;
}
mSnapshots.clear();
notifyDataSetChanged();
}
public void setQuery(Query query) {
// Stop listening
stopListening();
// Clear existing data
mSnapshots.clear();
notifyDataSetChanged();
// Listen to new query
mQuery = query;
startListening();
}
#Override
public int getItemCount() {
return mSnapshots.size();
}
protected DocumentSnapshot getSnapshot(int index) {
return mSnapshots.get(index);
}
protected void onDocumentAdded(DocumentChange change) {
mSnapshots.add(change.getNewIndex(), change.getDocument());
notifyItemInserted(change.getNewIndex());
}
protected void onDocumentModified(DocumentChange change) {
if (change.getOldIndex() == change.getNewIndex()) {
// Item changed but remained in same position
mSnapshots.set(change.getOldIndex(), change.getDocument());
notifyItemChanged(change.getOldIndex());
} else {
// Item changed and changed position
mSnapshots.remove(change.getOldIndex());
mSnapshots.add(change.getNewIndex(), change.getDocument());
notifyItemMoved(change.getOldIndex(), change.getNewIndex());
}
}
protected void onDocumentRemoved(DocumentChange change) {
mSnapshots.remove(change.getOldIndex());
notifyItemRemoved(change.getOldIndex());
}
protected void onError(FirebaseFirestoreException e) {};
protected void onDataChanged() {}
}
I used Firestore Adapter code which was given in samples of firestore documentation. Can anyone tell how to use the query object to load more data?
How to load the next 5 items in the recycler view when users scrolls to the end of the list?
You can paginate your Query's result using Query's methods like, startAt(), startAfter(), endAt(), endBefore() with a specified DocumentSnapshot.
If I considered your collection is called "interviews", you can add a method to your FireStoreAdapter like this:
private void paginate(final DocumentSnapshot last, final int limit) {
final Query subset;
if (last == null) {
subset = db.collection("interviews")
.limit(limit);
} else {
subset = db.collection("interviews")
.startAfter(last)
.limit(limit);
}
setQuery(subset);
}
You can perserve the last DocumentSnapshot within onEvent():
final List<DocumentChange> changes = documentSnapshots.getDocumentChanges();
final DocumentSnapshot lastDocument = changes.get(changes.size() - 1).getDocument();
Finally, when users scrolls to the end of the list:
paginate(lastDocument, 5);
And onDocumentAdded() will take care of it. Be carfure NOT to use startAt() because it will not execlude the last one (that already at the end of your list, and will duplicate it).
Below image shows my Firebase database structure:
All data retrieved successfully. Here is my model class.
public class Post
{
public String lastname;
public String postid;
public long timestamp;
public HashMap<String,Boolean> count;
public Post()
{
}
public Post(String lastname, long timestamp, String postid,HashMap count)
{
this.lastname=lastname;
this.timestamp=timestamp;
this.postid=postid;
this.count=count;
}
public HashMap<String, Boolean> getCounts() {
return count;
}
public void setCounts(HashMap<String, Boolean> count) {
this.count = count;
}
In Main Activity i used to get data
mAdapter = new PostAdapter(MainActivity.this);
getAllPost(null);
postList.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (!recyclerView.canScrollVertically(1))
{
loaded=loaded+10;
if (totalPost== mAdapter.getItemCount())
{
Toast.makeText(MainActivity.this, "no more post", Toast.LENGTH_SHORT).show();
}
else
{
getAllPost(mAdapter.getLastItemId());
}
}
}
});
postList.setAdapter(mAdapter);
private void getAllPost(final String nodeId)
{
final Query query;
final int left= (int) (totalPost-mAdapter.getItemCount());
Toast.makeText(this, String .valueOf(left), Toast.LENGTH_SHORT).show();
if (nodeId == null)
{
query = PostRef
.orderByChild("timestamp")
.limitToLast(mPostsPerPage);
}
else
{
if (left<10)
{
query = PostRef
.orderByChild("timestamp")
.limitToFirst(left);
}
else
{
Long time=Long.parseLong(nodeId);
query = PostRef
.orderByChild("timestamp").endAt(time)
.limitToLast(10);
}
}
query.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
List<Post> userModels = new ArrayList<>();
for (DataSnapshot userSnapshot : dataSnapshot.getChildren())
{
userModels.add(userSnapshot.getValue(Post.class));
}
if (!(nodeId ==null))
{
if (left>10)
{
userModels.remove(9);
}
}
Collections.reverse(userModels);
mAdapter.addAll(userModels);
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException();
}
});
}
And in adapter:
public class PostAdapter extends RecyclerView.Adapter<PostHolder>
{
List<Post> mPost;
Context mContext;
public PostAdapter(Context c) {
this.mPost = new ArrayList<>();
mContext=c;
}
#NonNull
#Override
public PostHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
return new PostHolder(LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.all_post_layout, viewGroup, false));
}
#Override
public void onBindViewHolder(#NonNull final PostHolder postHolder, final int i) {
final String PostKey=mPost.get(i).getPostid();
FirebaseAuth mAuth=FirebaseAuth.getInstance();
final String currentUserID=mAuth.getCurrentUser().getUid();
final DatabaseReference post=FirebaseDatabase.getInstance().getReference().child("Posts");
showCounts(postHolder,i);
setCountsButton(postHolder,i,currentUserID);
tapOnCounts(postHolder,i,currentUserID,post,PostKey);
}
private void tapOncounts(final PostHolder postHolder, final int i, final String currentUserID, final DatabaseReference post, final String postKey)
{
postHolder.countsButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v)
{
if (mPost.get(i).getCounts() !=null)
{
if(mPost.get(i).getCounts().containsKey(currentUserID))
{
post.child(postKey).child("counts").child(currentUserID).removeValue();
postHolder.countsButton.setImageResource(R.drawable.discounts);
}
else
{
postHolder.countsButton.setImageResource(R.drawable.counts);
post.child(postKey).child("counts").child(currentUserID).setValue(true);
}
}
else
{
postHolder.countsButton.setImageResource(R.drawable.counts);
post.child(postKey).child("counts").child(currentUserID).setValue(true);
}
}
});
}
private void setcountsButton(final PostHolder postHolder, int i, String currentUserID)
{
if (mPost.get(i).getCounts() !=null)
{
if(mPost.get(i).getCounts().containsKey(currentUserID))
{
postHolder.countsButton.setImageResource(R.drawable.counts);
}
else
{
postHolder.countsButton.setImageResource(R.drawable.discounts);
}
}
}
private void showCounts(PostHolder postHolder, int i)
{
if((mPost.get(i).getCounts() !=null))
{
postHolder.noOfcounts.setText(String.valueOf(mPost.get(i).getCounts().size()));
}
else
{
postHolder.noOfcounts.setText("0");
}
}
#Override
public int getItemCount() {
return mPost.size();
}
public void addAll(List<Post> newPost) {
int initialSize = mPost.size();
mPost.addAll(newPost);
notifyItemRangeInserted(initialSize, newPost.size());
}
public String getLastItemId() {
return String.valueOf(mPost.get(mPost.size() - 1).getTimestamp());
}
}
All is successfully but whenever total no. of child change(new child added OR old child removed) in count node recylerview is not update. It will only update when i tried to go another activity and come to rerun in MainActivity.
To get realtime updates, you should use Query's addValueEventListener(ValueEventListener listener) method:
Add a listener for changes in the data at this location.
When using addListenerForSingleValueEvent(ValueEventListener listener):
Add a listener for a single change in the data at this location.
Edit:
To get the size of your list, please change the following line of code:
holder.count.setText(String.valueOf(mPost.get(i).getCount().size));
to
holder.count.setText(String.valueOf(getItemCount());
Whenever total number of child changes then your list of Post modal also changes i.e. userModels in your case. Hence whenever your list of model changes your adapter needs to be notified. Hence my guess is to add notifyDataSetChanged to adapter.
Try this:
query.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
List<Post> userModels = new ArrayList<>();
for (DataSnapshot userSnapshot : dataSnapshot.getChildren()) {
userModels.add(userSnapshot.getValue(Post.class));
}
mAdapter.notifyDataSetChanged(); //<<changes made HERE
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
})
For this to work I hope userModels is instance variable to your MainActivity and is set to mAdapter during initialization.
This is my ArrayAdapter:
public class SackViewAdapter extends ArrayAdapter<PostInfo> {
private ArrayList<PostInfo> postInfo;
private Context context;
private LayoutInflater inflater;
public SackViewAdapter(#NonNull Context context, int resource,ArrayList<PostInfo> postInfo) {
super (context, resource);
this.context = context;
this.postInfo = postInfo;
inflater = (LayoutInflater) context.getSystemService(LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
Log.e ("Size", String.valueOf (postInfo.size ()));
return postInfo.size ();
}
#Nullable
#Override
public PostInfo getItem(int i) {
return postInfo.get (i);
}
#NonNull
#Override
public View getView(int i, View view, #NonNull ViewGroup parent) {
view = inflater.inflate (R.layout.card_sack_view, parent,false);
SelectableRoundedImageView imageView = view.findViewById (R.id.image_view);
TextView name = view.findViewById (R.id.nameCards);
TextView username = view.findViewById (R.id.usernameCards);
imageView.setDrawingCacheEnabled (true);
name.setText (postInfo.get (i).name);
username.setText (postInfo.get (i).username);
try{
Glide.with (context).load (postInfo.get (i).Url).into (imageView);
}catch (Exception e){
Toast.makeText (context, e.getMessage (), Toast.LENGTH_SHORT).show ();
}
Log.e ("i", String.valueOf (i));
return view;
}
}
This is my Main Activity:
public class WallActivity extends AppCompatActivity {
FloatingActionButton newFloatingButton;
SackViewAdapter baseAdapter;
ArrayList<PostInfo> postInfos;
DatabaseReference reference = FirebaseDatabase.getInstance ().getReference ();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate (savedInstanceState);
setContentView (R.layout.activity_wall);
postInfos = new ArrayList<PostInfo> ();
String isDirectly = getIntent ().getStringExtra ("directly");
if(isDirectly.equals ("yes")){
AuthAsyncTask authAsyncTask = new AuthAsyncTask (WallActivity.this);
authAsyncTask.execute ();
}
getData ();
CardStackView cardStackView = findViewById(R.id.cardView);
newFloatingButton = findViewById(R.id.newFloatingButton);
newFloatingButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent(WallActivity.this, NewItemActivity.class);
startActivity(intent);
overridePendingTransition(R.anim.slide_up, R.anim.slide_down);
}
});
cardStackView.setCardEventListener(new CardStackView.CardEventListener() {
#Override
public void onCardDragging(float percentX, float percentY) {
}
#Override
public void onCardSwiped(SwipeDirection direction) {
}
#Override
public void onCardReversed() {
}
#Override
public void onCardMovedToOrigin() {
}
#Override
public void onCardClicked(int index) {
Toast.makeText(WallActivity.this, "Clicked", Toast.LENGTH_SHORT).show();
}
});
baseAdapter = new SackViewAdapter (WallActivity.this,android.R.layout.simple_list_item_1, postInfos);
cardStackView.setAdapter(baseAdapter);
}
public void getData(){
reference.addValueEventListener (new ValueEventListener () {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
DataSnapshot posts = dataSnapshot.child ("Posts");
for (DataSnapshot time: posts.getChildren ()){
DataSnapshot url = time.child ("Url");
DataSnapshot name = time.child ("Name");
DataSnapshot username = time.child ("Username");
DataSnapshot date = time.child ("Date");
PostInfo postInfo = new PostInfo (String.valueOf (url.getValue ()), String.valueOf (name.getValue ()), String.valueOf (username.getValue ()), String.valueOf (date.getValue ()));
postInfos.add (postInfo);
}
baseAdapter.notifyDataSetChanged ();
}
#Override
public void onCancelled(DatabaseError databaseError) {
Toast.makeText (WallActivity.this, "Error 411: " + databaseError.getMessage (), Toast.LENGTH_SHORT).show ();
}
});
}
}
When I get all the data from getData() function I update my Array Adapter but after updating real size of the array list comes out to be 7 but the getView function takes the value of i from 0 to 2 only. It is not adding all the values from the ArrayList and it is showing same values again and again rather than showing different values.This is the log when the screen loads as I have added Log.e in getView:
04-06 13:04:26.797 10246-10246/lifeline.learn.com.hotornot E/Value of i: 0
04-06 13:04:26.804 10246-10246/lifeline.learn.com.hotornot E/Value of i: 1
04-06 13:04:26.814 10246-10246/lifeline.learn.com.hotornot E/Value of i: 2
It is not going over 2. But when I Log in getCount it returns 7.
Currently, your adapter's getCount() returns the size of urls.size (); but you are passing other 2 arraylists as well, which will only take the size of urls arraylist.
If you pass an Arraylist<UserObject>, you could add all the data to this object and then return the size as userObjects.size();
So the better approach would be to create an object, say UserObject and create an arraylist with this object like Arraylist<UserObject>
UserObject.java
public class UserObject {
String urls;
String names;
String usernames;
String dates;
public UserObject(String urls, String names, String usernames, String dates) {
this.urls = urls;
this.names = names;
this.usernames = usernames;
this.dates = dates;
}
}
Declare an arraylist
ArrayList<UserObject> userData=new ArrayList<>();
Now, change like this
public void onDataChange(DataSnapshot dataSnapshot) {
DataSnapshot posts = dataSnapshot.child ("Posts");
for (DataSnapshot time: posts.getChildren ()){
DataSnapshot url = time.child ("Url");
DataSnapshot name = time.child ("Name");
DataSnapshot username = time.child ("Username");
DataSnapshot date = time.child ("Date");
UserObject user=new UserObject(String.valueOf (url.getValue ()),String.valueOf (name.getValue ()),String.valueOf (username.getValue ()),String.valueOf (date.getValue ()));
userData.add(user);
}
baseAdapter.notifyDataSetChanged ();
}
Your adapter constructor will be like this
public SackViewAdapter(#NonNull Context context, int resource,ArrayList<UserObject> userObjects)
Please do not call these in getData()
You have already initialised it onCreate(), and addValueEventListener is called everytime there is a change
urls.clear ();
usernames.clear ();
dates.clear ();
times.clear ();
Also Its better if you use custom object arraylist rather than using three separate lists of type string and manage them
check your url size if their size two then take only 0 to 2.
because you add in adapter in size..
#Override
public int getCount() {
return urls.size (); // in that provide large size of your data.
}
then after when you bind adapter then after used ..
notifyDataSetChanged ();
I am new in Android and working on an Android app which can retrieve data from firebase. There is a weird problem. I already successfully implemented the retrieving function for one data model of my program, and I used same codes just changed the variables but it doesn't work for another data model. I did many tests and I think the problem is in FirebaseHelper cuz there is no any data returned from the data snapshot. The error is
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.ListView.setAdapter(android.widget.ListAdapter)' on a null object reference
The codes are shown below:
The Activity that shows the retrieved data in a listview.
public class TimeTableActivity extends AppCompatActivity {
DatabaseReference db;
FirebaseHelper firebasehelper;
TimeTableAdapter adapter;
ListView lv_CourseList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_time_table);
//GET INTENT
Intent intent = this.getIntent();
String majorID = intent.getStringExtra("MAJOR_ID");
lv_CourseList = (ListView) findViewById(R.id.lv_CourseList);
//INITIALIZE FIREBASE DB
db= FirebaseDatabase.getInstance().getReference();
firebasehelper=new FirebaseHelper(db);
//ADAPTER
adapter = new TimeTableAdapter(getApplicationContext(),firebasehelper.retrieveCourse(majorID, new CourseCallbacks() {
#Override
public void onCourseCallback(ArrayList<CourseInfo> courseInfos) {
lv_CourseList.setAdapter(adapter);
}
}));
lv_CourseList.setAdapter(adapter);
}
}
FirebaseHelper:
public class FirebaseHelper{
private DatabaseReference db;
private ArrayList<Major> majors = new ArrayList<>();
private ArrayList<CourseInfo> courseInfos = new ArrayList<>();
public FirebaseHelper(DatabaseReference db) {
this.db = db;
}
//Save the Major info. into db
public Boolean saveMajor(Major major)
{
Boolean saved = null;
if(major==null)
{
saved =false;
}else
{
try
{
db.child("Major").push().setValue(major);
saved =true;
}catch (DatabaseException e)
{
e.printStackTrace();
saved =false;
}
}
return saved;
}
//Save the Course info. into db
public Boolean saveCourse(CourseInfo courseInfo)
{
Boolean saved = null;
if(courseInfo==null)
{
saved =false;
}else
{
try
{
db.child("CourseInfo").push().setValue(courseInfo);
saved =true;
}catch (DatabaseException e)
{
e.printStackTrace();
saved =false;
}
}
return saved;
}
public ArrayList<Major> retrieveMajor(final MajorCallbacks majorCallbacks){
ChildEventListener childEventListener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
for (DataSnapshot ds : dataSnapshot.getChildren()) {
Major major = ds.getValue(Major.class);
if (major != null && major.getMajor_id() != null) {
majors.add(major);
}
}
majorCallbacks.onMajorCallback(majors);
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
for (DataSnapshot ds : dataSnapshot.getChildren()) {
Major major = ds.getValue(Major.class);
if (major != null && major.getMajor_id() != null) {
majors.add(major);
}
}
majorCallbacks.onMajorCallback(majors);
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
db.addChildEventListener(childEventListener);
if (!majors.isEmpty()){
db.removeEventListener(childEventListener);
}
return majors;
}
public ArrayList<CourseInfo> retrieveCourse(String majorID, final CourseCallbacks courseCallbacks){
ChildEventListener childEventListener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
for (DataSnapshot ds : dataSnapshot.getChildren()) {
CourseInfo courseInfo = ds.getValue(CourseInfo.class);
if (courseInfo != null && courseInfo.getCourse_id() != null) {
courseInfos.add(courseInfo);
}
}
courseCallbacks.onCourseCallback(courseInfos);
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String prevChildKey) {
for (DataSnapshot ds : dataSnapshot.getChildren()) {
CourseInfo courseInfo = ds.getValue(CourseInfo.class);
if (courseInfo != null && courseInfo.getCourse_id() != null) {
courseInfos.add(courseInfo);
}
}
courseCallbacks.onCourseCallback(courseInfos);
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String prevChildKey) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
db.child("CourseInfo").orderByChild("major_id").equalTo(majorID).addChildEventListener(childEventListener);
return courseInfos;
}
}
The retrieveMajor method works well even though it returns many null data and repetitive data, and I used the codes of retrieveMajor in retrieveCourse. It doesn't work, the courseInfos is always null. Even though I changed db.child("CourseInfo").orderByChild("major_id").equalTo(majorID).addChildEventListener(childEventListener);
to
db.addChildEventListener(childEventListener);
,still nothing is retrieved which means the problem is not the query (Probably the query is also wrong).
Adapter:
public class TimeTableAdapter extends BaseAdapter {
Context context;
ArrayList<CourseInfo> courseInfos;
public TimeTableAdapter(Context context, ArrayList<CourseInfo> courseInfos) {
this.context = context;
this.courseInfos = courseInfos;
}
#Override
public int getCount() {
return courseInfos.size();
}
#Override
public Object getItem(int pos) {
return courseInfos.get(pos);
}
#Override
public long getItemId(int pos) {
return pos;
}
#Override
public View getView(int position, View convertView, ViewGroup viewGroup) {
LayoutInflater inflater = LayoutInflater.from(context);
if(convertView == null)
{
convertView= LayoutInflater.from(context).inflate(R.layout.model_timetable,viewGroup,false);
}
TextView tv_courseid= (TextView) convertView.findViewById(R.id.tv_courseid);
TextView tv_coursename= (TextView) convertView.findViewById(R.id.tv_coursename);
TextView tv_courseinstructor= (TextView) convertView.findViewById(R.id.tv_courseinstructor);
TextView tv_courseavailable= (TextView) convertView.findViewById(R.id.tv_courseavailable);
final CourseInfo courseInfo= (CourseInfo) this.getItem(position);
tv_courseid.setText(courseInfo.getCourse_id());
tv_coursename.setText(courseInfo.getCourse_name());
tv_courseinstructor.setText(courseInfo.getCourse_instructor());
tv_courseavailable.setText(courseInfo.getCourse_available());
return convertView;
}
}
Data model:
#IgnoreExtraProperties
public class CourseInfo {
public String course_id;
public String course_name;
public int course_section;
public String course_type;
public double course_crdhrs;
public String course_days;
public String course_times;
public String course_location;
public int course_max;
public int course_cur;
public int course_available;
public int course_wl;
public double course_per;
public String course_instructor;
public String course_description;
public String course_prerequire;
public String major_id;
public CourseInfo() {
}
public CourseInfo(String course_id, String course_name, int course_section, String course_type, double course_crdhrs, String course_days, String course_times, String course_location, int course_max, int course_cur, int course_available, int course_wl, double course_per, String course_instructor, String course_description, String course_prerequire, String major_id) {
this.course_id = course_id;
this.course_name = course_name;
this.course_section = course_section;
this.course_type = course_type;
this.course_crdhrs = course_crdhrs;
this.course_days = course_days;
this.course_times = course_times;
this.course_location = course_location;
this.course_max = course_max;
this.course_cur = course_cur;
this.course_available = course_available;
this.course_wl = course_wl;
this.course_per = course_per;
this.course_instructor = course_instructor;
this.course_description = course_description;
this.course_prerequire = course_prerequire;
this.major_id = major_id;
}
public String getCourse_id() {
return course_id;
}
public String getCourse_name() {
return course_name;
}
public int getCourse_section() {
return course_section;
}
public String getCourse_type() {
return course_type;
}
public double getCourse_crdhrs() {
return course_crdhrs;
}
public String getCourse_days() {
return course_days;
}
public String getCourse_times() {
return course_times;
}
public String getCourse_location() {
return course_location;
}
public int getCourse_max() {
return course_max;
}
public int getCourse_cur() {
return course_cur;
}
public int getCourse_available() {
return course_available;
}
public int getCourse_wl() {
return course_wl;
}
public double getCourse_per() {
return course_per;
}
public String getCourse_instructor() {
return course_instructor;
}
public String getCourse_description() {
return course_description;
}
public String getCourse_prerequire() {
return course_prerequire;
}
public String getMajor_id() {
return major_id;
}
}
If you need more codes or information, please comment and let me know. I will really appreciate if you can also help me solve the null data and repetitive data problem cuz it makes the listview show many blank and repetitive items.
You cannot return something now that hasn't been loaded yet. With other words, you cannot just simply create a method that as a return type, an ArrayList<Major> and in the same time return that object. This is happening because those methods, onChildAdded(), onChildChanged() and so on, have an asynchronous behaviour, which means that are called even before you are getting/updating the data from/in the database. To solve this, you can move the declaration of that ArrayList inside one method and do what you need to do with it or dive into asynchronous world and use the last part of my answer from this post. You can take also a look at this video for a better understanding.