ExpandableListAdapter删除组问题

ExpandableListAdapter delete group problem

我在从 ExpandableListView 中删除组时遇到了很大的问题。即使在 google 很多次并尝试了很多教程和示例之后,我也无法解决它。

虽然我有很多编程经验,但我对 Android 编程比较了解。所以我确信源代码中有很多事情还没有做好。但截至目前,我想在从列表中删除一个组后以错误的观点关注问题。

为了更好地概述问题,这里有一些屏幕截图

应用程序启动

单击“列出所有预算”按钮后列出

所有组扩展

在删除最后一组child之前

剩余组显示child人两次

这次最后一组有两个children

删除最后一组children之前

删除最后一组child后的正确结果

我希望问题变得清楚。如果最后一个组只有一个 child 并且这个被删除,则整个组将被应用程序删除 - 但第一组的 children 会出现两次。

在调试的过程中session我检查了数据背后的所有资源,它们都没有问题。如果我返回 MainActivity 并再次启动列表,则视图完全正确。所以一定是删除一整组后人口不正确的问题

如您所见,如果我只从最后一组中删除最后一个 child 和两个 child,则整个列表的填充是正确的。

以下是有关该应用的更多信息:

在 BudgetListActivity 的 onCreate 中,我创建了两个 DAO 的 budgetDAO 和 categoryDAO 来获取数据并填充列表 allBudgetsList 和 all CatList。 有了这些信息,我创建了一个新数组 List allGroups ,其中包含视图所需的结构 - 分类为 header - 由于外键

,预算为 children

(这里只有一句话: 与此同时,我已经尝试对提供给 ExpandableListAdapter 的数据使用散列图 - 但结果是相同的错误视图人口!)

有一个 contentView "budget_expandable_list" 设置为 ExpandableListAdapter。适配器应使用 ArrayList "allGroups"

中的数据填充此列表的组和 childs

这是应用程序的结构

可能是有些资源没有实际使用

我现在将给出 importand classes

的源代码

预算列表活动:

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);
    }
}

如您所见,我使用上下文菜单来编辑和删除组 and/or childs。一些功能尚未完全实现。请理解,我将首先关注正确填充 ExpandableView 的主要问题。

还有其他事情——比如删除 child 后费用汇总的不正确更新——还不是很重要,稍后会完成。

这里是 class 组 Object:

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 中,我将 size 的数量加 1,因为我使用最后一个 children 作为页脚来显示费用摘要

希望我能为您提供所有必要的信息。如果缺少某些内容,请告诉我。我会尽快添加它。

希望有人能为我提供解决方案。 祝你今天过得愉快 安德烈亚斯

同时我找到了这种行为的原因。适配器源代码末尾有一个方法"getGroupID"。这里的 return 值被设置为 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;
}

这希望对所有同样 运行 遇到此问题的人有所帮助。 玩得开心 安德烈亚斯