androidfirebasefirebase-realtime-databaseexpandablelistviewexpandablelistadapter

Creating ExpandableListView with data from firebase


I'm creating a task manager app. And I have a problem with showing these tasks in ExpandableListView, I'm using it because every task may have subtasks. This is my Database structure:

Database Structure.

ExpandableListView Adapter may work well. But I have a problem with reading data from firebase in fillData() method.

Activity with ExpandableListView:

public class Tasks extends AppCompatActivity {

    ExpandableListView expandableListView;
    CustomExpandableListAdapter expandableListAdapter;

    List<String> tasks;
    Map<String, List<String>> subtasks;

    private FirebaseDatabase firebasedatabase;
    private DatabaseReference databaseReference;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tasks);

        BottomNavigationView bottomNav = findViewById(R.id.bottom_nav);
        bottomNav.setSelectedItemId(R.id.tasks);
        expandableListView = (ExpandableListView) findViewById(R.id.tasks_list);

        firebasedatabase = FirebaseDatabase.getInstance();
        databaseReference = firebasedatabase.getReference("Tasks");
        fillData();
        expandableListAdapter = new CustomExpandableListAdapter(this, tasks, subtasks);
        expandableListView.setAdapter(expandableListAdapter);




        bottomNav.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                switch (item.getItemId()){
                    case R.id.notes:
                        startActivity(new Intent(getApplicationContext(), Notes.class));
                        overridePendingTransition(0,0);
                        return true;
                    case R.id.tasks:
                        return true;
                    case R.id.goals:
                        startActivity(new Intent(getApplicationContext(), Goals.class));
                        overridePendingTransition(0,0);
                        return true;
                    case R.id.statistics:
                        startActivity(new Intent(getApplicationContext(), Statistics.class));
                        overridePendingTransition(0,0);
                        return true;
                    case R.id.add:
                        startActivity(new Intent(getApplicationContext(), Add.class));
                        overridePendingTransition(0,0);
                        return true;

                }
                return false;
            }
        });

    }

    public void fillData()
    {
        databaseReference.addValueEventListener(new ValueEventListener() {

            @Override
            public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
                if(tasks.size() > 0) tasks.clear();
                for(DataSnapshot ds : dataSnapshot.getChildren())
                {
                  String taskName = (String) ds.child("Tasks").getValue();
                  tasks.add(taskName);
                }

                expandableListAdapter.notifyDataSetChanged();
            }

            @Override
            public void onCancelled(@NonNull DatabaseError databaseError) {

            }
        });
    }
}

ExpandableListView Adapter:

public class CustomExpandableListAdapter extends BaseExpandableListAdapter {

private Context context;
private List<String> listHeader;
private Map<String, List<String>> listChildren;
public CustomExpandableListAdapter(Context context, List<String> listHeader, Map<String, List<String>> listChildren){
    this.context = context;
    this.listHeader = listHeader;
    this.listChildren = listChildren;
}

@Override
public int getGroupCount() {
    return this.listHeader.size();
}

@Override
public int getChildrenCount(int groupPosition) {
    return this.listChildren.get(listHeader.get(groupPosition)).size();
}

@Override
public Object getGroup(int groupPosition) {
    return this.listHeader.get(groupPosition);
}

@Override
public Object getChild(int groupPosition, int childPosition) {
    return this.listChildren.get(this.listHeader.get(groupPosition));
}

@Override
public long getGroupId(int groupPosition) {
    return groupPosition;
}

@Override
public long getChildId(int groupPosition, int childPosition) {
    return childPosition;
}

@Override
public boolean hasStableIds() {
    return false;
}

@Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
    String header = (String) getGroup(groupPosition);

    if(convertView == null)
    {
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(context.LAYOUT_INFLATER_SERVICE);
        convertView = inflater.inflate(R.layout.list_view_header, null);
    }

    TextView headerOfGroup = (TextView) convertView.findViewById(R.id.header_text);
    headerOfGroup.setText(header);
    return convertView;
}

@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
    String child = (String) getChild(groupPosition, childPosition);

    if(convertView == null)
    {
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(context.LAYOUT_INFLATER_SERVICE);
        convertView = inflater.inflate(R.layout.list_view_child, null);
    }

    TextView childText = (TextView) convertView.findViewById(R.id.child_text);
    childText.setText(child);
    return convertView;
}

@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
    return true;
}

}


Solution

  • As far as I can see, the relevant code in your question is:

    firebasedatabase = FirebaseDatabase.getInstance();
    databaseReference = firebasedatabase.getReference("Tasks");
    databaseReference.addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
            if(tasks.size() > 0) tasks.clear();
            for(DataSnapshot ds : dataSnapshot.getChildren())
            {
              String taskName = (String) ds.child("Tasks").getValue();
              tasks.add(taskName);
            }
    
            expandableListAdapter.notifyDataSetChanged();
        }
    
        @Override
        public void onCancelled(@NonNull DatabaseError databaseError) {
        }
    });
    

    Your code is attaching a listener to /Tasks, then looping over its child nodes, and getting the Tasks child of each of those child nodes. Yet if we look at your database structure, there is no Tasks subnode under each task. So that probably results in a lists of empty items, once for each child node of /Tasks


    If you want to show the key of each child node of /Tasks (so make a aolication and Shoping in your screenshot), you'll want to use the getKey() of each child node:

    firebasedatabase = FirebaseDatabase.getInstance();
    databaseReference = firebasedatabase.getReference("Tasks");
    databaseReference.addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
            if(tasks.size() > 0) tasks.clear();
            for(DataSnapshot ds : dataSnapshot.getChildren()) {
                String taskName = ds.getKey();
                tasks.add(taskName);
            }
    
            expandableListAdapter.notifyDataSetChanged();
        }
    
        @Override
        public void onCancelled(@NonNull DatabaseError databaseError) {
            throw databaseError.toException()
        }
    });
    

    If you want to show all the values under the Subtasks of each child node, you'll need to add another nested loop for this:

    firebasedatabase = FirebaseDatabase.getInstance();
    databaseReference = firebasedatabase.getReference("Tasks");
    databaseReference.addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
            if(tasks.size() > 0) tasks.clear();
            for(DataSnapshot taskSnapshot: dataSnapshot.getChildren()) {
                DataSnapshot subtasksSnapshot = taskSnapshot.child("Subtasks");
                for(DataSnapshot subtaskSnapshot: subtasksSnapshot.getChildren()) {
                    String taskName = subtaskSnapshot.getValue(String.class);
                    tasks.add(taskName);
                }
            }
    
            expandableListAdapter.notifyDataSetChanged();
        }
    
        @Override
        public void onCancelled(@NonNull DatabaseError databaseError) {
            throw databaseError.toException()
        }
    });