Skip to content

Commit

Permalink
add ExpandableStickyListHeaderListView which enable listview to expan…
Browse files Browse the repository at this point in the history
…d/collapse sub items
  • Loading branch information
lsjwzh committed Jun 15, 2014
1 parent 1cf7434 commit 3208d9c
Show file tree
Hide file tree
Showing 4 changed files with 445 additions and 0 deletions.
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 library/src/se/emilsjolander/stickylistheaders/DualHashMap.java
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);
}
}
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);
}
}
Loading

0 comments on commit 3208d9c

Please sign in to comment.