android attaching multiple adapters to one adapter

You can use MergeAdapter for your ListView. This is my modified and fully tested version.

/**
* Adapter that merges multiple child adapters and views into a single
* contiguous whole.
* 
* Adapters used as pieces within MergeAdapter must have view type IDs
* monotonically increasing from 0. Ideally, adapters also have distinct ranges
* for their row ids, as returned by getItemId().
* 
*/
public class MergeAdapter extends BaseAdapter implements SectionIndexer {
    protected ArrayList<ListAdapter> pieces = new ArrayList<ListAdapter>();
    protected String noItemsText;

    /**
    * Stock constructor, simply chaining to the superclass.
    */
    public MergeAdapter() {
        super();
    }

    /**
    * Adds a new adapter to the roster of things to appear in the aggregate
    * list.
    * 
    * @param adapter
    *            Source for row views for this section
    */
    public void addAdapter(ListAdapter adapter) {
        pieces.add(adapter);
        adapter.registerDataSetObserver(new CascadeDataSetObserver());
    }

    /**
    * Get the data item associated with the specified position in the data set.
    * 
    * @param position
    *            Position of the item whose data we want
    */
    public Object getItem(int position) {
        for (ListAdapter piece : pieces) {
            int size = piece.getCount();

            if (position < size) {
                return (piece.getItem(position));
            }

            position -= size;
        }

        return (null);
    }

    public void setNoItemsText(String text){
        noItemsText = text;
    }

    /**
    * Get the adapter associated with the specified position in the data set.
    * 
    * @param position
    *            Position of the item whose adapter we want
    */
    public ListAdapter getAdapter(int position) {
        for (ListAdapter piece : pieces) {
            int size = piece.getCount();

            if (position < size) {
                return (piece);
            }

            position -= size;
        }

        return (null);
    }

    /**
    * How many items are in the data set represented by this Adapter.
    */
    public int getCount() {
        int total = 0;

        for (ListAdapter piece : pieces) {
            total += piece.getCount();
        }

        if(total == 0 && noItemsText != null){
            total = 1;
        }

        return (total);
    }

    /**
    * Returns the number of types of Views that will be created by getView().
    */
    @Override
    public int getViewTypeCount() {
        int total = 0;

        for (ListAdapter piece : pieces) {
            total += piece.getViewTypeCount();
        }

        return (Math.max(total, 1)); // needed for setListAdapter() before
                                        // content add'
    }

    /**
    * Get the type of View that will be created by getView() for the specified
    * item.
    * 
    * @param position
    *            Position of the item whose data we want
    */
    @Override
    public int getItemViewType(int position) {
        int typeOffset = 0;
        int result = -1;

        for (ListAdapter piece : pieces) {
            int size = piece.getCount();

            if (position < size) {
                result = typeOffset + piece.getItemViewType(position);
                break;
            }

            position -= size;
            typeOffset += piece.getViewTypeCount();
        }

        return (result);
    }

    /**
    * Are all items in this ListAdapter enabled? If yes it means all items are
    * selectable and clickable.
    */
    @Override
    public boolean areAllItemsEnabled() {
        return (false);
    }

    /**
    * Returns true if the item at the specified position is not a separator.
    * 
    * @param position
    *            Position of the item whose data we want
    */
    @Override
    public boolean isEnabled(int position) {
        for (ListAdapter piece : pieces) {
            int size = piece.getCount();

            if (position < size) {
                return (piece.isEnabled(position));
            }

            position -= size;
        }

        return (false);
    }

    /**
    * Get a View that displays the data at the specified position in the data
    * set.
    * 
    * @param position
    *            Position of the item whose data we want
    * @param convertView
    *            View to recycle, if not null
    * @param parent
    *            ViewGroup containing the returned View
    */
    public View getView(int position, View convertView, ViewGroup parent) {
        for (ListAdapter piece : pieces) {
            int size = piece.getCount();

            if (position < size) {

                return (piece.getView(position, convertView, parent));
            }

            position -= size;
        }

        if(noItemsText != null){
            TextView text = new TextView(parent.getContext());
            text.setText(noItemsText);
            return text;
        }

        return (null);
    }

    /**
    * Get the row id associated with the specified position in the list.
    * 
    * @param position
    *            Position of the item whose data we want
    */
    public long getItemId(int position) {
        for (ListAdapter piece : pieces) {
            int size = piece.getCount();

            if (position < size) {
                return (piece.getItemId(position));
            }

            position -= size;
        }

        return (-1);
    }

    public int getPositionForSection(int section) {
        int position = 0;

        for (ListAdapter piece : pieces) {
            if (piece instanceof SectionIndexer) {
                Object[] sections = ((SectionIndexer) piece).getSections();
                int numSections = 0;

                if (sections != null) {
                    numSections = sections.length;
                }

                if (section < numSections) {
                    return (position + ((SectionIndexer) piece)
                            .getPositionForSection(section));
                } else if (sections != null) {
                    section -= numSections;
                }
            }

            position += piece.getCount();
        }

        return (0);
    }

    public int getSectionForPosition(int position) {
        int section = 0;

        for (ListAdapter piece : pieces) {
            int size = piece.getCount();

            if (position < size) {
                if (piece instanceof SectionIndexer) {
                    return (section + ((SectionIndexer) piece)
                            .getSectionForPosition(position));
                }

                return (0);
            } else {
                if (piece instanceof SectionIndexer) {
                    Object[] sections = ((SectionIndexer) piece).getSections();

                    if (sections != null) {
                        section += sections.length;
                    }
                }
            }

            position -= size;
        }

        return (0);
    }

    public Object[] getSections() {
        ArrayList<Object> sections = new ArrayList<Object>();

        for (ListAdapter piece : pieces) {
            if (piece instanceof SectionIndexer) {
                Object[] curSections = ((SectionIndexer) piece).getSections();

                if (curSections != null) {
                    for (Object section : curSections) {
                        sections.add(section);
                    }
                }
            }
        }

        if (sections.size() == 0) {
            return (null);
        }

        return (sections.toArray(new Object[0]));
    }

    private class CascadeDataSetObserver extends DataSetObserver {
        @Override
        public void onChanged() {
            notifyDataSetChanged();
        }

        @Override
        public void onInvalidated() {
            notifyDataSetInvalidated();
        }
    }
}

You also will need ListTitleAdapter to put some titles before each adapter if you want.

  public class ListTitleAdapter extends BaseAdapter {

      Context context;
      String text;
      BaseAdapter parentAdapter;

      public ListTitleAdapter(Context c, String textToShow) {
          this(c, textToShow, null);
  }

      public ListTitleAdapter(Context c, String textToShow, BaseAdapter dependentAdapter) {
      super();
      context = c;
      text = textToShow;

      if(dependentAdapter != null){
          parentAdapter = dependentAdapter;
      }
  }

      public int getCount() {
      if(parentAdapter != null){
          if(parentAdapter.getCount() == 0){
              return 0;
          }
      }
      return 1;
  }

      public Object getItem(int position) {
      return position;
  }

      public long getItemId(int position) {
      return position;
  }

      public View getView(int position, View convertView, ViewGroup parent) {
      LinearLayout layout = new LinearLayout(context);
      TextView textView = new TextView(context);
      textView.setText(text);

      layout.addView(textView);

      return layout;
  }
  }

And here is small example on how to use these two classes.

MergeAdapter mergeAdapter = new MergeAdapter();

mergeAdapter.addAdapter(new ListTitleAdapter(context, "Title1", someAdapter1));
mergeAdapter.addAdapter(someAdapter1);

mergeAdapter.addAdapter(new ListTitleAdapter(context, "Title2", someAdapter2));
mergeAdapter.addAdapter(someAdapter2);

mergeAdapter.addAdapter(new ListTitleAdapter(context, "Title3", someAdapter3));
mergeAdapter.addAdapter(someAdapter3);

mergeAdapter.setNoItemsText("Nothing to display. This list is empty.");
((ListView)findViewById(R.id.list)).setAdapter(mergeAdapter);

Leave a Comment