Eric Kok
12 years ago
15 changed files with 518 additions and 738 deletions
@ -1,481 +0,0 @@ |
|||||||
/*** |
|
||||||
Copyright (c) 2008-2009 CommonsWare, LLC |
|
||||||
Portions (c) 2009 Google, Inc. |
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License"); you may |
|
||||||
not use this file except in compliance with the License. You may obtain |
|
||||||
a copy of the License at |
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
Unless required by applicable law or agreed to in writing, software |
|
||||||
distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
See the License for the specific language governing permissions and |
|
||||||
limitations under the License. |
|
||||||
*/ |
|
||||||
|
|
||||||
package com.commonsware.cwac.merge; |
|
||||||
|
|
||||||
import android.database.DataSetObserver; |
|
||||||
import android.view.View; |
|
||||||
import android.view.ViewGroup; |
|
||||||
import android.widget.BaseAdapter; |
|
||||||
import android.widget.ListAdapter; |
|
||||||
import android.widget.SectionIndexer; |
|
||||||
import java.util.ArrayList; |
|
||||||
import java.util.List; |
|
||||||
import com.commonsware.cwac.sacklist.SackOfViewsAdapter; |
|
||||||
|
|
||||||
/** |
|
||||||
* 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 PieceStateRoster pieces=new PieceStateRoster(); |
|
||||||
|
|
||||||
/** |
|
||||||
* 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()); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Adds a new View to the roster of things to appear in |
|
||||||
* the aggregate list. |
|
||||||
* |
|
||||||
* @param view |
|
||||||
* Single view to add |
|
||||||
*/ |
|
||||||
public void addView(View view) { |
|
||||||
addView(view, false); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Adds a new View to the roster of things to appear in |
|
||||||
* the aggregate list. |
|
||||||
* |
|
||||||
* @param view |
|
||||||
* Single view to add |
|
||||||
* @param enabled |
|
||||||
* false if views are disabled, true if enabled |
|
||||||
*/ |
|
||||||
public void addView(View view, boolean enabled) { |
|
||||||
ArrayList<View> list=new ArrayList<View>(1); |
|
||||||
|
|
||||||
list.add(view); |
|
||||||
|
|
||||||
addViews(list, enabled); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Adds a list of views to the roster of things to appear |
|
||||||
* in the aggregate list. |
|
||||||
* |
|
||||||
* @param views |
|
||||||
* List of views to add |
|
||||||
*/ |
|
||||||
public void addViews(List<View> views) { |
|
||||||
addViews(views, false); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Adds a list of views to the roster of things to appear |
|
||||||
* in the aggregate list. |
|
||||||
* |
|
||||||
* @param views |
|
||||||
* List of views to add |
|
||||||
* @param enabled |
|
||||||
* false if views are disabled, true if enabled |
|
||||||
*/ |
|
||||||
public void addViews(List<View> views, boolean enabled) { |
|
||||||
if (enabled) { |
|
||||||
addAdapter(new EnabledSackAdapter(views)); |
|
||||||
} |
|
||||||
else { |
|
||||||
addAdapter(new SackOfViewsAdapter(views)); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get the data item associated with the specified |
|
||||||
* position in the data set. |
|
||||||
* |
|
||||||
* @param position |
|
||||||
* Position of the item whose data we want |
|
||||||
*/ |
|
||||||
@Override |
|
||||||
public Object getItem(int position) { |
|
||||||
for (ListAdapter piece : getPieces()) { |
|
||||||
int size=piece.getCount(); |
|
||||||
|
|
||||||
if (position < size) { |
|
||||||
return(piece.getItem(position)); |
|
||||||
} |
|
||||||
|
|
||||||
position-=size; |
|
||||||
} |
|
||||||
|
|
||||||
return(null); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 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 : getPieces()) { |
|
||||||
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. |
|
||||||
*/ |
|
||||||
@Override |
|
||||||
public int getCount() { |
|
||||||
int total=0; |
|
||||||
|
|
||||||
for (ListAdapter piece : getPieces()) { |
|
||||||
total+=piece.getCount(); |
|
||||||
} |
|
||||||
|
|
||||||
return(total); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Returns the number of types of Views that will be |
|
||||||
* created by getView(). |
|
||||||
*/ |
|
||||||
@Override |
|
||||||
public int getViewTypeCount() { |
|
||||||
int total=0; |
|
||||||
|
|
||||||
for (PieceState piece : pieces.getRawPieces()) { |
|
||||||
total+=piece.adapter.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 (PieceState piece : pieces.getRawPieces()) { |
|
||||||
if (piece.isActive) { |
|
||||||
int size=piece.adapter.getCount(); |
|
||||||
|
|
||||||
if (position < size) { |
|
||||||
result=typeOffset + piece.adapter.getItemViewType(position); |
|
||||||
break; |
|
||||||
} |
|
||||||
|
|
||||||
position-=size; |
|
||||||
} |
|
||||||
|
|
||||||
typeOffset+=piece.adapter.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 : getPieces()) { |
|
||||||
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 |
|
||||||
*/ |
|
||||||
@Override |
|
||||||
public View getView(int position, View convertView, ViewGroup parent) { |
|
||||||
for (ListAdapter piece : getPieces()) { |
|
||||||
int size=piece.getCount(); |
|
||||||
|
|
||||||
if (position < size) { |
|
||||||
|
|
||||||
return(piece.getView(position, convertView, parent)); |
|
||||||
} |
|
||||||
|
|
||||||
position-=size; |
|
||||||
} |
|
||||||
|
|
||||||
return(null); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get the row id associated with the specified position |
|
||||||
* in the list. |
|
||||||
* |
|
||||||
* @param position |
|
||||||
* Position of the item whose data we want |
|
||||||
*/ |
|
||||||
@Override |
|
||||||
public long getItemId(int position) { |
|
||||||
for (ListAdapter piece : getPieces()) { |
|
||||||
int size=piece.getCount(); |
|
||||||
|
|
||||||
if (position < size) { |
|
||||||
return(piece.getItemId(position)); |
|
||||||
} |
|
||||||
|
|
||||||
position-=size; |
|
||||||
} |
|
||||||
|
|
||||||
return(-1); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public int getPositionForSection(int section) { |
|
||||||
int position=0; |
|
||||||
|
|
||||||
for (ListAdapter piece : getPieces()) { |
|
||||||
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); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public int getSectionForPosition(int position) { |
|
||||||
int section=0; |
|
||||||
|
|
||||||
for (ListAdapter piece : getPieces()) { |
|
||||||
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); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public Object[] getSections() { |
|
||||||
ArrayList<Object> sections=new ArrayList<Object>(); |
|
||||||
|
|
||||||
for (ListAdapter piece : getPieces()) { |
|
||||||
if (piece instanceof SectionIndexer) { |
|
||||||
Object[] curSections=((SectionIndexer)piece).getSections(); |
|
||||||
|
|
||||||
if (curSections != null) { |
|
||||||
for (Object section : curSections) { |
|
||||||
sections.add(section); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if (sections.size() == 0) { |
|
||||||
return(new String[0]); |
|
||||||
} |
|
||||||
|
|
||||||
return(sections.toArray(new Object[0])); |
|
||||||
} |
|
||||||
|
|
||||||
public void setActive(ListAdapter adapter, boolean isActive) { |
|
||||||
pieces.setActive(adapter, isActive); |
|
||||||
notifyDataSetChanged(); |
|
||||||
} |
|
||||||
|
|
||||||
public void setActive(View v, boolean isActive) { |
|
||||||
pieces.setActive(v, isActive); |
|
||||||
notifyDataSetChanged(); |
|
||||||
} |
|
||||||
|
|
||||||
protected List<ListAdapter> getPieces() { |
|
||||||
return(pieces.getPieces()); |
|
||||||
} |
|
||||||
|
|
||||||
private static class PieceState { |
|
||||||
ListAdapter adapter; |
|
||||||
boolean isActive=true; |
|
||||||
|
|
||||||
PieceState(ListAdapter adapter, boolean isActive) { |
|
||||||
this.adapter=adapter; |
|
||||||
this.isActive=isActive; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private static class PieceStateRoster { |
|
||||||
protected ArrayList<PieceState> pieces=new ArrayList<PieceState>(); |
|
||||||
protected ArrayList<ListAdapter> active=null; |
|
||||||
|
|
||||||
void add(ListAdapter adapter) { |
|
||||||
pieces.add(new PieceState(adapter, true)); |
|
||||||
} |
|
||||||
|
|
||||||
void setActive(ListAdapter adapter, boolean isActive) { |
|
||||||
for (PieceState state : pieces) { |
|
||||||
if (state.adapter==adapter) { |
|
||||||
state.isActive=isActive; |
|
||||||
active=null; |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void setActive(View v, boolean isActive) { |
|
||||||
for (PieceState state : pieces) { |
|
||||||
if (state.adapter instanceof SackOfViewsAdapter && |
|
||||||
((SackOfViewsAdapter)state.adapter).hasView(v)) { |
|
||||||
state.isActive=isActive; |
|
||||||
active=null; |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
List<PieceState> getRawPieces() { |
|
||||||
return(pieces); |
|
||||||
} |
|
||||||
|
|
||||||
List<ListAdapter> getPieces() { |
|
||||||
if (active == null) { |
|
||||||
active=new ArrayList<ListAdapter>(); |
|
||||||
|
|
||||||
for (PieceState state : pieces) { |
|
||||||
if (state.isActive) { |
|
||||||
active.add(state.adapter); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return(active); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private static class EnabledSackAdapter extends SackOfViewsAdapter { |
|
||||||
public EnabledSackAdapter(List<View> views) { |
|
||||||
super(views); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public boolean areAllItemsEnabled() { |
|
||||||
return(true); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public boolean isEnabled(int position) { |
|
||||||
return(true); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private class CascadeDataSetObserver extends DataSetObserver { |
|
||||||
@Override |
|
||||||
public void onChanged() { |
|
||||||
notifyDataSetChanged(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void onInvalidated() { |
|
||||||
notifyDataSetInvalidated(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,177 +0,0 @@ |
|||||||
/*** |
|
||||||
Copyright (c) 2008-2009 CommonsWare, LLC |
|
||||||
Portions (c) 2009 Google, Inc. |
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License"); you may |
|
||||||
not use this file except in compliance with the License. You may obtain |
|
||||||
a copy of the License at |
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
Unless required by applicable law or agreed to in writing, software |
|
||||||
distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
See the License for the specific language governing permissions and |
|
||||||
limitations under the License. |
|
||||||
*/ |
|
||||||
|
|
||||||
package com.commonsware.cwac.sacklist; |
|
||||||
|
|
||||||
import java.util.ArrayList; |
|
||||||
import java.util.List; |
|
||||||
import android.view.View; |
|
||||||
import android.view.ViewGroup; |
|
||||||
import android.widget.BaseAdapter; |
|
||||||
|
|
||||||
/** |
|
||||||
* Adapter that simply returns row views from a list. |
|
||||||
* |
|
||||||
* If you supply a size, you must implement newView(), to |
|
||||||
* create a required view. The adapter will then cache these |
|
||||||
* views. |
|
||||||
* |
|
||||||
* If you supply a list of views in the constructor, that |
|
||||||
* list will be used directly. If any elements in the list |
|
||||||
* are null, then newView() will be called just for those |
|
||||||
* slots. |
|
||||||
* |
|
||||||
* Subclasses may also wish to override areAllItemsEnabled() |
|
||||||
* (default: false) and isEnabled() (default: false), if some |
|
||||||
* of their rows should be selectable. |
|
||||||
* |
|
||||||
* It is assumed each view is unique, and therefore will not |
|
||||||
* get recycled. |
|
||||||
* |
|
||||||
* Note that this adapter is not designed for long lists. It |
|
||||||
* is more for screens that should behave like a list. This |
|
||||||
* is particularly useful if you combine this with other |
|
||||||
* adapters (e.g., SectionedAdapter) that might have an |
|
||||||
* arbitrary number of rows, so it all appears seamless. |
|
||||||
*/ |
|
||||||
public class SackOfViewsAdapter extends BaseAdapter { |
|
||||||
private List<View> views=null; |
|
||||||
|
|
||||||
/** |
|
||||||
* Constructor creating an empty list of views, but with |
|
||||||
* a specified count. Subclasses must override newView(). |
|
||||||
*/ |
|
||||||
public SackOfViewsAdapter(int count) { |
|
||||||
super(); |
|
||||||
|
|
||||||
views=new ArrayList<View>(count); |
|
||||||
|
|
||||||
for (int i=0;i<count;i++) { |
|
||||||
views.add(null); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Constructor wrapping a supplied list of views. |
|
||||||
* Subclasses must override newView() if any of the elements |
|
||||||
* in the list are null. |
|
||||||
*/ |
|
||||||
public SackOfViewsAdapter(List<View> views) { |
|
||||||
super(); |
|
||||||
|
|
||||||
this.views=views; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get the data item associated with the specified |
|
||||||
* position in the data set. |
|
||||||
* @param position Position of the item whose data we want |
|
||||||
*/ |
|
||||||
@Override |
|
||||||
public Object getItem(int position) { |
|
||||||
return(views.get(position)); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* How many items are in the data set represented by this |
|
||||||
* Adapter. |
|
||||||
*/ |
|
||||||
@Override |
|
||||||
public int getCount() { |
|
||||||
return(views.size()); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Returns the number of types of Views that will be |
|
||||||
* created by getView(). |
|
||||||
*/ |
|
||||||
@Override |
|
||||||
public int getViewTypeCount() { |
|
||||||
return(getCount()); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 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) { |
|
||||||
return(position); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 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) { |
|
||||||
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 |
|
||||||
*/ |
|
||||||
@Override |
|
||||||
public View getView(int position, View convertView, |
|
||||||
ViewGroup parent) { |
|
||||||
View result=views.get(position); |
|
||||||
|
|
||||||
if (result==null) { |
|
||||||
result=newView(position, parent); |
|
||||||
views.set(position, result); |
|
||||||
} |
|
||||||
|
|
||||||
return(result); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Get the row id associated with the specified position |
|
||||||
* in the list. |
|
||||||
* @param position Position of the item whose data we want |
|
||||||
*/ |
|
||||||
@Override |
|
||||||
public long getItemId(int position) { |
|
||||||
return(position); |
|
||||||
} |
|
||||||
|
|
||||||
public boolean hasView(View v) { |
|
||||||
return(views.contains(v)); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Create a new View to go into the list at the specified |
|
||||||
* position. |
|
||||||
* @param position Position of the item whose data we want |
|
||||||
* @param parent ViewGroup containing the returned View |
|
||||||
*/ |
|
||||||
protected View newView(int position, ViewGroup parent) { |
|
||||||
throw new RuntimeException("You must override newView()!"); |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,298 @@ |
|||||||
|
package org.transdroid.core.gui.lists; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
|
||||||
|
import android.database.DataSetObserver; |
||||||
|
import android.view.View; |
||||||
|
import android.view.ViewGroup; |
||||||
|
import android.widget.Adapter; |
||||||
|
import android.widget.BaseAdapter; |
||||||
|
import android.widget.ListAdapter; |
||||||
|
import android.widget.SectionIndexer; |
||||||
|
import android.widget.TextView; |
||||||
|
|
||||||
|
/** |
||||||
|
* An adapter that can contain many other adapters and shows them in sequence. Taken from |
||||||
|
* http://stackoverflow.com/questions/7964259/android-attaching-multiple-adapters-to-one-adapter and based on the Apache
|
||||||
|
* 2-licensed CommonsWare MergeAdapter. |
||||||
|
* @author Eric Kok |
||||||
|
* @author Alex Amiryan |
||||||
|
* @author Mark Murphy |
||||||
|
*/ |
||||||
|
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 {@link 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 {@link View}s that will be created by {@link #getView(int, View, ViewGroup)}. |
||||||
|
*/ |
||||||
|
@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 {@link View} that will be created by {@link #getView(int, View, ViewGroup)} 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 {@link 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 {@link 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 final 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 final 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 final 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(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,107 @@ |
|||||||
|
package org.transdroid.core.gui.lists; |
||||||
|
|
||||||
|
import android.view.View; |
||||||
|
import android.view.ViewGroup; |
||||||
|
import android.widget.BaseAdapter; |
||||||
|
import android.widget.ListView; |
||||||
|
|
||||||
|
/** |
||||||
|
* A simple wrapper adapter around a single view, typically for use in a {@link MergeAdapter}. Notably, this adapter |
||||||
|
* handles the proper showing or hiding of the view according to the contained view's visibility if |
||||||
|
* {@link #setViewVisibility(int)} is used on this adapter rather than setting the visibility of the view directly on |
||||||
|
* the view object. This is required since otherwise the adapter's consumer (i.e. a {@link ListView}) does not update |
||||||
|
* the list row accordingly. Use {@link #setViewEnabled(boolean)} to enable or disable this contained view for user |
||||||
|
* interaction. |
||||||
|
* @author Eric Kok |
||||||
|
*/ |
||||||
|
public class ViewHolderAdapter extends BaseAdapter { |
||||||
|
|
||||||
|
private final View view; |
||||||
|
|
||||||
|
/** |
||||||
|
* Instantiates this wrapper adapter with the one and only view to show. It can not be updated and view visibility |
||||||
|
* should be set directly on this adapter using {@link #setViewVisibility(int)}. Use |
||||||
|
* {@link #setViewEnabled(boolean)} to enable or disable this contained view for user interaction. |
||||||
|
* @param view The view that will be wrapper in an adapter to show in a list view |
||||||
|
*/ |
||||||
|
public ViewHolderAdapter(View view) { |
||||||
|
this.view = view; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the visibility on the contained view and notifies consumers of this adapter (i.e. a {@link ListView}) |
||||||
|
* accordingly. Use {@link View#GONE} to hide this adapter's view altogether. |
||||||
|
* @param visibility The visibility to set on the contained view |
||||||
|
*/ |
||||||
|
public void setViewVisibility(int visibility) { |
||||||
|
view.setVisibility(visibility); |
||||||
|
notifyDataSetChanged(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets whether the contained view should be enabled and notifies consumers of this adapter (i.e. a {@link ListView} |
||||||
|
* ) accordingly. A contained enabled view allows user interaction (clicks, focus), while a disabled view does not. |
||||||
|
* @param enabled Whether the contained view should be enabled |
||||||
|
*/ |
||||||
|
public void setViewEnabled(boolean enabled) { |
||||||
|
view.setEnabled(enabled); |
||||||
|
notifyDataSetChanged(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns 1 if the contained view is {@link View#VISIBLE} or {@link View#INVISIBLE}, return 0 if {@link View#GONE}. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public int getCount() { |
||||||
|
return view.getVisibility() == View.VISIBLE ? 1 : 0; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Always directly returns the single contained view instance. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public Object getItem(int position) { |
||||||
|
return view; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Always returns the position directly as item id. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public long getItemId(int position) { |
||||||
|
return position; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Always directly returns the single contained view instance. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public View getView(int position, View convertView, ViewGroup parent) { |
||||||
|
return view; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Always returns true, as there is only one contained item and it is never changed. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public boolean hasStableIds() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns false, as the contained view can still be enabled and disabled. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public boolean areAllItemsEnabled() { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns true if the contained view is enabled, returns false otherwise. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public boolean isEnabled(int position) { |
||||||
|
return view.isEnabled(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
Loading…
Reference in new issue