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:
.
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;
}
}
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()
}
});