如何编写自定义ExpandableListAdapter

时间:2011-03-04 00:03:40

标签: android expandablelistadapter

我正在编写自己的ExpandableListAdapter,其操作方式与ArrayAdapter类似。我的数据模型是:

public class Group {

    private String name;   

    private List<Child> children;
}

public class Child {

     private String name;
}

非常简单。如何将此关系映射到ExpandableListAdapter实现?我现在正在工作SimpleExpandableListAdapter,但我需要对项目进行更多自定义控制(显示图标等)。我该怎么办?

主要的是我需要一个add()方法来添加组,并在添加和从适配器中删除子项时使列表无效。我真的很惊讶SDK中没有实现(甚至是抽象的),这有助于实现这一点。

4 个答案:

答案 0 :(得分:26)

这是我刚刚发起的一个实现。我不知道它是否有效,但似乎&#34;聪明&#34;对我来说:)顺便说一下,如何获得组合的子id或组合组ID,所以我只是在那里即兴创作。

package example;

import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;

import android.content.Context;
import android.database.DataSetObservable;
import android.database.DataSetObserver;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ExpandableListAdapter;

public abstract class AbstractExpandableListAdapter<A, B> implements ExpandableListAdapter {

    private final List<Entry<A, List<B>>> objects;

    private final DataSetObservable dataSetObservable = new DataSetObservable();

    private final Context context;

    private final Integer groupClosedView;

    private final Integer groupExpandedView;

    private final Integer childView;

    private final LayoutInflater inflater;

    public AbstractExpandableListAdapter(Context context, int groupClosedView, 
            int groupExpandedView, int childView, List<Entry<A, List<B>>> objects) {
        this.context = context;
        this.objects = objects;
        this.groupClosedView = new Integer(groupClosedView);
        this.groupExpandedView = new Integer(groupExpandedView);
        this.childView = new Integer(childView);

        this.inflater = (LayoutInflater) this.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    public void add(Entry<A, List<B>> group) {
        this.getObjects().add(group);
        this.notifyDataSetChanged();
    }

    public void remove(A group) {
        for (Entry<A, List<B>> entry : this.getObjects()) {
            if (entry != null && entry.getKey().equals(group)) {
                this.getObjects().remove(group);
                this.notifyDataSetChanged();
                break;
            }
        }
    }

    public void remove(Entry<A, List<B>> entry) {
        remove(entry.getKey());
    }

    public void addChild(A group, B child) {
        for (Entry<A, List<B>> entry : this.getObjects()) {
            if (entry != null && entry.getKey().equals(group)) {
                if (entry.getValue() == null) 
                    entry.setValue(new ArrayList<B>());

                entry.getValue().add(child);
                this.notifyDataSetChanged();
                break;
            }
        }
    }

    public void removeChild(A group, B child) {
        for (Entry<A, List<B>> entry : this.getObjects()) {
            if (entry != null && entry.getKey().equals(group)) {
                if (entry.getValue() == null)
                    return;

                entry.getValue().remove(child);
                this.notifyDataSetChanged();
                break;
            }
        }
    }

    public void notifyDataSetChanged() {
        this.getDataSetObservable().notifyChanged();
    }

    public void notifyDataSetInvalidated() {
        this.getDataSetObservable().notifyInvalidated();
    }

    public void registerDataSetObserver(DataSetObserver observer) {
        this.getDataSetObservable().registerObserver(observer);
    }

    public void unregisterDataSetObserver(DataSetObserver observer) {
        this.getDataSetObservable().unregisterObserver(observer);
    }

    public int getGroupCount() {
        return getObjects().size();
    }

    public int getChildrenCount(int groupPosition) {
        return getObjects().get(groupPosition).getValue().size();
    }

    public Object getGroup(int groupPosition) {
        return getObjects().get(groupPosition).getKey();
    }

    public Object getChild(int groupPosition, int childPosition) {
        return getObjects().get(groupPosition).getValue().get(childPosition);
    }

    public long getGroupId(int groupPosition) {
        return ((Integer)groupPosition).longValue();
    }

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

    public boolean hasStableIds() {
        return true;
    }

    public View getGroupView(int groupPosition, boolean isExpanded,
            View convertView, ViewGroup parent) {

        if (convertView != null && convertView.getId() != 
                (isExpanded ? getGroupExpandedView() : getGroupClosedView())) {
//          do nothing, we're good to go, nothing has changed.
        } else {
//          something has changed, update.
            convertView = inflater.inflate(isExpanded ? getGroupExpandedView() :
                    getGroupClosedView(), parent, false);
            convertView.setTag(getObjects().get(groupPosition));
        }

        return convertView;
    }

    public View getChildView(int groupPosition, int childPosition,
            boolean isLastChild, View convertView, ViewGroup parent) {

        if (convertView != null) {
//          do nothing 
        } else {
//          create
            convertView = inflater.inflate(getChildView(), parent, false);
            convertView.setTag(getObjects().get(groupPosition).getValue().get(childPosition));
        }

        return convertView;
    }

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

    public boolean areAllItemsEnabled() {
        return true;
    }

    public boolean isEmpty() {
        return getObjects().size() == 0;
    }

    public void onGroupExpanded(int groupPosition) {

    }

    public void onGroupCollapsed(int groupPosition) {

    }

    public long getCombinedChildId(long groupId, long childId) {
        return groupId * 10000L + childId;
    }

    public long getCombinedGroupId(long groupId) {
        return groupId * 10000L;
    }

    protected DataSetObservable getDataSetObservable() {
        return dataSetObservable;
    }

    protected List<Entry<A, List<B>>> getObjects() {
        return objects;
    }

    protected Context getContext() {
        return context;
    }

    protected Integer getGroupClosedView() {
        return groupClosedView;
    }

    protected Integer getGroupExpandedView() {
        return groupExpandedView;
    }

    protected Integer getChildView() {
        return childView;
    }
}

欢迎任何评论或批评。

答案 1 :(得分:4)

我很惊讶我没有找到更好的文档。如果您找到,请在此处发布。我发现的最佳实现示例是在ApiDemos中。有ExpandableListActivity实现了BaseExpandableListAdapter。该类名为ExpandableList1.java。

您必须创建自己的add()方法,将GroupChild类添加到适配器。我不认为乍一看会那么困难。实际上,您可能只能创建对类对象的引用。当我实现我的时,我的数据集很小并且没有改变所以我只需要引用我的array.xml文件。

答案 2 :(得分:1)

   public class CustomExpandableAdapter extends BaseExpandableListAdapter {
        private Context mContext;
        private List<Group> mData;
        private int mSelectedPosition = -1;

        public CustomExpandableAdapter(Context context, List<Group> data ) {
            mData = data;
            mContext = context;

        }

        @Override
        public int getGroupCount() {
            return mData.size();
        }

        @Override
        public int getChildrenCount(int groupPosition) {
            return mData.get(groupPosition).children.size();
        }

        @Override
        public Object getGroup(int groupPosition) {
            return mData.get(groupPosition);
        }

        @Override
        public Object getChild(int groupPosition, int childPosition) {
            return mData.get(groupPosition).children.get(childPosition);
        }

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

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

        @Override
        public boolean hasStableIds() {
            return false;
        }

        @Override
        public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
            HeaderViewHolder headerViewHolder = null;
            if (convertView == null) {
                convertView = LayoutInflater.from(mContext).inflate(R.layout.faq_header_text_layout, null);
                headerViewHolder = new HeaderViewHolder(convertView);
                convertView.setTag(headerViewHolder);
            }
            headerViewHolder = (HeaderViewHolder) convertView.getTag();

            headerViewHolder.mGroupHeader.setText(mData.get(groupPosition).name);
            return convertView;
        }

        @Override
        public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
            ChildViewHolder childViewHolder = null;
            if (convertView == null) {
                convertView = LayoutInflater.from(mContext).inflate(R.layout.faq_textview_layout, null);
                childViewHolder = new ChildViewHolder(convertView);
                convertView.setTag(childViewHolder);
            }
            childViewHolder = (ChildViewHolder) convertView.getTag();

                        childViewHolder.mChildTitle.setText(mData.get(groupPosition).children.get(childPosition));
            return convertView;
        }

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

        private static class HeaderViewHolder {
            final TextView mGroupHeader;

            private HeaderViewHolder(View group) {
                mGroupHeader = (TextView) group.findViewById(R.id.txv_faq_header_text_layout);
            }
        }

        private static class ChildViewHolder {
            final TextView mChildTitle;

            private ChildViewHolder(View group) {
                mChildTitle = (TextView) group.findViewById(R.id.txv_faq_textview_layout);
            }
        }

        @Override
        public void unregisterDataSetObserver(DataSetObserver observer) {
            if (observer != null) {
                super.unregisterDataSetObserver(observer);
            }
        }

        public void setSelectedPosition(int selectedPosition) {
            mSelectedPosition = selectedPosition;
        }
    }

答案 3 :(得分:1)

看到这篇文章和答案有多久,我想我会指出有一个非常好的3rd party library,这种填补了这个缺失的空白。虽然发布的自定义解决方案很好,但它们仍然缺少一些东西,并且正在遵循要求程序员生成数据结构的数据结构的繁琐设计。有时候,你只想把一个List组织成漂亮的小组,而不必自己去做。

它被称为 RolodexArrayAdapter ,可以轻松用于创建自定义 ExpandableListAdapters ...而无需担心所有数据管理问题和功能。它支持add,addAll,remove,removeAll,retainAll,contains,sorting等方法。它还支持更高级的功能,如ChoiceMode,Filtering和自动扩展组。

示例:

class MovieAdapter extends RolodexArrayAdapter<Integer, MovieItem> {
    public MovieAdapter(Context activity, List<MovieItem> movies) {
        super(activity, movies);
    }

    @Override
    public Integer createGroupFor(MovieItem childItem) {
        //Lets organize our movies by their release year
        return childItem.year;
    }

    @Override
    public View getChildView(LayoutInflater inflater, int groupPosition, int childPosition,
                             boolean isLastChild, View convertView, ViewGroup parent) {
        if (convertView == null) {
            //Inflate your view
        }
        //Fill view with data
        return convertView;
    }

    @Override
    public View getGroupView(LayoutInflater inflater, int groupPosition, boolean isExpanded,
                             View convertView, ViewGroup parent) {
        if (convertView == null) {
            //Inflate your view
        }
        //Fill view with data
        return convertView;
    }

    @Override
    public boolean hasAutoExpandingGroups() {
        return true;
    }

    @Override
    protected boolean isChildFilteredOut(MovieItem movie, CharSequence constraint) {
        //Lets filter by movie title
        return !movie.title.toLowerCase(Locale.US).contains(
                constraint.toString().toLowerCase(Locale.US));
    }

    @Override
    protected boolean isGroupFilteredOut(Integer year, CharSequence constraint) {
        //Lets filter out everything whose year does not match the numeric values in the constraint.
        return TextUtils.isDigitsOnly(constraint) && !year.toString().contains(constraint);
    }
}