У меня большая проблема при удалении группы из ExpandableListView. Даже после того, как я много гуглил и перепробовал много уроков и примеров, я не смог ее решить.
Хотя у меня большой опыт программирования, я относительно знаком с программированием на Android. Поэтому я уверен, что в источнике есть много вещей, которые еще не сделаны хорошо. Но на данный момент я хотел сосредоточиться на проблеме с неправильным представлением после удаления группы из списка.
Чтобы дать хороший обзор проблемы, вот несколько скриншотов
Запуск приложения
Список после нажатия на кнопку Список всех бюджетов
Все группы Expandet
Перед удалением последнего потомка последней группы
Остальные группы показывают детей дважды
Последняя группа с двумя детьми
Перед удалением последних детей последней группы
Исправить результат после удаления последнего потомка последней группы
Надеюсь, проблема станет ясной. Если в последней группе есть только один дочерний элемент, и он был удален, приложение удалит всю группу, но потом дочерние элементы первой группы появятся дважды.
Во время сеанса отладки я проверил все ресурсы за данными, и все они в порядке. Если я вернусь к MainActivity и снова начну список, представление будет полностью правильным. Таким образом, это может быть проблема неправильной совокупности после удаления всей группы.
Как видите, если я удаляю только последнего потомка из последней группы с двумя потомками, заполнение всего списка корректно.
Вот еще немного информации о приложении:
Я использую базу данных комнаты с двумя таблицами, содержащими данные.
Одна таблица содержит категории с именем и идентификатором, а другая таблица предназначена для отдельных записей бюджета с идентификатором категории в качестве внешнего ключа.
В функции создания BudgetListActivity я создал два бюджета DAO DAO и категорию DAO, чтобы получить данные и заполнить списки allBudgetsList и весь CatList.
С помощью этой информации я создаю новый массив List allGroups со структурой, которая мне нужна для представления
- Категории в заголовке
- бюджеты как дети за счет внешнего ключа
(только одно замечание здесь:
тем временем я уже пытался использовать хеш-карту для данных, передаваемых в ExpandableListAdapter, но в результате получилось то же неверное представление!)
Существует contentView "budget_expandable_list", для которого установлен ExpandableListAdapter. Адаптер должен заполнить группы и дочерние элементы для этого списка, используя данные из ArrayList "allGroups"
Это структура приложения
Возможно, есть некоторые ресурсы, которые на самом деле не используются.
Я сейчас дам код Сурэ для важных классов
BudgetListActivity:
package com.wbapps.WBEasyBudgetManagement;
import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.CoordinatorLayout;
import android.support.v7.app.AppCompatActivity;
import android.view.ContextMenu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ExpandableListView;
import android.widget.Toast;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class BudgetListActivity extends AppCompatActivity implements AdapterView.OnItemClickListener {
CoordinatorLayout coordinatorLayout;
private SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
//wb, 23Oct2018: now using an array list for the expandable list adapter
ArrayList<Group> allGroups = new ArrayList();
private ArrayAdapter adapter;
private final int REQUEST_CODE_EDIT = 1;
private BudgetDAO budgetDAO;
private CategoryDAO categoryDAO;
List<Budget> allBudgetsList;
List<Category> allCatsList;
ExpandableListView expListView;
List<String> expListViewTitle;
ExpandableListAdapter expAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.budget_expandable_list);
if (allGroups.size() > 0 ) {allGroups.clear();}
//get instances for DAO's of db from MainActivity
budgetDAO = MainActivity.getBudgetDAO();
categoryDAO = MainActivity.getCategoryDAO();
//the list for budgets and categories
allBudgetsList = budgetDAO.getBudgets();
allCatsList = categoryDAO.getCategories();
//temporary Group-Object for the ArrayList allGroups
Group tmpGroup;
double sumExpenses = 0;
//Start with reading all categories
for (int i=0;i<allCatsList.size(); i++) {
String tmpCat = allCatsList.get(i).getCategory();
tmpGroup = new Group(tmpCat);
sumExpenses = 0.0;
//now read all budgets for the current category and fill the rest of the temporary Group-Object
for (int j=0;j<allBudgetsList.size();j++){
if (allBudgetsList.get(j).getCategoryId() == allCatsList.get(i).getId()){
//tmpGroup.budgetId = allBudgetsList.get(j).getId();
tmpGroup.catId = allBudgetsList.get(j).getCategoryId();
tmpGroup.children.add(Arrays.asList
(
" Date: " + sdf.format(allBudgetsList.get(j).getDateTime())
+ " - Expenses: " + Double.toString(allBudgetsList.get(j).getExpenses()),
Long.toString(allBudgetsList.get(j).getId())
)
);
sumExpenses = sumExpenses + allBudgetsList.get(j).getExpenses();
tmpGroup.sumExpenses = sumExpenses;
}
}
//if at least one children for the current category was found
// =>> write all the group information the the array list
if (tmpGroup.children.size() > 0 ) {allGroups.add(tmpGroup);}
}
expListView = (ExpandableListView) findViewById(R.id.expandableList);
expAdapter = new ExpandableListAdapter(this, allGroups);
expListView.setAdapter(expAdapter);
expListView.setOnItemClickListener(this);
registerForContextMenu(expListView);
}
@Override
public void onCreateContextMenu(ContextMenu contMenu, View v,
ContextMenu.ContextMenuInfo contextMenuInfo) {
super.onCreateContextMenu(contMenu, v, contextMenuInfo);
ExpandableListView.ExpandableListContextMenuInfo info = (ExpandableListView.ExpandableListContextMenuInfo) contextMenuInfo;
int type = ExpandableListView.getPackedPositionType(info.packedPosition);
int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
int childPosition = ExpandableListView.getPackedPositionChild(info.packedPosition);
// Show context menu for groups
if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
contMenu.setHeaderTitle("Budget");
contMenu.add(R.string.context_editBudget);
contMenu.add(R.string.context_delBudget);
// Show context menu for children
} else if (type == ExpandableListView.PACKED_POSITION_TYPE_CHILD) {
contMenu.setHeaderTitle("Child");
contMenu.add(R.string.context_editChild);
contMenu.add(R.string.context_delChild);
}
}
@Override
public boolean onContextItemSelected(MenuItem item) {
Integer tmpInt = item.getItemId();
ExpandableListView.ExpandableListContextMenuInfo info = (ExpandableListView.ExpandableListContextMenuInfo) item
.getMenuInfo();
int type = ExpandableListView.getPackedPositionType(info.packedPosition);
int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
int childPosition = ExpandableListView.getPackedPositionChild(info.packedPosition);
//TextView vItem = info.targetView.findViewById(R.id.context_editBudget);
if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
//Toast.makeText(this, "Click auf Group: " + Integer.toString(item.getGroupId()), Toast.LENGTH_SHORT).show();
if (item.getTitle().toString().equals(getString(R.string.context_editBudget))){
Toast.makeText(this, "Edit Budget clicked in Budget Context Menu", Toast.LENGTH_SHORT).show();
}
if (item.getTitle().toString().equals(getString(R.string.context_delBudget))){
int size = allGroups.get(groupPosition).children.size();
for (int i = 0; i<size; i++) {
budgetDAO.delAllBudgetsForCategory(allGroups.get(groupPosition).catId);
}
allGroups.remove(groupPosition);
//expAdapter.notifyDataSetChanged();
if (allGroups.size() == 0){
Intent intent = new Intent(BudgetListActivity.this, MainActivity.class);
startActivity(intent);
}
}
}
if (type == ExpandableListView.PACKED_POSITION_TYPE_CHILD) {
if (item.getTitle().toString().equals(getString(R.string.context_editChild))){
Toast.makeText(this, "Edit Child clicked in Child Context Menu", Toast.LENGTH_SHORT).show();
}
if (item.getTitle().toString().equals(getString(R.string.context_delChild))){
//wb, 27Oct2018: Delete the selected child for a budget with given category
budgetDAO.delBudgetChildForCategory(Integer.parseInt(allGroups.get(groupPosition).children.get(childPosition).get(1)));
allGroups.get(groupPosition).children.remove(childPosition);
//expAdapter.notifyDataSetChanged();
//wb, 28Oct2018: If no more budget rows available delete the whole budget for category
if (allGroups.get(groupPosition).children.size() == 0) {
allGroups.remove(groupPosition);
//expAdapter.notifyDataSetChanged();
//expAdapter.notifyDataSetChanged();
if (allGroups.size() ==0){
Intent intent = new Intent(BudgetListActivity.this, MainActivity.class);
startActivity(intent);
}
}
/*
else {
//allGroups.get(groupPosition).sumExpenses = 0.0;
//allGroups.get(groupPosition) = expAdapter.getSum(groupPosition)
for (int i = 0; i < allBudgetsList.size(); i++) {
if (allBudgetsList.get(i).getCategoryId() == allGroups.get(groupPosition).catId) {
allGroups.get(groupPosition).sumExpenses =
allGroups.get(groupPosition).sumExpenses + allBudgetsList.get(i).getExpenses();
}
}
}*/
}
}
expAdapter.notifyDataSetChanged();
//return super.onContextItemSelected(item);
return true;
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Budget budget = (Budget)adapter.getItem(position);
editEntry(budget, position);
}
private void editEntry(Budget budget, int position) {
Intent intent = new Intent(this, EditBudgetActivity.class);
intent.putExtra("position", position);
startActivityForResult(intent, REQUEST_CODE_EDIT);
}
}
Как видите, я использую контекстное меню для редактирования и удаления групп и / или дочерних элементов. Некоторые функции еще не полностью реализованы. Пожалуйста, поймите, что я сначала сосредоточусь на своей основной проблеме с правильным заполнением ExpandableView.
Кроме того, другие вещи, такие как неправильное обновление суммы расходов после удаления потомка, еще не очень важны и будут выполнены позже.
Вот класс для группового объекта:
package com.wbapps.WBEasyBudgetManagement;
import java.util.ArrayList;
import java.util.List;
public class Group {
public long budgetId;
public long catId;
public String category;
public final List<List<String>> children = new ArrayList<List<String>>();
public final List<Long> BudIds = new ArrayList<Long>();
public double sumExpenses;
public Group(String pcategory) {
category = pcategory;
}
}
Вот источник ExpandableListAdapter:
пакет com.wbapps.WBEasyBudgetManagement;
import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.CheckedTextView;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Currency;
import java.util.Locale;
public class ExpandableListAdapter extends BaseExpandableListAdapter{
Context context;
Locale locale;
Currency curr;
//array list to take the data for the list from the activity
private final ArrayList<Group> allGroups;
public LayoutInflater inflater;
public AppCompatActivity activity;
public int times = 0;
//Constructor for ExpandableListAdapter
//public ExpandableListAdapter(AppCompatActivity act, SparseArray<Group> groups) {
public ExpandableListAdapter(AppCompatActivity act, ArrayList<Group> allGroups) {
this.activity = act;
this.allGroups = allGroups;
inflater = act.getLayoutInflater();
}
@Override
public View getGroupView(int groupPosition, boolean isExpanded,
View convertView, ViewGroup parent) {
times = times + 1;
Log.d("Info getGroupView","In getGroupView " + Integer.toString(times) + " times");
for (Locale wbLocale : Locale.getAvailableLocales()) {
//Log.d("LOCALES", wbLocale.getLanguage() + "_" + wbLocale.getCountry() + " [" + wbLocale.getDisplayName() + "]");
if (wbLocale.getCountry().equals("PH")) {
curr = Currency.getInstance(wbLocale);
curr.getSymbol(wbLocale);
break;
}
}
if (convertView == null || convertView.findViewById(R.id.tvCatGroup)==null){
convertView = inflater.inflate(R.layout.list_row_group, null);
}
convertView = inflater.inflate(R.layout.list_row_group, null);
String tmpCat = allGroups.get(groupPosition).category;
Group tmpGroup = new Group(tmpCat);
sortList();
Group group = (Group) getGroup(groupPosition);
//((CheckedTextView) convertView).setText(group.category + "\nTotal Expenses: " + group.sumExpenses + " " + curr.getSymbol());
((CheckedTextView) convertView).setText(group.category + "\nTotal Expenses: " + getSum(groupPosition) + " " + curr.getSymbol());
((CheckedTextView) convertView).setChecked(isExpanded);
return convertView;
}
/* wb, 18Sep2017: sort the list_selectedShoppingItems list */
public void sortList() {
Collections.sort(allGroups, new Comparator<Group>() {
@Override
public int compare(Group content1, Group content2) {
/* ignore case sensitivity */
return content1.category.compareToIgnoreCase(content2.category);
}
});
}
@Override
public View getChildView(int groupPosition, final int childPosition,
boolean isLastChild, View convertView, ViewGroup parent)
{
if(childPosition < getChildrenCount(groupPosition)-1) {
//holds the detail string for one child
final String children = (String) getChild(groupPosition, childPosition);
if (convertView == null || convertView.findViewById(R.id.tvChildRow)==null)
convertView = inflater.inflate(R.layout.list_row_details, null);
convertView = inflater.inflate(R.layout.list_row_details, null);
TextView txtChildRow = (TextView)convertView.findViewById(R.id.tvChildRow);
txtChildRow.setText(children + " " + curr.getSymbol());
convertView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(activity, children + " " + curr.getSymbol(),
Toast.LENGTH_SHORT).show();
}
});
}
//children is the last one
if(childPosition == getChildrenCount(groupPosition)-1)
{
if (convertView == null || convertView.findViewById(R.id.tvSum)==null)
convertView = inflater.inflate(R.layout.listview_footer,null);
TextView txtFooter = (TextView)convertView.findViewById(R.id.tvSum);
//txtFooter.setText("Total expenses: " + allGroups.get(groupPosition).sumExpenses + " " + curr.getSymbol() );
txtFooter.setText("Total expenses: " + getSum(groupPosition) + " " + curr.getSymbol() );
//Log.e(TAG, "getChildView - sumExpenses: "+txtFooter.getText().toString());
}
convertView.setLongClickable( true);
return convertView;
}
@Override
public Object getChild(int groupPosition, int childPosition) {
return allGroups.get(groupPosition).children.get(childPosition).get(0);
}
public Object getSum(int groupPosition) {
return allGroups.get(groupPosition).sumExpenses;
}
@Override
public long getChildId(int groupPosition, int childPosition) {
return 0;
}
//Add 1 to childCount. The last row is used as footer to childView
@Override
public int getChildrenCount(int groupPosition) {
return allGroups.get(groupPosition).children.size() +1;
}
@Override
public Object getGroup(int groupPosition) {
return allGroups.get(groupPosition);
}
@Override
public int getGroupCount() {
return allGroups.size();
}
@Override
public void onGroupCollapsed(int groupPosition) {
super.onGroupCollapsed(groupPosition);
}
@Override
public void onGroupExpanded(int groupPosition) {
super.onGroupExpanded(groupPosition);
}
@Override
public long getGroupId(int groupPosition) {
return 0;
}
@Override
public boolean hasStableIds() {
return false;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
@Override
public void notifyDataSetChanged() {
super.notifyDataSetChanged();
}
}
Некоторые замечания могут быть полезны:
- в getChildrenCount я добавил 1 к числу размеров, потому что я использую одного последнего ребенка в качестве нижнего колонтитула, чтобы показать сводку расходов
- для лучшего понимания вот картинка из списка "allGroups"
Я надеюсь, что смогу поддержать вас всей необходимой информацией. Пожалуйста, дайте мне знать, если некоторые из них отсутствуют. Я добавлю это в ближайшее время.
Надеюсь, кто-то найдет решение для меня.
Хорошего дня
Andreas