Как написать собственный ExpandableListAdapter

Я хочу написать свой собственный ExpandableListAdapter который работает аналогично ArrayAdapter . Моя модель данных такова:

 public class Group { private String name; private List<Child> children; } public class Child { private String name; } 

Довольно просто. Как я могу сопоставить эти отношения с реализацией ExpandableListAdapter ? У меня есть рабочий SimpleExpandableListAdapter работающий прямо сейчас, но мне нужно больше настраиваемого контроля над элементами (отображение значков и т. Д.). Что я должен сделать для этого?

Главное, что мне нужен метод add() чтобы иметь возможность добавлять группы и аннулировать список, когда дети добавляются и удаляются из адаптера. Я действительно удивлен тем, что в SDK нет (даже абстрактной) реализации, которая помогает выполнить это.

Solutions Collecting From Web of "Как написать собственный ExpandableListAdapter"

Вот реализация, которую я просто взбивал. Я понятия не имею, работает ли это или нет, но мне кажется «умным» 🙂 Кстати, как нужно собирать комбинированный идентификатор ребенка или объединенный идентификатор группы, поэтому я просто импровизировал там.

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

Любые комментарии или критика приветствуются.

Я был довольно удивлен, что я тоже не нашел лучшей документации. Если вы его найдете, напишите здесь. Лучший пример реализации, который я нашел, был в ApiDemos. Существует ExpandableListActivity , реализующая BaseExpandableListAdapter . Класс называется ExpandableList1.java.

Вам нужно будет создать свой собственный метод add() который добавит ваши классы Group и Child к адаптеру. Я не думаю, что это будет сложно с первого взгляда. Фактически вы можете просто создать ссылки на объекты класса. Когда я реализовал мой, мой набор данных был небольшим и не менялся, поэтому мне нужно было только обратиться к моему файлу array.xml.

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

Увидев, сколько лет этот пост и ответы, я думал, что хочу отметить, что есть очень хорошая сторонняя библиотека, которая sorta заполняет этот недостающий пробел. Хотя опубликованные пользовательские решения хороши, они все еще не хватает некоторых вещей и следуют громоздкому дизайну, требуя от программиста генерировать структуру данных структур данных. Иногда вы просто хотите организовать один Список в симпатичные маленькие группы без хлопот делать это самостоятельно.

Он называется RolodexArrayAdapter и может быть легко использован при создании пользовательских ExpandableListAdapters … без необходимости беспокоиться обо всех проблемах и функциях управления данными. Он поддерживает такие методы, как add, addAll, remove, removeAll, retainAll, содержит, сортировку и т. Д. Он также поддерживает более сложные функции, такие как 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); } }