A suite of adapters designed to provide alternatives to Android’s provided adapters. From standard ArrayAdapters
to RolodexAdapters
(sectioned adapters); They provide customizable filtering solutions which can be safely mutated during any filtering operation. Don’t need filtering? No problem, we have those specific kind as well.
All the adapters created were meant to fill the void where you need to build a custom adapter from scratch but don’t want to worry about the data management part. Below are some code examples with simple explanations of what they provide.
FILTERABLE ADAPTERS
ArrayAdapter
:contains()
, containsAll()
removeAll()
, retainAll()
update()
getFilteredList()
getList()
, setList()
getView()
and getDropDownView()
Slightly smarter internal notifyDataSetChanged()
invocations
public class MovieAdapter extends AbsArrayAdapter<MovieItem> {
MovieAdapter(Context activity) {
super(activity);
}
@Override
public View getView(LayoutInflater inflater, int position, View convertView, ViewGroup parent) {
if (convertView == null) {
//Inflate your view
}
//Fill your view with data
return convertView;
}
//Easily customize your filtered results here. Too easy!
@Override
protected boolean isFilteredOut(MovieItem movie, CharSequence constraint) {
return !movie.title.toLowerCase(Locale.US).contains(
constraint.toString().toLowerCase(Locale.US));
}
}
SparseArray
SparseArray
.getFilteredSparseArray()
getSparseArray()
, setSparseArray()
LayoutInflater Passed down to both getView()
and getDropDownView()
public class MovieAdapter extends SparseAdapter<MovieItem> {
MovieAdapter(Context activity) {
super(activity);
}
@Override
public View getView(LayoutInflater inflater, int position, View convertView, ViewGroup parent) {
if (convertView == null) {
//Inflate your view
}
//Fill your view with data
return convertView;
}
//Easily customize your filtered results here. Too easy!
@Override
protected boolean isFilteredOut(int keyId, MovieItem item, CharSequence constraint) {
return !item.title.toLowerCase(Locale.US).
contains(constraint.toString().toLowerCase(Locale.US));
}
}
JSONArray
JSONArray
.Boolean
, Double
, Integer
, Long
and String
data types. Can optionally override these with your own behavior.getFilteredJSONArray()
getJSONArray()
, setJSONArray()
LayoutInflater Passed down to both getView()
and getDropDownView()
public class MovieAdapter extends JSONAdapter<MovieItem> {
MovieAdapter(Context activity) {
super(activity);
}
@Override
public View getView(LayoutInflater inflater, int position, View convertView, ViewGroup parent) {
if (convertView == null) {
//Inflate your view
}
//Fill your view with data
return convertView;
}
//Easily customize your filtered results here. Too easy!
@Override
protected boolean isFilteredOut(Object item, CharSequence constraint) {
return !item.toString().toLowerCase(Locale.US).
contains(constraint.toString().toLowerCase(Locale.US));
}
}
ArrayList
. Data is organized in a Map
of ArrayLists
.ExpandableListView
features and provides additional conveniences. Learn more here.getFilteredList()
, getList()
, setList()
sortGroup()
, sortAllChildren()
collapseAll()
, expandAll()
, hasAutoExpandingGroups()
, isGroupSelectable()
LayoutInflater Passed down to both getView()
and getDropDownView()
class MovieAdapter extends RolodexArrayAdapter<Integer, MovieItem> {
MovieAdapter(Context activity) {
super(activity);
}
@Override
public Integer createGroupFor(MovieItem childItem) {
return childItem.year;
}
@Override
public View getChildView(LayoutInflater inflater, int groupPosition, int childPosition,
boolean isLastChild, View convertView, ViewGroup parent) {
if (convertView == null) {
//Inflate your view
}
//Fill your view with data
return convertView;
}
@Override
public View getGroupView(LayoutInflater inflater, int groupPosition, boolean isExpanded,
View convertView, ViewGroup parent) {
if (convertView == null) {
//Inflate your view
}
//Fill your view with data
return convertView;
}
@Override
protected boolean isChildFilteredOut(MovieItem movie, CharSequence constraint) {
//Make sure we aren't checking against a constraint containing a movie year, then filter by movie title
return !TextUtils.isDigitsOnly(constraint) && !movie.title.toLowerCase(Locale.US).contains(
constraint.toString().toLowerCase(Locale.US));
}
@Override
protected boolean isGroupFilteredOut(Integer year, CharSequence constraint) {
//Lets filter out everything whose year does not the numeric values in constraint.
return TextUtils.isDigitsOnly(constraint) && !year.toString().contains(constraint);
}
}
NON-FILTERABLE ADAPTERS
ArrayAdapter
:contains()
, containsAll()
removeAll()
, retainAll()
update()
, insertAll()
getList()
, setList()
getView()
and getDropDownView()
Slightly smarter internal notifyDataSetChanged()
invocations
public class MovieAdapter extends NFArrayAdapter<MovieItem> {
MovieAdapter(Context activity) {
super(activity);
}
@Override
public View getView(LayoutInflater inflater, int position, View convertView, ViewGroup parent) {
if (convertView == null) {
//Inflate your view
}
//Fill your view with data
return convertView;
}
}
SparseArray
SparseArray
.getSparseArray()
, setSparseArray()
LayoutInflater Passed down to both getView()
and getDropDownView()
public class MovieAdapter extends NFSparseAdapter<MovieItem> {
MovieAdapter(Context activity) {
super(activity);
}
@Override
public View getView(LayoutInflater inflater, int position, View convertView, ViewGroup parent) {
if (convertView == null) {
//Inflate your view
}
//Fill your view with data
return convertView;
}
}
JSONArray
JSONArray
.getJSONArray()
, setJSONArray()
LayoutInflater Passed down to both getView()
and getDropDownView()
public class MovieAdapter extends JSONAdapter<MovieItem> {
MovieAdapter(Context activity) {
super(activity);
}
@Override
public View getView(LayoutInflater inflater, int position, View convertView, ViewGroup parent) {
if (convertView == null) {
//Inflate your view
}
//Fill your view with data
return convertView;
}
}
ArrayList
. Data is organized in a Map
of ArrayLists
.ExpandableListView
features and provides additional conveniences. Learn more here.getList()
, setList()
sortGroup()
, sortAllChildren()
collapseAll()
, expandAll()
, hasAutoExpandingGroups()
, isGroupSelectable()
LayoutInflater Passed down to both getView()
and getDropDownView()
class MovieAdapter extends NFRolodexArrayAdapter<Integer, MovieItem> {
MovieAdapter(Context activity) {
super(activity);
}
@Override
public Integer createGroupFor(MovieItem childItem) {
return childItem.year;
}
@Override
public View getChildView(LayoutInflater inflater, int groupPosition, int childPosition,
boolean isLastChild, View convertView, ViewGroup parent) {
if (convertView == null) {
//Inflate your view
}
//Fill your view with data
return convertView;
}
@Override
public View getGroupView(LayoutInflater inflater, int groupPosition, boolean isExpanded,
View convertView, ViewGroup parent) {
if (convertView == null) {
//Inflate your view
}
//Fill your view with data
return convertView;
}
}
MISCELLANEOUS ADAPTERS
BaseExpandableListAdapter
ExpandableListView
ExpandableListView
to never collapse a group? Now possible.BaseExpandableLisAdapter
provides an isChildSelectable()
method but forgot about an isGroupSelectable()
. Now longer forgotten.collapseAll()
, expandAll()
hasAutoExpandingGroups()
, isGroupSelectable()
LayoutInflater Passed down to both getView()
and getDropDownView()
Already have a custom adapter extending BaseExpandableListAdapter
? No sweat. Just switch out with the PatchedExpandableListAdapter
and you’re done.
class MyCustomAdapter extends PatchedExpandableListAdapter {
//No further work required to get working. Just implement your custom adapter
//as normal. However there are a couple methods available to override for
//certain behavior changes.
//Override and return true to force all groups to always render expanded
@Override
public boolean hasAutoExpandingGroups() {
return true;
}
//Override and return true to toggle whether a group is clickable or not.
@Override
public boolean isGroupSelectable(int groupPosition) {
return true;
}
}
To enable and use choice mode, there are a few key things to remember. All interactions relating to choice mode must go through the adapter instead of the ExpandableListView
. Here are some code examples to get you started.
//If you need to set your own group or child click listeners, do so through the
//adapter.
mAdapter.setOnChildClickListener(new MyChildClickListener());
mAdapter.setOnGroupClickListener(new MyGroupClickListener());
//To enable choice mode, go through the adapter.
mAdapter.setChoiceMode(PatchedExpandableListAdapter.ChoiceMode.MULITPLE);
//If enabling one of the modal choice modes, don't forget to set the
//ChoiceModeListener
mAdapter.setMultiChoiceModeListener(new MyChoiceModeListener());
private class MyChoiceModeListener implements
PatchedExpandableListAdapter.ChoiceModeListener {
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
}
@Override
public void onChildCheckedStateChanged(ActionMode mode, int groupPosition, long groupId,
int childPosition, long childId, boolean checked) {
}
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
}
@Override
public void onDestroyActionMode(ActionMode mode) {
}
@Override
public void onGroupCheckedStateChanged(ActionMode mode, int groupPosition, long groupId,
boolean checked) {
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
}
}
When choide mode is turned on, if you need to access any of the data revolving around which items are selected and so forth, do so through the adapter. Those returned via the ExpandableListView
will not be valid.
//Example list of available functions
mAdapter.getCheckedChildCount();
mAdapter.getCheckedChildIds();
mAdapter.getCheckedChildPositions();
mAdapter.getCheckedGroupCount();
mAdapter.getCheckedGroupIds();
mAdapter.getCheckedGroupPositions();
There are a bunch more available methods when using choice mode. Just remember to work directly through the adapter instead of the ExpandableListView