forked from emilsjolander/StickyListHeaders
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add ExpandableStickyListHeaderListView which enable listview to expan…
…d/collapse sub items
- Loading branch information
Showing
4 changed files
with
445 additions
and
0 deletions.
There are no files selected for viewing
147 changes: 147 additions & 0 deletions
147
library/src/se/emilsjolander/stickylistheaders/DistinctMultiHashMap.java
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
package se.emilsjolander.stickylistheaders; | ||
|
||
import java.util.ArrayList; | ||
import java.util.LinkedHashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Set; | ||
|
||
/** | ||
* a hash map can maintain an one-to-many relationship which the value only belongs to one “one” part | ||
* and the map also support getKey by value quickly | ||
* | ||
* @author lsjwzh | ||
*/ | ||
public class DistinctMultiHashMap<TKey,TItemValue> { | ||
private IDMapper<TKey, TItemValue> mIDMapper; | ||
|
||
public interface IDMapper<TKey,TItemValue>{ | ||
public Object keyToKeyId(TKey key); | ||
public TKey keyIdToKey(Object keyId); | ||
public Object valueToValueId(TItemValue value); | ||
public TItemValue valueIdToValue(Object valueId); | ||
} | ||
|
||
LinkedHashMap<Object,List<TItemValue>> mKeyToValuesMap = new LinkedHashMap<Object, List<TItemValue>>(); | ||
LinkedHashMap<Object,TKey> mValueToKeyIndexer = new LinkedHashMap<Object, TKey>(); | ||
|
||
public DistinctMultiHashMap(){ | ||
this(new IDMapper<TKey, TItemValue>() { | ||
@Override | ||
public Object keyToKeyId(TKey key) { | ||
return key; | ||
} | ||
|
||
@Override | ||
public TKey keyIdToKey(Object keyId) { | ||
return (TKey) keyId; | ||
} | ||
|
||
@Override | ||
public Object valueToValueId(TItemValue value) { | ||
return value; | ||
} | ||
|
||
@Override | ||
public TItemValue valueIdToValue(Object valueId) { | ||
return (TItemValue) valueId; | ||
} | ||
}); | ||
} | ||
public DistinctMultiHashMap(IDMapper<TKey, TItemValue> idMapper){ | ||
mIDMapper = idMapper; | ||
} | ||
|
||
public List<TItemValue> get(TKey key){ | ||
//todo immutable | ||
return mKeyToValuesMap.get(mIDMapper.keyToKeyId(key)); | ||
} | ||
public TKey getKey(TItemValue value){ | ||
return mValueToKeyIndexer.get(mIDMapper.valueToValueId(value)); | ||
} | ||
|
||
public void add(TKey key,TItemValue value){ | ||
Object keyId = mIDMapper.keyToKeyId(key); | ||
if(mKeyToValuesMap.get(keyId)==null){ | ||
mKeyToValuesMap.put(keyId,new ArrayList<TItemValue>()); | ||
} | ||
//remove old relationship | ||
TKey keyForValue = getKey(value); | ||
if(keyForValue !=null){ | ||
mKeyToValuesMap.get(mIDMapper.keyToKeyId(keyForValue)).remove(value); | ||
} | ||
mValueToKeyIndexer.put(mIDMapper.valueToValueId(value), key); | ||
if(!containsValue(mKeyToValuesMap.get(mIDMapper.keyToKeyId(key)),value)) { | ||
mKeyToValuesMap.get(mIDMapper.keyToKeyId(key)).add(value); | ||
} | ||
} | ||
|
||
public void removeKey(TKey key){ | ||
if(mKeyToValuesMap.get(mIDMapper.keyToKeyId(key))!=null){ | ||
for (TItemValue value : mKeyToValuesMap.get(mIDMapper.keyToKeyId(key))){ | ||
mValueToKeyIndexer.remove(mIDMapper.valueToValueId(value)); | ||
} | ||
mKeyToValuesMap.remove(mIDMapper.keyToKeyId(key)); | ||
} | ||
} | ||
public void removeValue(TItemValue value){ | ||
if(getKey(value)!=null){ | ||
List<TItemValue> itemValues = mKeyToValuesMap.get(mIDMapper.keyToKeyId(getKey(value))); | ||
if(itemValues!=null){ | ||
itemValues.remove(value); | ||
} | ||
} | ||
mValueToKeyIndexer.remove(mIDMapper.valueToValueId(value)); | ||
} | ||
|
||
public void clear(){ | ||
mValueToKeyIndexer.clear(); | ||
mKeyToValuesMap.clear(); | ||
} | ||
|
||
public void clearValues(){ | ||
for (Map.Entry<Object,List<TItemValue>> entry:entrySet()){ | ||
if(entry.getValue()!=null){ | ||
entry.getValue().clear(); | ||
} | ||
} | ||
mValueToKeyIndexer.clear(); | ||
} | ||
|
||
public Set<Map.Entry<Object,List<TItemValue>>> entrySet(){ | ||
return mKeyToValuesMap.entrySet(); | ||
} | ||
|
||
public Set<Map.Entry<Object,TKey>> reverseEntrySet(){ | ||
return mValueToKeyIndexer.entrySet(); | ||
} | ||
|
||
public int size(){ | ||
return mKeyToValuesMap.size(); | ||
} | ||
public int valuesSize(){ | ||
return mValueToKeyIndexer.size(); | ||
} | ||
|
||
protected boolean containsValue(List<TItemValue> list,TItemValue value){ | ||
for (TItemValue itemValue :list){ | ||
if(mIDMapper.valueToValueId(itemValue).equals(mIDMapper.valueToValueId(value))){ | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
/** | ||
* @param position | ||
* @return | ||
*/ | ||
public TItemValue getValueByPosition(int position){ | ||
Object[] vauleIdArray = mValueToKeyIndexer.keySet().toArray(); | ||
if(position>vauleIdArray.length){ | ||
throw new IndexOutOfBoundsException(); | ||
} | ||
Object valueId = vauleIdArray[position]; | ||
return mIDMapper.valueIdToValue(valueId); | ||
} | ||
} |
39 changes: 39 additions & 0 deletions
39
library/src/se/emilsjolander/stickylistheaders/DualHashMap.java
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package se.emilsjolander.stickylistheaders; | ||
|
||
import java.util.HashMap; | ||
|
||
/** | ||
* simple two way hashmap | ||
* @author lsjwzh | ||
*/ | ||
public class DualHashMap<TKey, TValue> { | ||
HashMap<TKey, TValue> mKeyToValue = new HashMap<TKey, TValue>(); | ||
HashMap<TValue, TKey> mValueToKey = new HashMap<TValue, TKey>(); | ||
|
||
public void put(TKey t1, TValue t2){ | ||
remove(t1); | ||
removeByValue(t2); | ||
mKeyToValue.put(t1, t2); | ||
mValueToKey.put(t2, t1); | ||
} | ||
|
||
public TKey getKey(TValue value){ | ||
return mValueToKey.get(value); | ||
} | ||
public TValue get(TKey key){ | ||
return mKeyToValue.get(key); | ||
} | ||
|
||
public void remove(TKey key){ | ||
if(get(key)!=null){ | ||
mValueToKey.remove(get(key)); | ||
} | ||
mKeyToValue.remove(key); | ||
} | ||
public void removeByValue(TValue value){ | ||
if(getKey(value)!=null){ | ||
mKeyToValue.remove(getKey(value)); | ||
} | ||
mValueToKey.remove(value); | ||
} | ||
} |
131 changes: 131 additions & 0 deletions
131
library/src/se/emilsjolander/stickylistheaders/ExpandableStickyListHeadersAdapter.java
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
package se.emilsjolander.stickylistheaders; | ||
|
||
import android.database.DataSetObserver; | ||
import android.view.View; | ||
import android.view.ViewGroup; | ||
import android.widget.BaseAdapter; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
|
||
/** | ||
* @author lsjwzh | ||
*/ | ||
public class ExpandableStickyListHeadersAdapter extends BaseAdapter implements StickyListHeadersAdapter { | ||
|
||
private final StickyListHeadersAdapter mInnerAdapter; | ||
DualHashMap<View,Long> mViewToItemMap = new DualHashMap<View, Long>(); | ||
DistinctMultiHashMap<Integer,View> mGroupIdToViewMap = new DistinctMultiHashMap<Integer, View>(); | ||
List<Long> mCollapseGroupIds = new ArrayList<Long>(); | ||
|
||
public ExpandableStickyListHeadersAdapter(StickyListHeadersAdapter innerAdapter){ | ||
this.mInnerAdapter = innerAdapter; | ||
} | ||
|
||
@Override | ||
public View getHeaderView(int position, View convertView, ViewGroup parent) { | ||
return mInnerAdapter.getHeaderView(position,convertView,parent); | ||
} | ||
|
||
@Override | ||
public long getHeaderId(int position) { | ||
return mInnerAdapter.getHeaderId(position); | ||
} | ||
|
||
@Override | ||
public boolean areAllItemsEnabled() { | ||
return mInnerAdapter.areAllItemsEnabled(); | ||
} | ||
|
||
@Override | ||
public boolean isEnabled(int i) { | ||
return mInnerAdapter.isEnabled(i); | ||
} | ||
|
||
@Override | ||
public void registerDataSetObserver(DataSetObserver dataSetObserver) { | ||
mInnerAdapter.registerDataSetObserver(dataSetObserver); | ||
} | ||
|
||
@Override | ||
public void unregisterDataSetObserver(DataSetObserver dataSetObserver) { | ||
mInnerAdapter.unregisterDataSetObserver(dataSetObserver); | ||
} | ||
|
||
@Override | ||
public int getCount() { | ||
return mInnerAdapter.getCount(); | ||
} | ||
|
||
@Override | ||
public Object getItem(int i) { | ||
return mInnerAdapter.getItem(i); | ||
} | ||
|
||
@Override | ||
public long getItemId(int i) { | ||
return mInnerAdapter.getItemId(i); | ||
} | ||
|
||
@Override | ||
public boolean hasStableIds() { | ||
return mInnerAdapter.hasStableIds(); | ||
} | ||
|
||
@Override | ||
public View getView(int i, View view, ViewGroup viewGroup) { | ||
View convertView = mInnerAdapter.getView(i,view,viewGroup); | ||
mViewToItemMap.put(convertView,getItemId(i)); | ||
mGroupIdToViewMap.add((int) getHeaderId(i),convertView); | ||
if(mCollapseGroupIds.contains(getHeaderId(i))){ | ||
convertView.setVisibility(View.GONE); | ||
}else { | ||
convertView.setVisibility(View.VISIBLE); | ||
} | ||
return convertView; | ||
} | ||
|
||
@Override | ||
public int getItemViewType(int i) { | ||
return mInnerAdapter.getItemViewType(i); | ||
} | ||
|
||
@Override | ||
public int getViewTypeCount() { | ||
return mInnerAdapter.getViewTypeCount(); | ||
} | ||
|
||
@Override | ||
public boolean isEmpty() { | ||
return mInnerAdapter.isEmpty(); | ||
} | ||
|
||
public List<View> getItemViewsByGroup(long groupId){ | ||
return mGroupIdToViewMap.get((int) groupId); | ||
} | ||
|
||
public boolean isGroupCollapsed(long groupId){ | ||
return mCollapseGroupIds.contains(groupId); | ||
} | ||
|
||
public void expand(long groupId) { | ||
if(isGroupCollapsed(groupId)){ | ||
mCollapseGroupIds.remove((Object)groupId); | ||
} | ||
} | ||
|
||
public void collapse(long groupId) { | ||
if(!isGroupCollapsed(groupId)){ | ||
mCollapseGroupIds.add(groupId); | ||
} | ||
} | ||
|
||
public View findViewByItemId(long itemId){ | ||
return mViewToItemMap.getKey(itemId); | ||
} | ||
|
||
public long findItemIdByView(View view){ | ||
return mViewToItemMap.get(view); | ||
} | ||
} |
Oops, something went wrong.