不同组的不同 Child 布局 ExpandableListView

Different Child Layouts for different groups ExpandableListView

我在尝试执行此操作时遇到了问题。我好像不太对劲,我想控制 parent 中的每个 parent + children。 ExpandableListView 一直让我头疼。 包裹评论;

public class CommentsExpandableListAdapter extends BaseExpandableListAdapter {

private Activity context;
private Map<String, List<String>> comments_feed_collection;
private List<String> group_list;



private boolean a_comment = false;
public CommentsExpandableListAdapter(Activity context, List<String> group_list,
                             Map<String, List<String>> comments_feed_collection) {
    this.context = context;
    this.comments_feed_collection = comments_feed_collection;
    this.group_list = group_list;
}


public Object getChild(int groupPosition, int childPosition) {
    return comments_feed_collection.get(group_list.get(groupPosition)).get(childPosition);
}

public long getChildId(int groupPosition, int childPosition) {
    return childPosition;
}

public View getChildView(final int groupPosition, final int childPosition,
                         boolean isLastChild, View convertView, ViewGroup parent) {
    final String incoming_text = (String) getChild(groupPosition, childPosition);
    LayoutInflater inflater = context.getLayoutInflater();


    if (convertView == null) {
        //first view
        if(childPosition==0 && groupPosition==0) {
            convertView = inflater.inflate(R.layout.description_of_ads_expandable_list, null);
            TextView description_child = (TextView) convertView.findViewById(R.id.description_of_ads_expandable_list_child_text_view);
            description_child.setText(incoming_text);
        }else if(childPosition==0 && groupPosition==1){
            //second view view
            convertView = inflater.inflate(R.layout.comments_create_comment, null);
        }else if(childPosition>=1 && groupPosition>=1){
            //thrid view
            convertView = inflater.inflate(R.layout.comments_expandable_list_child, null);
        }
    }
    return convertView;
}

public int getChildrenCount(int groupPosition) {
    return comments_feed_collection.get(group_list.get(groupPosition)).size();
}

public Object getGroup(int groupPosition) {
    return group_list.get(groupPosition);
}
public int getGroupCount() {
    return group_list.size();
}

public long getGroupId(int groupPosition) {
    return groupPosition;
}


public View getGroupView(int groupPosition, boolean isExpanded,
                         View convertView, ViewGroup parent) {
    String incoming_text = (String) getGroup(groupPosition);
    if (convertView == null) {
        LayoutInflater infalInflater = (LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = infalInflater.inflate(R.layout.expandable_list_single_item,
                null);
    }
    TextView item = (TextView) convertView.findViewById(R.id.expandable_list_single_item_text_view_group);
    item.setTypeface(null, Typeface.BOLD);
    item.setText(incoming_text);
    return convertView;
}


public boolean hasStableIds() {
    return true;
}


public boolean isChildSelectable(int groupPosition, int childPosition) {
    return true;
}


@Override
public int getChildTypeCount() {
    return 3;
}

@Override
public int getGroupType(int groupPosition) {
    return super.getGroupType(groupPosition);
}

@Override
public int getGroupTypeCount() {
    return 3;
}
}

我认为你的主要错误在于public View getChildView(...)

  • 条件 if (convertView == null) 实际上只达到一次,那是当您加载您标记为第一视图的视图时。因此,您正在膨胀第一个布局 R.layout.description_of_ads_expandable_list 并将其重新用于列表中的所有其他行。
  • 您检查要加载的子视图类型的方式很危险,因为它不完整。如果 childPosition==1 && groupPosition==0 你认为会发生什么?轰! NullPointerException 如果您的数据源对于第一组还有一个元素。

所以要事第一;如果您想对 ExpandableListView 的不同 groups/children 使用不同的布局,则应完全实现 HeterogeneousExpandableList interface

然后使用该界面在应加载布局的条件下工作。

终于在 public View getChildView(...) 上解决重用视图问题。

因为我是一个好人,这里有一个片段应该可以帮助你(基于你的适配器 post):

public class CommentsExpandableListAdapter extends BaseExpandableListAdapter {

    // 4 Child types
    private static final int CHILD_TYPE_1 = 0;
    private static final int CHILD_TYPE_2 = 1;
    private static final int CHILD_TYPE_3 = 2;
    private static final int CHILD_TYPE_UNDEFINED = 3;

    // 3 Group types
    private static final int GROUP_TYPE_1 = 0;
    private static final int GROUP_TYPE_2 = 1;
    private static final int  GROUP_TYPE_3 = 2;

    private Activity context;
    private Map<String, List<String>> comments_feed_collection;
    private List<String> group_list;

    public CommentsExpandableListAdapter(Activity context, List<String> group_list,
                                         Map<String, List<String>> comments_feed_collection) {
        this.context = context;
        this.comments_feed_collection = comments_feed_collection;
        this.group_list = group_list;
    }

    public Object getChild(int groupPosition, int childPosition) {
        return comments_feed_collection.get(group_list.get(groupPosition)).get(childPosition);
    }

    public long getChildId(int groupPosition, int childPosition) {
        return childPosition;
    }

    public View getChildView(final int groupPosition, final int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
        final String incoming_text = (String) getChild(groupPosition, childPosition);
        LayoutInflater inflater = context.getLayoutInflater();

        int childType = getChildType(groupPosition, childPosition);

        // We need to create a new "cell container"
        if (convertView == null || convertView.getTag() != childType) {
            switch (childType) {
                case CHILD_TYPE_1:
                    convertView = inflater.inflate(R.layout.description_of_ads_expandable_list, null);
                    convertView.setTag(childType);
                    break;
                case CHILD_TYPE_2:
                    convertView = inflater.inflate(R.layout.comments_create_comment, null);
                    convertView.setTag(childType);
                    break;
                case CHILD_TYPE_3:
                    convertView = inflater.inflate(R.layout.comments_expandable_list_child, null);
                    convertView.setTag(childType);
                    break;
                case CHILD_TYPE_UNDEFINED:
                    convertView = inflater.inflate(R.layout.comments_undefined, null);
                    convertView.setTag(childType);
                    break;
                default:
                    // Maybe we should implement a default behaviour but it should be ok we know there are 4 child types right?
                    break;
            }
        }
        // We'll reuse the existing one
        else {
            // There is nothing to do here really we just need to set the content of view which we do in both cases
        }

        switch (childType) {
            case CHILD_TYPE_1:
                TextView description_child = (TextView) convertView.findViewById(R.id.description_of_ads_expandable_list_child_text_view);
                description_child.setText(incoming_text);
                break;
            case CHILD_TYPE_2:
                //Define how to render the data on the CHILD_TYPE_2 layout
                break;
            case CHILD_TYPE_3:
                //Define how to render the data on the CHILD_TYPE_3 layout
                break;
            case CHILD_TYPE_UNDEFINED:
                //Define how to render the data on the CHILD_TYPE_UNDEFINED layout
                break;
        }

        return convertView;
    }

    public int getChildrenCount(int groupPosition) {
        String groupName = group_list.get(groupPosition);
        List<String> groupContent = comments_feed_collection.get(groupName);
        return groupContent.size();
    }

    public Object getGroup(int groupPosition) {
        return group_list.get(groupPosition);
    }
    public int getGroupCount() {
        return group_list.size();
    }

    public long getGroupId(int groupPosition) {
        return groupPosition;
    }

    public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
        LayoutInflater inflater = context.getLayoutInflater();
        final String incoming_text = (String) getGroup(groupPosition);

        int groupType = getGroupType(groupPosition);

        // We need to create a new "cell container"
        if (convertView == null || convertView.getTag() != groupType) {
            switch (groupType) {
                case GROUP_TYPE_1 :
                    convertView = inflater.inflate(R.layout.expandable_list_single_item, null);
                    break;
                case GROUP_TYPE_2:
                    // Am using the same layout cause am lasy and don't wanna create other ones but theses should be different
                    // or the group type shouldnt exist
                    convertView = inflater.inflate(R.layout.expandable_list_single_item, null);
                    break;
                case GROUP_TYPE_3:
                    // Am using the same layout cause am lasy and don't wanna create other ones but theses should be different
                    // or the group type shouldnt exist
                    convertView = inflater.inflate(R.layout.expandable_list_single_item, null);
                    break;
                default:
                    // Maybe we should implement a default behaviour but it should be ok we know there are 3 group types right?
                    break;
            }
        }
        // We'll reuse the existing one
        else {
            // There is nothing to do here really we just need to set the content of view which we do in both cases
        }

        switch (groupType) {
            case GROUP_TYPE_1 :
                TextView item = (TextView) convertView.findViewById(R.id.expandable_list_single_item_text_view_group);
                item.setTypeface(null, Typeface.BOLD);
                item.setText(incoming_text);
                break;
            case GROUP_TYPE_2:
                //TODO: Define how to render the data on the GROUPE_TYPE_2 layout
                // Since i use the same layout as GROUPE_TYPE_1 i could do the same thing as above but i choose to do nothing
                break;
            case GROUP_TYPE_3:
                //TODO: Define how to render the data on the GROUPE_TYPE_3 layout
                // Since i use the same layout as GROUPE_TYPE_1 i could do the same thing as above but i choose to do nothing
                break;
            default:
                // Maybe we should implement a default behaviour but it should be ok we know there are 3 group types right?
                break;
        }

        return convertView;
    }


    public boolean hasStableIds() {
        return true;
    }


    public boolean isChildSelectable(int groupPosition, int childPosition) {
        return false;
    }


    @Override
    public int getChildTypeCount() {
        return 4; // I defined 4 child types (CHILD_TYPE_1, CHILD_TYPE_2, CHILD_TYPE_3, CHILD_TYPE_UNDEFINED)
    }

    @Override
    public int getGroupTypeCount() {
        return 3; // I defined 3 groups types (GROUP_TYPE_1, GROUP_TYPE_2, GROUP_TYPE_3)
    }

    @Override
    public int getGroupType(int groupPosition) {
        switch (groupPosition) {
            case 0:
                return GROUP_TYPE_1;
            case 1:
                return GROUP_TYPE_1;
            case 2:
                return GROUP_TYPE_2;
            default:
                return GROUP_TYPE_3;
        }
    }

    @Override
    public int getChildType(int groupPosition, int childPosition) {
        switch (groupPosition) {
            case 0:
                switch (childPosition) {
                    case 0:
                        return CHILD_TYPE_1;
                    case 1:
                        return CHILD_TYPE_UNDEFINED;
                    case 2:
                        return CHILD_TYPE_UNDEFINED;
                }
                break;
            case 1:
                switch (childPosition) {
                    case 0:
                        return CHILD_TYPE_2;
                    case 1:
                        return CHILD_TYPE_3;
                    case 2:
                        return CHILD_TYPE_3;
                }
                break;
            default:
                return CHILD_TYPE_UNDEFINED;
        }

        return CHILD_TYPE_UNDEFINED;
    }
}

这是它的样子:

希望这对您有所帮助,如果有帮助请告诉我 does/doesnt。