2011-03-04 48 views
13

我正在尋求編寫自己的ExpandableListAdapter,其操作方式與ArrayAdapter相似。我的數據模型是這樣的:如何編寫自定義ExpandableListAdapter

public class Group { 

    private String name; 

    private List<Child> children; 
} 

public class Child { 

    private String name; 
} 

很簡單。我如何將這種關係映射爲ExpandableListAdapter實現?我現在有一個工作SimpleExpandableListAdapter工作,但我需要更多的自定義項目控制(顯示圖標等)。我應該怎麼做?

最主要的是我需要一個add()方法來添加組,並且當從適配器中添加和刪除子組時,使列表無效。我真的很驚訝,SDK中沒有實現(甚至是抽象的),這有助於實現這一點。

回答

26

這是我剛剛提出的一個實現。我不知道它是否有效,但對我來說似乎是「聰明」:)順便說一下,應該如何獲得組合的兒童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; 
    } 
} 

歡迎任何評論或批評。

+0

順便說一句,只是測試這一點,它似乎工作的偉大。 :) – 2011-03-04 22:28:00

+0

剛剛出色,謝謝! – myforums 2014-11-17 19:18:56

+0

讓人印象深刻的是當場鞭打。 – 2015-02-08 22:02:07

4

我很驚訝我沒有找到更好的文檔。如果你找到一個,請在這裏發佈。我發現的最好的實現例子是ApiDemos。有一個ExpandableListActivity,實現了BaseExpandableListAdapter。該類被稱爲ExpandableList1.java。

您將不得不創建自己的add()方法,將GroupChild類添加到適配器。我認爲乍看起來不會那麼困難。實際上你可能只能創建對類對象的引用。當我實現我的時候,我的數據集很小,並沒有改變,所以我只需要引用我的array.xml文件。

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