Last active
January 4, 2016 18:08
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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