-
-
Save Rodolfocartas/919f1036fe6793ebd143 to your computer and use it in GitHub Desktop.
Composite adapter class for Android
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
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(); | |
} | |
} | |
} | |
} |
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
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