I'm working on an app that is using an Expandable List Adapter to vote on certain characteristics of a movie. There's a problem where the viewholders are getting synced up with each other some, but not all of the time. i.e. the holder total and which button is clicked are changing together, despite the data in firebase. I'm trying to figure out how to keep the views separate.
I have a cloud function in firebase which does the math of converting a user vote to a movie vote and it is firing correctly.
public class ExpandableListAdapter extends BaseExpandableListAdapter {
private DatabaseReference triggerDB;
private DatabaseReference userDB = FirebaseDatabase.getInstance().getReference("users");
private int userVote;
private int uv;
private Context mContext;
private ArrayList<Category> _listDataHeader; // header titles
private String imdbID;
private String title;
private String mUserId;
private ValueEventListener mUserListener;
private HashMap<DatabaseReference, ValueEventListener> hashMap = new HashMap<>();
public ExpandableListAdapter(Context context, String imdbID, String title, ArrayList<Category> listDataHeader) {
this.mContext = context;
this._listDataHeader = listDataHeader;
this.imdbID = imdbID;
this.title = title;
}
@Override
public Trigger getChild(int groupPosition, int childPosition) {
Category category = _listDataHeader.get(groupPosition);
ArrayList<Trigger> categoryArrayList = category.getTriggerList();
return categoryArrayList.get(childPosition);
}
@Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
@Override
public int getChildrenCount(int groupPosition) {
Category category = _listDataHeader.get(groupPosition);
ArrayList<Trigger> triggerList = category.getTriggerList();
return triggerList.size();
}
@Override
public Object getGroup(int groupPosition) {
return this._listDataHeader.get(groupPosition);
}
@Override
public int getGroupCount() {
return this._listDataHeader.size();
}
@Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
@Override
public View getGroupView(int groupPosition, boolean isExpanded,
View convertView, ViewGroup parent) {
Category category = _listDataHeader.get(groupPosition);
String headerTitle = category.getCategoryName();
if (convertView == null) {
LayoutInflater infalInflater = (LayoutInflater) this.mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (infalInflater != null) {
convertView = infalInflater.inflate(R.layout.list_group, parent, false);
}
}
assert convertView != null;
TextView lblListHeader = convertView.findViewById(R.id.lblListHeader);
lblListHeader.setTypeface(null, Typeface.BOLD);
lblListHeader.setText(headerTitle);
return convertView;
}
@Override
public void onGroupExpanded(int groupPosition) {
FirebaseDatabase.getInstance().goOnline();
super.onGroupExpanded(groupPosition);
}
@Override
public void onGroupCollapsed(int groupPosition) {
super.onGroupCollapsed(groupPosition);
removeValueEventListener(hashMap);
}
@Override
public View getChildView(final int i, final int i1, boolean b, View view, ViewGroup viewGroup) {
final ViewHolder holder;
FirebaseAuth firebaseAuth = FirebaseAuth.getInstance();
FirebaseUser user = firebaseAuth.getCurrentUser();
DatabaseReference movieDB = FirebaseDatabase.getInstance().getReference("movies");
Category category = _listDataHeader.get(i);
ArrayList<Trigger> categoryArrayList = category.getTriggerList();
final Trigger trigger = categoryArrayList.get(i1);
final String tId = trigger.getId();
final String childText = trigger.getTriggerName();
triggerDB = movieDB.child(imdbID).child("Triggers").child(tId).child("votes");
if (view == null) {
view = LayoutInflater.from(mContext).inflate(R.layout.trigger_list_item, null);
holder = new ViewHolder();
holder.mTrigger = trigger;
setHolderData(view, holder, childText);
triggerDB.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.child("value").exists()) {
int value = dataSnapshot.child("value").getValue(Integer.class);
String valStr = String.valueOf(value);
holder.total.setText(valStr);
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
});
Log.d("if", holder.mTrigger.getTriggerName());
view.setTag(holder);
}
else {
holder = (ViewHolder) view.getTag();
populateTriggerVote(holder, trigger, imdbID);
Log.d("else", holder.mTrigger.getTriggerName());
}
if (user != null) {
mUserId = user.getUid();
mUserListener = new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.getValue() != null) {
holder.myVote = dataSnapshot.getValue(int.class);
waitForVote(holder, i, mUserId, tId);
Log.d("PAXIE", String.valueOf(userVote));
if (holder.myVote == 0) {
holder.upButton.setChecked(false);
holder.downButton.setChecked(false);
}
if (holder.myVote == 1) {
//user has voted yuppers
holder.upButton.setChecked(true);
holder.downButton.setChecked(false);
}
if (holder.myVote == -1) {
//user has voted nopers
holder.upButton.setChecked(false);
holder.downButton.setChecked(true);
}
else {
Log.d("elsie", String.valueOf(holder.myVote));
}
}
else {
userDB.child(mUserId).child("changes").child(imdbID).child("triggers").child(trigger.getId()).setValue(0);
Log.d("Zippity","Doo Dah");
holder.myVote = 0;
Movie movie = new Movie(imdbID, title);
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
};
}
else {
Log.d("Firing PEW PEW", "B");
}
userDB.child(mUserId).child("changes").child(imdbID).child("triggers").child(trigger.getId()).addValueEventListener(mUserListener);
hashMap.put(userDB, mUserListener);
return view;
}
private void setHolderData(View view, ViewHolder holder, String childText) {
holder.triggerName = view.findViewById(R.id.triggerName);
holder.upButton = view.findViewById(R.id.mTriggerButtonUp);
holder.downButton = view.findViewById(R.id.mTriggerButtonDown);
holder.total = view.findViewById(R.id.triggerCounter);
holder.triggerName.setText(childText);
}
@Override
public boolean hasStableIds() {
return false;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
private void waitForVote(final ViewHolder holder, final int i, final String mUserId, final String tId) {
DatabaseReference movieDB = FirebaseDatabase.getInstance().getReference("movies");
triggerDB = movieDB.child(imdbID).child("Triggers").child(tId).child("votes");
holder.upButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.d("trigger just voted on: ", (String) holder.triggerName.getText());
if ((holder.myVote == 0)||(holder.myVote == -1)) {
uv = 1;
}
else if (holder.myVote == 1) {
uv = 0;
}
else {
uv = 1;
}
holder.myVote = uv;
userDB.child(mUserId).child("changes").child(imdbID).child("triggers").child(holder.mTrigger.getId()).setValue(holder.myVote);
notifyDataSetChanged();
}
});
holder.downButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.d("trigger just voted on: ", (String) holder.triggerName.getText());
if ((holder.myVote == 0)||(holder.myVote == 1)) {
uv = -1;
}
else if (holder.myVote == -1) {
uv = 0;
}
else {
uv = -1;
}
holder.myVote = uv;
userDB.child(mUserId).child("changes").child(imdbID).child("triggers").child(holder.mTrigger.getId()).setValue(holder.myVote);
notifyDataSetChanged();
// notifyDataSetChanged();
}
});
// notifyDataSetChanged();
}
private static class ViewHolder {
TextView triggerName;
ToggleButton upButton;
ToggleButton downButton;
TextView total;
int myVote;
Trigger mTrigger;
int voteTotal;
}
private static void removeValueEventListener(HashMap<DatabaseReference, ValueEventListener> hashMap) {
for (Map.Entry<DatabaseReference, ValueEventListener> entry : hashMap.entrySet()) {
DatabaseReference databaseReference = entry.getKey();
ValueEventListener valueEventListener = entry.getValue();
databaseReference.removeEventListener(valueEventListener);
Log.d("PEW PEW PEW", "EXPANDABLE LIST ADAPTER");
}
}
private static void populateTriggerVote(final ViewHolder holder, final Trigger trigger, final String imdbID){
DatabaseReference movieDB = FirebaseDatabase.getInstance().getReference("movies");
DatabaseReference triggerDB = movieDB.child(imdbID).child("Triggers").child(trigger.getId()).child("votes");
final DatabaseReference userDB = FirebaseDatabase.getInstance().getReference("users");
final ValueEventListener triggerListener = new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.child("value").exists()) {
holder.voteTotal = dataSnapshot.child("value").getValue(int.class);
holder.total.setText(String.valueOf(holder.voteTotal));
}
else {
Log.d("Setting Value", "To Zero");
userDB.child(FirebaseAuth.getInstance().getCurrentUser().getUid()).child("changes").child(imdbID).child("triggers").child(trigger.getId()).setValue(0);
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
};
triggerDB.addValueEventListener(triggerListener);
}
}