ExpandableListAdapter удалить проблему группы - PullRequest
0 голосов
/ 03 ноября 2018

У меня большая проблема при удалении группы из ExpandableListView. Даже после того, как я много гуглил и перепробовал много уроков и примеров, я не смог ее решить.

Хотя у меня большой опыт программирования, я относительно знаком с программированием на Android. Поэтому я уверен, что в источнике есть много вещей, которые еще не сделаны хорошо. Но на данный момент я хотел сосредоточиться на проблеме с неправильным представлением после удаления группы из списка.

Чтобы дать хороший обзор проблемы, вот несколько скриншотов

Запуск приложения

The MainActivity

Список после нажатия на кнопку Список всех бюджетов

The not expanded groups on BudgetListActivity

Все группы Expandet

Groups expandet

Перед удалением последнего потомка последней группы

before deleting last child of group 2

Остальные группы показывают детей дважды

remaining group show children twice

Последняя группа с двумя детьми

last group has two children

Перед удалением последних детей последней группы

before delteing last child of group 2

Исправить результат после удаления последнего потомка последней группы

result after deleting last child of group 2

Надеюсь, проблема станет ясной. Если в последней группе есть только один дочерний элемент, и он был удален, приложение удалит всю группу, но потом дочерние элементы первой группы появятся дважды.

Во время сеанса отладки я проверил все ресурсы за данными, и все они в порядке. Если я вернусь к MainActivity и снова начну список, представление будет полностью правильным. Таким образом, это может быть проблема неправильной совокупности после удаления всей группы.

Как видите, если я удаляю только последнего потомка из последней группы с двумя потомками, заполнение всего списка корректно.

Вот еще немного информации о приложении:

  • Я использую базу данных комнаты с двумя таблицами, содержащими данные.

    Одна таблица содержит категории с именем и идентификатором, а другая таблица предназначена для отдельных записей бюджета с идентификатором категории в качестве внешнего ключа. project database

В функции создания BudgetListActivity я создал два бюджета DAO DAO и категорию DAO, чтобы получить данные и заполнить списки allBudgetsList и весь CatList. С помощью этой информации я создаю новый массив List allGroups со структурой, которая мне нужна для представления - Категории в заголовке - бюджеты как дети за счет внешнего ключа

(только одно замечание здесь: тем временем я уже пытался использовать хеш-карту для данных, передаваемых в ExpandableListAdapter, но в результате получилось то же неверное представление!)

Существует contentView "budget_expandable_list", для которого установлен ExpandableListAdapter. Адаптер должен заполнить группы и дочерние элементы для этого списка, используя данные из ArrayList "allGroups"

Это структура приложения app structure

Возможно, есть некоторые ресурсы, которые на самом деле не используются.

Я сейчас дам код Сурэ для важных классов

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" allGroups List

Я надеюсь, что смогу поддержать вас всей необходимой информацией. Пожалуйста, дайте мне знать, если некоторые из них отсутствуют. Я добавлю это в ближайшее время.

Надеюсь, кто-то найдет решение для меня. Хорошего дня Andreas

1 Ответ

0 голосов
/ 10 ноября 2018

тем временем я нашел причину такого поведения. В конце исходного кода адаптера есть метод «getGroupID». Возвращаемое значение здесь было установлено в 0, что вызвало проблему. Это должно быть установлено в groupPosition, и тогда это работает!

@Override
public long getGroupId(int groupPosition) {
    /* wb, 10Nov2018: this statement was due to the error of deleting a last child of a group
    With "return 0" the children of the remaining group was shown twice !!!
    return 0;
    */
    return groupPosition;
}

Это, надеюсь, полезно для всех, кто также сталкивается с этой проблемой. Приятно провести время Andreas

...