【问题标题】:Initializing CheckBoxes in ExpandableListView在 ExpandableListView 中初始化复选框
【发布时间】:2013-06-04 05:27:52
【问题描述】:

我有一个 ExpandableListView,其中每一行都包含一个 CheckBox 和一个 TextView。扩展组时,我需要根据从 sqlite 数据库中提取的数据(并存储在 ArrayList 中)初始化每个复选框。我有数组列表。这很容易。

我无法处理每个复选框。现在,无论我做什么,当我尝试引用它们中的任何一个时,我都会继续遇到空指针错误。这是我的代码(在活动的 onCreate 方法中):

expandableList.setOnGroupClickListener(new OnGroupClickListener() {

    @Override
    public boolean onGroupClick(ExpandableListView parent, View v, int groupPostition, long id) {

        //this initializes to 0 
        int i = expandableList.getLastVisiblePosition() - expandableList.getFirstVisiblePosition();

        for (MySettings thisSetting : settingsList) {
            System.err.println(i);
            View view = parent.getChildAt(i);
            CheckBox checkBox = (CheckBox) view.findViewById(R.id.check_box); //<--null pointer error
            System.err.println(thisSetting.getIsSelected());
            checkBox.setChecked(thisSetting.getIsSelected());
            i++;
        }

        return false;
    }

});

我在声明 checkBox 变量的那一行出现空指针错误。我使用几乎相同的代码来处理 onChildClick 事件,所以我不明白这有什么不同。谢谢你的帮助。如果您需要其他代码,请告诉我。

【问题讨论】:

    标签: android checkbox expandablelistview


    【解决方案1】:

    我将尝试解释我在申请中的做法。

    您首先必须创建自己的自定义适配器,它允许您使用您的列表、数组或任何您使用的东西来初始化列表:

    public class CustomELVAdapter extends BaseExpandableListAdapter {
    
    
    private LayoutInflater mInflater;
    private ArrayList<AttributeFilter> mParent;//List containing the groups to display
    
    public CustomELVAdapter(Context context, ArrayList<AttributeFilter> parent){
        mParent = parent;
        mInflater = LayoutInflater.from(context);
    }
    
    
    @Override
    //counts the number of groups in the exapndable list
    public int getGroupCount() {
        return mParent.size();
    }
    
    @Override
    //counts the number of children items contained in a specific group
    public int getChildrenCount(int groupPosition) {
        return mParent.get(groupPosition).getArrayChildren().size();
    }
    
    @Override
    //gets the title ofa group
    public Object getGroup(int groupPosition) {
        return mParent.get(groupPosition).getTitle();
    }
    
    @Override
    //gets a child object in a group
    public Object getChild(int groupPosition, int childPosition) {
        //return mParent.get(groupPosition).getArrayChildren().get(childPosition);
        return mParent.get(groupPosition).getArrayChildren().valueAt(childPosition);
    }
    
    @Override
    public long getGroupId(int i) {
        return i;
    }
    
    @Override
    public long getChildId(int i, int i1) {
        return i1;
    }
    
    @Override
    public boolean hasStableIds() {
        return true;
    }
    
    @Override
    //in this method you must set the text to see the parent/group on the list
    public View getGroupView(int groupPosition, boolean b, View view, ViewGroup viewGroup) {
    
        if (view == null) {
            view = mInflater.inflate(R.layout.filter_list_parent, viewGroup,false);
        }
    
        TextView textView = (TextView) view.findViewById(R.id.filter_list_parent_textview);
        TextView detailsTV = (TextView) view.findViewById(R.id.filter_list_parent_details);
    
        Object parentObject = mParent.get(groupPosition);
        AttributeFilter attributeFilter = (AttributeFilter) parentObject;
        //"i" is the position of the parent/group in the list
        textView.setText(getGroup(groupPosition).toString());
        detailsTV.setText(attributeFilter.getDetails());
    
        //return the entire view
        return view;
    }
    
    @Override
    //set a child in a group
    public View getChildView(int groupPosition, int childPosition, boolean b, View view, ViewGroup viewGroup) {
        ViewHolder holder;
        if (view == null) {
            view = mInflater.inflate(R.layout.filter_list_child, viewGroup,false);
            holder = new ViewHolder();
            //holder.imageView = (ImageView) view.findViewById(R.id.filter_list_child_imageview);
            holder.checkbox = (CheckBox) view.findViewById(R.id.filter_list_child_checkbox);
            holder.title = (TextView) view.findViewById(R.id.filter_list_child_textview);
    
            view.setTag(holder);
        }
        else {  
            holder = (ViewHolder) view.getTag();  
        }   
    
        try {
            Attribute attribute = mParent.get(groupPosition).getArrayChildren().valueAt(childPosition);
    
            holder.checkbox.setChecked(attribute.isSelected());
            holder.title.setText(attribute.getName());
    
            view.setTag(R.string.filter_attribute_id, attribute.getID());
        } catch (Exception e) {
            Log.e(RateDayApplication.LOG_ERROR,"CustomELVAdapter - getChildView - attribute is null");
        }
    
        //return the entire view
        return view;
    }
    
    @Override
    public boolean isChildSelectable(int i, int i1) {
        return true;
    }
    
    @Override
    public void registerDataSetObserver(DataSetObserver observer) {
        /* used to make the notifyDataSetChanged() method work */
        super.registerDataSetObserver(observer);
    }
    
    static class ViewHolder {
        CheckBox checkbox;
        TextView title;
        //ImageView icon; NOT NEEDED YET
    }
    
    }
    

    在我的示例中,mParent 中的孩子都包含在SparseArray 中,这对我来说很方便,因为我需要访问具有特定 ID 的孩子,而不仅仅是一个位置。但是在您的情况下,您当然可以做任何事情,例如,如果您使用列表,则必须修改代码以通过get 修改valueAtvalueAt 是可用于 SparseArrays 但不适用于列表的函数)。

    此适配器完成后,您必须在活动中准备要提供的数据。就我而言,我有 3 个组(固定),所有组都包含不同数量的孩子:

    ArrayList<AttributeFilter> arrayParents = new ArrayList<AttributeFilter>();//Initialize of the parents array that will be provided to the adapter.
    
        //Preparation of the first group (AttributeFilter is a custom type I created holding the `SparseArray` with the children and the group title)
        AttributeFilter categoriesFilter = new AttributeFilter();
        categoriesFilter.setTitle(getResources().getString(R.string.categories));
        categoriesFilter.setDetails(Tools.getLabelsStringFromSparseArray(categoriesSparseArray, prefs.getCategories()));
        categoriesSparseArray = Tools.initializeSparseArray(categoriesSparseArray, prefs.getCategories());
        categoriesFilter.setArrayChildren(categoriesSparseArray);
    
        //Preparation of the second group
        AttributeFilter emotionsFilter = new AttributeFilter();
        emotionsFilter.setTitle(getResources().getString(R.string.emotions));
        emotionsFilter.setDetails(Tools.getLabelsStringFromSparseArray(emotionsSparseArray, prefs.getEmotions()));
        emotionsSparseArray = Tools.initializeSparseArray(emotionsSparseArray, prefs.getEmotions());
        emotionsFilter.setArrayChildren(emotionsSparseArray);
    
        //Preparation of the third group
        AttributeFilter ratingsFilter = new AttributeFilter();
        ratingsFilter.setTitle(getResources().getString(R.string.ratings));
        ratingsFilter.setDetails(Tools.getLabelsStringFromSparseArray(ratingsSparseArray, prefs.getRatings()));
        ratingsSparseArray = Tools.initializeSparseArray(ratingsSparseArray, prefs.getRatings());
        ratingsFilter.setArrayChildren(ratingsSparseArray);
    
        //in this array we add the Parent object. We will use the arrayParents at the setAdapter
        arrayParents.add(categoriesFilter);
        arrayParents.add(emotionsFilter);
        arrayParents.add(ratingsFilter);
    
        //sets the adapter that provides data to the list.
        filtersELV.setAdapter(new CustomELVAdapter(this,arrayParents));//the adapter is applied on my `ExpandableListView`.
    

    这是自定义类型定义FilterAttribute(仅供参考):

    public class AttributeFilter {
    private String mTitle;
    private String mDetails;
    private SparseArray<Attribute> mArrayChildren;
    
    public String getTitle() {
        return mTitle;
    }
    
    public void setTitle(String mTitle) {
        this.mTitle = mTitle;
    }
    
    public String getDetails() {
        return mDetails;
    }
    
    public void setDetails(String mDetails) {
        this.mDetails = mDetails;
    }
    
    public SparseArray<Attribute> getArrayChildren() {
        return mArrayChildren;
    }
    
    public void setArrayChildren(SparseArray<Attribute> mArrayChildren) {
        this.mArrayChildren = mArrayChildren;
    }
    
    }
    

    以防万一,班级Attribute

    public class Attribute {
    
    private int id;
    private String name;
    private boolean selected;
    private int drawableID;
    
    // constructor
    public Attribute(int _id, String _name, int _drawable_id, boolean _selected){
        id = _id;
        name = _name;
        drawableID = _drawable_id;
        selected = _selected;       
    }
    
    public int getID() {
        return id;
    }
    
    public void setID(int _id) {
        this.id = _id;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String _name) {
        this.name = _name;
    }
    
    public int getDrawableID() {
        return drawableID;
    }
    
    public void setDrawableID(int _drawable_id) {
        this.drawableID = _drawable_id;
    }
    
    public boolean isSelected() {
        return selected;
    }
    
    public void setSelected(boolean _selected) {
        this.selected = _selected;
    }
    
    }
    

    对于布局很重要的一件事,你必须将复选框设置为不可聚焦,否则你将永远无法使复选框切换工作:

    <?xml version="1.0" encoding="utf-8"?>
    

    <CheckBox android:id="@+id/filter_list_child_checkbox"
           android:focusable="false"
           android:focusableInTouchMode="false"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"/>
    
    <TextView
        android:id="@+id/filter_list_child_textview"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginLeft="5dp"
        android:padding="10dp"
        android:layout_weight="1" />
    

    希望不会太难消化

    【讨论】:

    • 我不是在谈论处理项目点击。这是在用户单击任何复选框之前初始化复选框的状态。
    • 您得到一个空指针,因为您尝试获取组上的复选框,而不是子级上的复选框。我认为最好通过覆盖 getChildView 函数来初始化自定义适配器中的复选框。
    • 谢谢。我也是这么想的。不过,这改变了我的问题。我需要传递一个字符串才能执行填充我的数组列表的查询。我不知道如何将数据传递给适配器。
    • 我可以使用共享首选项甚至是一个小的 sqlite 表,然后在适配器中检索该数据。有没有更好的办法?
    • 如果保持信息检查/未检查的数据在settingsList,您应该在创建新适配器时提供它。如果您需要,我可以发布我的自定义 BaseExpandableListAdapter
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-29
    • 2022-08-22
    • 1970-01-01
    相关资源
    最近更新 更多