1

I am developing an android app that displays the ranks of students based on their marks retrieved from the firebase database. Everything is working fine but, when I update the marks in the db, it keeps the old data and adds the new data in the recyclerView. I can restart the app to refresh the data. But while it is still running, it shows the old data too.

Below is my firebase data:

Student1: {
      c: 70,
      cPlus: 90,
      java: 70,
      name: "Samson",
      regno: "16sksb7034",
      unix: 60
       }
Student2: {
      c: 20,
      cPlus: 85,
      java: 68,
      name: "Samson",
      regno: "16sksb7034",
      unix: 86
       }
Student3: {
      c: 70,
      cPlus: 70,
      java: 80,
      name: "Samson",
      regno: "16sksb7034",
      unix: 90
       }

Here is my dataModel class:

public class Marks {
private String name;
private String regno;
private int c;
private int cPlus;
private int  java;
private int unix;
private int percentage;

public Marks() {}

public Marks(int c, int cPlus, int java, int unix) {
    this.c = c;
    this.cPlus = cPlus;
    this.java = java;
    this.unix = unix;
}

public int getPercentage() {
    return percentage;
}

public void setPercentage(int percentage) {
    this.percentage = percentage;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public String getRegno() {
    return regno;
}

public void setRegno(String regno) {
    this.regno = regno;
}

public int getC() {
    return c;
}

public void setC(int c) {
    this.c = c;
}

public int getcPlus() {
    return cPlus;
}

public void setcPlus(int cPlus) {
    this.cPlus = cPlus;
}

public int getJava() {
    return java;
}

public void setJava(int java) {
    this.java = java;
}

public int getUnix() {
    return unix;
}

public void setUnix(int unix) {
    this.unix = unix;
     }
  }

class MarksComparator implements Comparator<Marks> {
  @Override
  public int compare(Marks marks1, Marks marks2) {
    int Marks1Total = marks1.getPercentage();
    int Marks2Total = marks2.getPercentage();

    if (Marks2Total < Marks1Total) {
        return -1;
    } else if (Marks2Total > Marks1Total) {
        return 1;
    } else {
        return 0;
    }
 }
}

Here's my activity class:

public class MarksFragment extends Fragment{

private List<Marks> mMarksList = new ArrayList<>();

private RecyclerView mRecyclerView;
private MyAdapter mAdapter;
private RecyclerView.LayoutManager mLayoutManager;
private FirebaseDatabase mDatabase;
private DatabaseReference mReference;
private int total=0;

    public MarksFragment() {
        // Required empty public constructor
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_marks, container, false);


        mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);

        // use this setting to improve performance if you know that changes
        // in content do not change the layout size of the RecyclerView
        mRecyclerView.setHasFixedSize(true);

        // use a linear layout manager
        mLayoutManager = new LinearLayoutManager(getActivity());
        mRecyclerView.setLayoutManager(mLayoutManager);

        mRecyclerView.setItemAnimator(new DefaultItemAnimator());

        // specify an adapter (see also next example)
        /*mAdapter = new MyAdapter(getContext(),mMarksList);
        mAdapter.notifyDataSetChanged();
        mRecyclerView.setAdapter(mAdapter);*/

        //get Firebase Reference
        FirebaseDatabase.getInstance().setPersistenceEnabled(true);
        mDatabase = FirebaseDatabase.getInstance();
        mReference = mDatabase.getReference();

        mReference.addChildEventListener(new ChildEventListener() {
            @Override
            public void onChildAdded(DataSnapshot dataSnapshot, String s) {
                    fetchData(dataSnapshot);


            }

            @Override
            public void onChildChanged(DataSnapshot dataSnapshot, String s) {
                    fetchData(dataSnapshot);
            }

            @Override
            public void onChildRemoved(DataSnapshot dataSnapshot) {

            }

            @Override
            public void onChildMoved(DataSnapshot dataSnapshot, String s) {

            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        });
        return view;
    }

public void findPercentage(Marks value) {
        total =value.getC() + value.getcPlus() + value.getJava() + value.getUnix();
        value.setPercentage(total);
}

private void fetchData(DataSnapshot dataSnapshot) {
    Marks value = dataSnapshot.getValue(Marks.class);
    Log.v("Marks Fragment", "" +value);
    findPercentage(value);
    mMarksList.add(value);
    Collections.sort(mMarksList, new MarksComparator());

    // specify an adapter (see also next example)
    mAdapter = new MyAdapter(getContext(),mMarksList);
    mAdapter.notifyDataSetChanged();
    mRecyclerView.setAdapter(mAdapter);

Here is my adapter class:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder>{

private Context mContext;
private List<Marks> marksList;


public MyAdapter(Context mContext, List<Marks> marksList) {
    this.mContext = mContext;
    this.marksList = marksList;
}

public class MyViewHolder extends RecyclerView.ViewHolder {
    public TextView mItemName, mItemRegNo, mItemNo, mTotal;
    CircleImageView mImageView;

    public MyViewHolder(View view) {
        super(view);
        mItemName = (TextView) view.findViewById(R.id.card_name);
        mItemRegNo = (TextView) view.findViewById(R.id.card_regno);
        mItemNo = (TextView) view.findViewById(R.id.item_id);
        mImageView = (CircleImageView) view.findViewById(R.id.item_photo);
        mTotal = view.findViewById(R.id.card_total);
    }
}

@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View itemView = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.card_item, parent, false);

    return new MyViewHolder(itemView);
}

@Override
public void onBindViewHolder(final MyViewHolder holder, int position) {
    Marks marks = marksList.get(position);
    int count = position + 1;
    holder.mItemName.setText("" + marks.getName());
    holder.mItemRegNo.setText("" + marks.getRegno());
    holder.mItemNo.setText("" + count);
    holder.mImageView.setImageResource(R.drawable.after_cookie);
    holder.mTotal.setText(""+ marks.getPercentage());

}

@Override
public int getItemCount() {
    return marksList.size();
        }
    }

So the code does what its intended to do it retrieves the data and calculates the total and ranks the students. but when I update the data in firebase console the views in recyclerView duplicates temporarily. Like for example if I update Student1 unix value as 10 then two views will be shown in the recyclerView: 1 for previous value and 2 for updated value and again if I update the values it will yet show another views representing the new data without removing the old views. But if I restart recyclerView gets refreshed and its all ok but while I am running the app during the update it shows temporary duplicate views too.

I am new here and this is my first question so I can't even upload picture as you need 10 points to upload photo. I really hope someone help me out on this. I thank you in advance.

UPDATE

Here is link to the image:

When I start the app, the image is: first Image

when I update the unix value of Student3, the image in recyclerView becomes like this: After updating the data in firebase console

So, you see it adds new data as well as keeps the old data untill I restart.

Ron Daulagupu
  • 423
  • 6
  • 18
  • notify your adapter that value is changed, where you insert value in list use adapter.notifyDatasetchanged(); – Shashwat Gupta Aug 18 '17 at 09:06
  • Do you remove old entries from `marksList`? Or do you replace the old list with a new one? In either case you should call notidyDataSetChanged(). Also make sure a new `RecyclerView` or any of its container is not created, that will cause duplicate views (from your question I'm not sure if data is duplicated or view). – Abbas Aug 18 '17 at 09:09
  • I have used notifyDatasetChanged() on my adapter, you can see the code. Its still not working. – Ron Daulagupu Aug 18 '17 at 09:10
  • @Abbas Its the views that is duplicated. if you update anything manually in firebase console. views gets duplicated again and again.. – Ron Daulagupu Aug 18 '17 at 09:13
  • The difference between views duplication and data duplication in an adapter is usually there are two RecyclerViews, one on top of the other. Data duplication means same RecyclerView contains duplicate entries. Why don't you post a snapshot, it 'll be easier to understand the problem. – Abbas Aug 18 '17 at 09:16
  • @Abbas he wrote in his post that it isn't possible for him to upload as his rep is too low – Thomas Aug 18 '17 at 09:17
  • 1
    @Thomas If I remember correctly you can still share external links. All he needs to do is upload image on a 3rd party server and paste the link to it. – Abbas Aug 18 '17 at 09:19
  • @Abbas Ok, I will try to share the external link if it is possible. – Ron Daulagupu Aug 18 '17 at 09:27
  • @Abbas can you please tell me how can I post my image on 3rd party external server? – Ron Daulagupu Aug 18 '17 at 09:31
  • 1
    Use any of the free image hosting sites, or you can even upload it to your google drive, make it public and share the link here. If you get stuck, first try google. – Abbas Aug 18 '17 at 09:36
  • Just use imgur.com @RonDaulagupu ? – Thomas Aug 18 '17 at 09:44
  • @Thomas I have updated and posted the link of the images. please check it. – Ron Daulagupu Aug 18 '17 at 10:09
  • Thank you @RonDaulagupu, I'm now sure that my updated answer will fix it – Thomas Aug 18 '17 at 10:26

1 Answers1

2

Your problem is that you're never checking if the student already exists in your mMarksList so you're simply duplicating him by adding him again with new grades.

What I would do in you case is to add an unique id in firebase to each student.

Then you can check in your fetchData whether the student with that id is already in the array, delete him and add the new one.

private void fetchData(DataSnapshot dataSnapshot) {
    Marks value = dataSnapshot.getValue(Marks.class);
    Log.v("Marks Fragment", "" +value);
    findPercentage(value);
    // Get an iterator.
    Iterator<Marks> ite = mMarksList.iterator();
    while(ite.hasNext()) {
        Marks iteValue = ite.next();
        if(iteValue.getId().equals(value.getId())) ite.remove();
    }
    mMarksList.add(value);

    ....
}

Optionally To make that even cleaner, you can override the equals and hashcode methods in your Marks data model, so that a Marks object is considered the same if the id is equal. More

//ASSUMING THAT ID IS int

@Override
public int hashCode() {
    return id;
}

@Override
public boolean equals(Object obj) {
    if (this == obj) return true;
    if (obj == null) return false;
    if (this.getClass() != obj.getClass()) return false;

    Marks other = (Marks) obj;
    if (this.getId != other.getId) {
        return false;
    }
    return true;

}

Then it's possible to either use a hashmap, which will override the old student automatically or a arraylist as is and iterate through it before and check if a student equals your new student, like this:

private void fetchData(DataSnapshot dataSnapshot) {
    Marks value = dataSnapshot.getValue(Marks.class);
    Log.v("Marks Fragment", "" +value);
    findPercentage(value);
    // Use an iterator.
    Iterator<Marks> ite = mMarksList.iterator();
    while(ite.hasNext()) {
        Marks iteValue = ite.next();
        if(iteValue.equals(value)) ite.remove();
    }
    mMarksList.add(value);

    ....
}
Thomas
  • 483
  • 7
  • 22
  • @RonDaulagupu Could you be a little bit more specific? I just wrote the code in the reply window for an easy example and haven't even tested for syntax failures, but you may want to read what I wrote above and try something for yourself based on that information. I'd expect from you to debug that a little bit by yourself and then come back with a concrete problem instead of "doesn't work" – Thomas Aug 18 '17 at 09:29
  • Actually I am new to android programming. So, I am so sure about hashcode methods that you are referring to. This is my college project and I don't have much time to submit it. Anyhow I will still try to read about that and find a solution. But I request you too to please explain me more on how can I put unique ID's and check it as you described. – Ron Daulagupu Aug 18 '17 at 09:38
  • @RonDaulagupu I've updated my code with more examples and made clear that the "hashcode" part is totally optionally and more *for style* – Thomas Aug 18 '17 at 09:43
  • Hey, I assigned unique id's in firebase for all the student and did some tweets to the code u wrote and guess what it worked. But now a new problem has arised. Whenever the data is updated the app closes by itself – Ron Daulagupu Aug 18 '17 at 10:27
  • @RonDaulagupu That's when you look at the `logcat` output in android studio. There will be a report why it has crashed. Please google it. – Thomas Aug 18 '17 at 10:30
  • there is java.util.ConcurrentModificationException but I am not so sure how to tackle I have looked up in google. still no clue. string searching for answer though. – Ron Daulagupu Aug 18 '17 at 10:42
  • This would qualify for a different question, your original q is solved? @RonDaulagupu. I would try to pass a new List object ot your adapter though. `List list = new ArrayList<>(); list.addAll(mMarksList);` and then pass that list to your adapter (all in your `fetchData`). – Thomas Aug 18 '17 at 10:51
  • wow! I finally did it. Thank you so much. I just had to use the Iterator class and ivoke iterator() method on the arraylist. Thanks I am so happy. This was causing a headache for me. I think i would update the Iterator code in your answer. – Ron Daulagupu Aug 18 '17 at 10:58
  • @yeah, sure. :) – Ron Daulagupu Aug 18 '17 at 11:00