【问题标题】:How to write custom ExpandableListAdapter如何编写自定义 ExpandableListAdapter
【发布时间】:2011-03-04 00:03:40
【问题描述】:

我希望编写自己的 ExpandableListAdapter,其操作类似于 ArrayAdapter。我的数据模型是这样的:

public class Group {

    private String name;   

    private List<Child> children;
}

public class Child {

     private String name;
}

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

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

【问题讨论】:

    标签: android expandablelistadapter


    【解决方案1】:

    这是我刚刚创建的一个实现。我不知道它是否有效,但对我来说似乎“聪明”:) 顺便说一句,应该如何获得组合的子 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;
        }
    }
    

    欢迎大家批评指正。

    【讨论】:

    • 当场把它掀起来相当令人印象深刻。
    • 如果你扩展 BaseExpandableListAdapter 而不是实现 ExpandableListAdapter 那么你应该不需要实现组合 id 的方法
    • Android SDK simple and minimum 可扩展列表适配器的内置视图资源可以传递给AbstractExpandableListAdapter 构造函数是android.R.layout.simple_expandable_list_item_1 用于groupClosedView 和@987654325 @ 和 android.R.layout.simple_list_item_1 代表 childView
    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:
         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;
              }
          }
      

      【讨论】:

        【解决方案4】:

        看到这篇文章和答案有多老,我想我会指出有一个非常好的3rd party library 可以填补这个缺失的空白。虽然发布的自定义解决方案很好,但它们仍然缺少一些东西,并且遵循要求程序员生成数据结构的数据结构的繁琐设计。有时,您只想将一个列表组织成漂亮的小组,而无需自己动手。

        它被称为 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);
            }
        }
        

        【讨论】:

          猜你喜欢
          • 2020-02-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-06-27
          • 2019-04-07
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多