Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save Rodolfocartas/919f1036fe6793ebd143 to your computer and use it in GitHub Desktop.
Save Rodolfocartas/919f1036fe6793ebd143 to your computer and use it in GitHub Desktop.
Composite adapter class for Android
public final class CompositeAdapter<T> extends BaseAdapter {
private final List<T> fHeadings;
private final List<ListAdapter> fAdapters;
private int[] fLookupTable;
private final DataSetObserver fChildObserver;
private final Object fLock;
private final int fHeadingResourceId;
private final int fHeadingTextViewResourceId;
private final LayoutInflater fInflater;
public CompositeAdapter( Context aContext, int aHeadingResourceId, int aHeadingTextViewResourceId ) {
fHeadings = new ArrayList<T>();
fAdapters = new ArrayList<ListAdapter>();
fLookupTable = new int[ 0 ];
fChildObserver = new ChildObserver();
fLock = new Object();
fHeadingResourceId = aHeadingResourceId;
fHeadingTextViewResourceId = aHeadingTextViewResourceId;
fInflater = ( LayoutInflater ) aContext.getSystemService( Context.LAYOUT_INFLATER_SERVICE );
}
public void add( ListAdapter aAdapter, T aHeading ) {
if ( aAdapter == null || aHeading == null ) {
throw new NullPointerException( "Arguments cannot be null." );
}
synchronized ( fLock ) {
if ( !fAdapters.contains( aAdapter ) ) {
aAdapter.registerDataSetObserver( fChildObserver );
}
fHeadings.add( aHeading );
fAdapters.add( aAdapter );
}
notifyDataSetChanged();
}
public void remove( T aHeading ) {
synchronized ( fLock ) {
int index = fHeadings.indexOf( aHeading );
removeElement( index );
}
notifyDataSetChanged();
}
public void remove( ListAdapter aAdapter ) {
synchronized ( fLock ) {
int index = fAdapters.indexOf( aAdapter );
removeElement( index );
}
notifyDataSetChanged();
}
private void removeElement( int aIndex ) {
if ( aIndex >= 0 ) {
fHeadings.remove( aIndex );
ListAdapter removedAdapter = fAdapters.remove( aIndex );
removedAdapter.unregisterDataSetObserver( fChildObserver );
}
}
/**
* {@inheritDoc}
*/
@Override
public int getCount() {
return 1 + fLookupTable[ fLookupTable.length - 1 ];
}
/**
* {@inheritDoc}
*/
@Override
public Object getItem( int aPosition ) {
int lookupIndex = getLookupIndex( aPosition );
if ( lookupIndex % 2 == 0 ) { // Header element.
return fHeadings.get( lookupIndex / 2 );
}
else { // Adapter element.
ListAdapter adapter = fAdapters.get( lookupIndex / 2 );
int relativePosition = aPosition - 1 - fLookupTable[ lookupIndex - 1 ];
return adapter.getItem( relativePosition );
}
}
private int getLookupIndex( int aPosition ) {
int lookupIndex = Arrays.binarySearch( fLookupTable, aPosition );
if ( lookupIndex < 0 ) {
lookupIndex = -1 * ( lookupIndex + 1 );
}
return lookupIndex;
}
/**
* {@inheritDoc}
*/
@Override
public long getItemId( int aPosition ) {
return aPosition;
}
/**
* {@inheritDoc}
*/
@Override
public View getView( int aPosition, View aConvertView, ViewGroup aViewGroup ) {
int lookupIndex = getLookupIndex( aPosition );
if ( lookupIndex % 2 == 0 ) {
return getHeaderView( lookupIndex, aConvertView, aViewGroup );
}
else {
return getElementView( aPosition, lookupIndex, aConvertView, aViewGroup );
}
}
private View getHeaderView( int aLookupIndex, View aConvertView, ViewGroup aViewGroup ) {
if ( aConvertView == null ) {
aConvertView = fInflater.inflate( fHeadingResourceId, aViewGroup, false );
}
String headingText = fHeadings.get( aLookupIndex / 2 ).toString();
TextView textView = ( TextView ) aConvertView.findViewById( fHeadingTextViewResourceId );
textView.setText( headingText );
return aConvertView;
}
private View getElementView( int aPosition, int aLookupIndex, View aConvertView, ViewGroup aViewGroup ) {
ListAdapter adapter = fAdapters.get( aLookupIndex / 2 );
int relativePosition = aPosition - 1 - fLookupTable[ aLookupIndex - 1 ];
return adapter.getView( relativePosition, aConvertView, aViewGroup );
}
/**
* {@inheritDoc}
*/
@Override
public int getItemViewType( int aPosition ) {
int lookupIndex = getLookupIndex( aPosition );
if ( lookupIndex % 2 == 0 ) { // Header element.
return 0;
}
else { // Adapter element.
ListAdapter adapter = fAdapters.get( lookupIndex / 2 );
int relativePosition = aPosition - 1 - fLookupTable[ lookupIndex - 1 ];
int relativeItemViewType = adapter.getItemViewType( relativePosition );
int cumulativePreviousViewTypes = 0;
for ( int i = 0; i <= lookupIndex / 2; i++ ) {
cumulativePreviousViewTypes += fAdapters.get( i ).getViewTypeCount();
}
return cumulativePreviousViewTypes + relativeItemViewType;
}
}
/**
* {@inheritDoc}
*/
@Override
public int getViewTypeCount() {
int nbViewTypes = 1/*Header.*/;
for ( ListAdapter adapter : fAdapters ) {
nbViewTypes += adapter.getViewTypeCount();
}
return nbViewTypes;
}
/**
* {@inheritDoc}
*/
@Override
public void notifyDataSetChanged() {
invalidateLookupTable();
super.notifyDataSetChanged();
}
private void invalidateLookupTable() {
synchronized ( fLock ) {
resetLookupTable();
fillLookupTable();
}
}
private void resetLookupTable() {
int nbHeadings = fHeadings.size();
if ( 2 * nbHeadings >= fLookupTable.length ) {
// Naive but it allows us to perform other methods much faster.
fLookupTable = new int[ 2 * nbHeadings ];
}
}
private void fillLookupTable() {
int lookupIndex = 0;
int elementsIndex = 0;
for ( Adapter adapter : fAdapters ) {
fLookupTable[ lookupIndex++ ] = elementsIndex; // Heading.
elementsIndex += adapter.getCount();
fLookupTable[ lookupIndex++ ] = elementsIndex++; // Adapter values.
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean areAllItemsEnabled() {
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isEnabled( int aPosition ) {
if ( aPosition < 0 || aPosition >= getCount() ) {
throw new ArrayIndexOutOfBoundsException( "Invalid position." );
}
int lookupIndex = getLookupIndex( aPosition );
if ( lookupIndex % 2 == 0 ) { // Header element.
return false;
}
else { // Adapter element.
ListAdapter adapter = fAdapters.get( lookupIndex / 2 );
int relativePosition = aPosition - 1 - fLookupTable[ lookupIndex - 1 ];
return adapter.isEnabled( relativePosition );
}
}
private final class ChildObserver extends DataSetObserver {
public ChildObserver() {
super();
}
/**
* {@inheritDoc}
*/
@Override
public void onChanged() {
synchronized ( fLock ) {
notifyDataSetChanged();
}
}
/**
* {@inheritDoc}
*/
@Override
public void onInvalidated() {
synchronized ( fLock ) {
notifyDataSetChanged();
}
}
}
}
ArrayAdapter<Object> adapter1 = new ArrayAdapter<Object>( this, R.layout.list_item, R.id.name );
adapter1.add( ... )
ArrayAdapter<Object> adapter2 = new ArrayAdapter<Object>( this, R.layout.list_item, R.id.name );
adapter2.add( ... )
CompositeAdapter<Object> compositeAdapter = new CompositeAdapter<Object>( this, R.layout.header_list_item, R.id.title );
compositeAdapter.add( adapter1, "Section 1" );
compositeAdapter.add( adapter2, "Section 2" );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment