Skip to content

Instantly share code, notes, and snippets.

@JayNewstrom
Last active January 4, 2016 18:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JayNewstrom/8658227 to your computer and use it in GitHub Desktop.
Save JayNewstrom/8658227 to your computer and use it in GitHub Desktop.
An adapter to use in a listview with separate views for header views and content views. with full reuse of cells
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Created by jaynewstrom on 9/26/13.
* Updated by jaynewstrom on 1/31/14
*
* {@code S} is the section type
* {@code C} is the content type
* {@code SV} is the section view type
* {@code CV} is the content view type
*/
public abstract class HeaderAdapter<S, C, SV extends View, CV extends View> extends BaseAdapter {
private final Context context;
private final LayoutInflater layoutInflater;
private final Map<S, List<C>> originalData;
private final List<C> originalContentList;
private List<Object> filteredData;
private List<C> filteredContentList;
private int filteredCount; // this includes headers and content
private static final Object sectionViewTag = new Object();
private static final Object contentViewTag = new Object();
public HeaderAdapter(Context context, Map<S, List<C>> map) {
this.context = context;
this.layoutInflater = LayoutInflater.from(context);
this.originalData = new LinkedHashMap<S, List<C>>(map); // LinkedHashMap preserves ordering of key set.
this.computeDataList(this.originalData, null);
this.originalContentList = new ArrayList<C>();
for (S s : map.keySet()) {
for (C c : map.get(s)) {
originalContentList.add(c);
}
}
}
protected abstract SV buildSectionView(LayoutInflater layoutInflater);
protected abstract SV bindSectionView(S section, SV view);
protected abstract CV buildContentView(LayoutInflater layoutInflater);
protected abstract CV bindContentView(C content, CV view);
protected Context getContext() {
return this.context;
}
protected LayoutInflater getLayoutInflater() {
return this.layoutInflater;
}
protected int getOriginalContentCount() {
return this.originalContentList.size();
}
protected List<C> getFilteredContentList() {
return this.filteredContentList;
}
@SuppressWarnings("unchecked")
public S getSectionForIndex(int index) {
Object item = this.getItem(index);
if (item == null) {
return null;
}
if (SectionDataHolder.class.isInstance(item)) {
return ((SectionDataHolder) item).section;
} else if (ContentDataHolder.class.isInstance(item)) {
return this.getSectionForContent(((ContentDataHolder) item).content);
}
return null;
}
private S getSectionForContent(C content) {
for (S s : this.originalData.keySet()) {
if (this.originalData.get(s).contains(content)) {
return s;
}
}
return null;
}
public void filterResults(String filterText) {
this.computeDataList(this.originalData, filterText);
this.notifyDataSetChanged();
}
@Override
public int getCount() {
return this.filteredCount;
}
@Override
public Object getItem(int i) {
if (this.getCount() > i) {
return this.filteredData.get(i);
} else {
return null;
}
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public int getViewTypeCount() {
return 2;
}
@Override
public int getItemViewType(int position) {
Object item = this.getItem(position);
if (SectionDataHolder.class.isInstance(item)) {
return 0;
} else if (ContentDataHolder.class.isInstance(item)) {
return 1;
}
return super.getItemViewType(position);
}
@Override @SuppressWarnings("unchecked")
public View getView(int i, View view, ViewGroup viewGroup) {
Object item = this.getItem(i);
if (SectionDataHolder.class.isInstance(item)) {
SectionDataHolder sdh = (SectionDataHolder) item;
SV sv;
if (view != null && view.getTag() == sectionViewTag) {
sv = (SV) view;
} else {
sv = this.buildSectionView(this.layoutInflater);
sv.setTag(sectionViewTag);
}
view = this.bindSectionView(sdh.section, sv);
} else if (ContentDataHolder.class.isInstance(item)) {
ContentDataHolder cdh = (ContentDataHolder) item;
CV cv;
if (view != null && view.getTag() == contentViewTag) {
cv = (CV) view;
} else {
cv = this.buildContentView(this.layoutInflater);
cv.setTag(contentViewTag);
}
view = this.bindContentView(cdh.content, cv);
} else {
// uhhh, we're in deep trouble.
}
return view;
}
private void computeDataList(Map<S, List<C>> map, String filterText) {
boolean hasFilterText = filterText != null && !filterText.equals("");
this.filteredData = new ArrayList<Object>();
this.filteredContentList = new ArrayList<C>();
for (S section : map.keySet()) {
List<C> listOfCs = map.get(section);
if (listOfCs.size() > 0) {
List<ContentDataHolder> contentToAdd = new ArrayList<ContentDataHolder>();
for (C content : listOfCs) {
if (!hasFilterText) {
this.filteredContentList.add(content);
contentToAdd.add(new ContentDataHolder(content));
} else {
if (content.toString().toLowerCase().contains(filterText.toLowerCase())) {
this.filteredContentList.add(content);
contentToAdd.add(new ContentDataHolder(content));
}
}
}
if (contentToAdd.size() > 0) {
this.filteredData.add(new SectionDataHolder(section));
this.filteredData.addAll(contentToAdd);
}
}
}
this.filteredCount = this.filteredData.size();
}
private class SectionDataHolder {
private final S section;
SectionDataHolder(S section) {
this.section = section;
}
}
private class ContentDataHolder {
private final C content;
ContentDataHolder(C content) {
this.content = content;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment