public class SelectHelper {
private Context context;
private AbsListView mObj;
private int listCount = -1;
public void setListCount(int listCount) {
this.listCount = listCount;
}
public SelectHelper(Context context, AttributeSet attrs, AbsListView obj) {
this.context = context;
this.mObj = obj;
mode = context.getResources().getStringArray(R.array.select_item);
clickR = new Runnable() {
@Override
public void run() {
onSingleTap(mLastTouchPosition);
}
};
longPressR = new Runnable() {
@Override
public void run() {
isClick = false;
onLongPress();
}
};
mHandler = new Handler();
longPressTimeout = ViewConfiguration.getLongPressTimeout();
slop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
}
private Handler mHandler;
private int longPressTimeout;
private int doubleTapTimeout = 250;
private int slop;
private Runnable clickR;
private Runnable longPressR;
private Context getContext() {
return context;
}
private int mLastTouchPosition = -1;
private int mFirstSelect = -1;
private long first = 0;
private int mDownX;
private int mDownY;
private boolean isClick;
public void onTouchEvent(MotionEvent ev, int motionPosition) {
if(motionPosition == ListView.INVALID_POSITION)return;
int x = (int) ev.getX();
int y = (int) ev.getY();
int dx = Math.abs(x - mDownX);
int dy = Math.abs(y - mDownY);
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
if (mLastTouchPosition != motionPosition && System.currentTimeMillis() - first < doubleTapTimeout) {
mHandler.removeCallbacks(clickR);
onSingleTap(mLastTouchPosition);
}
mLastTouchPosition = motionPosition;
mDownX = (int) ev.getX();
mDownY = (int) ev.getY();
isClick = true;
mHandler.postDelayed(longPressR, longPressTimeout);
break;
case MotionEvent.ACTION_MOVE:
if (dx >= slop || dy >= slop) {
mHandler.removeCallbacks(longPressR);
mHandler.removeCallbacks(clickR);
isClick = false;
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
mHandler.removeCallbacks(longPressR);
if (isClick) {
if (System.currentTimeMillis() - first < doubleTapTimeout) {
mHandler.removeCallbacks(clickR);
onDoubleTap();
} else {
if(OnDoubleClickListener==null)doubleTapTimeout = 0;
mHandler.postDelayed(clickR, doubleTapTimeout);
first = System.currentTimeMillis();
}
}
break;
}
}
private void onSingleTap(int position) {
int selectNum = mSelecteds.size();
switch (currentMode) {
case mode0:
radio(position);
break;
case mode1:
multiselect(position);
break;
case mode2:
continuity(position);
break;
}
mObj.invalidateViews();
if (selectNum != mSelecteds.size()) {
if (OnSelectNumChangeListener != null)
OnSelectNumChangeListener.selectChange(mSelecteds.size(), getCount());
}
}
private void radio(int position) {
mSelecteds.clear();
mSelecteds.add(position);
checkPostionKeep();
}
private void multiselect(int position) {
if (!mSelecteds.remove(position))
mSelecteds.add(position);
if (mFirstSelect == -1)
mFirstSelect = position;
checkPostionKeep();
}
public void continuity(int position) {
if (mFirstSelect == -1) {
mFirstSelect = position;
mSelecteds.add(position);
} else {
int start = Math.min(position, mFirstSelect);
int end = Math.max(position, mFirstSelect);
if (start != end) {
mSelecteds.clear();
for (int i = start; i <= end; i++) {
mSelecteds.add(i);
}
} else if (start == end && mSelecteds.size() > 1) {
mSelecteds.clear();
mSelecteds.add(start);
} else {
mSelecteds.clear();
}
}
checkPostionKeep();
}
private void onDoubleTap() {
if (OnDoubleClickListener != null)
OnDoubleClickListener.onClick(mLastTouchPosition);
}
public void setChoiceMode() {
}
public static enum Mode {
RADIO(mode0), MULTISELECT(mode1), CONTINUITY(mode2), REVERSESELECT(mode3), SELECTALL(mode4), CLEAR(mode5);
int value;
private Mode(int value) {
this.value = value;
}
}
public void initSelectMode() {
currentMode = mode0;
}
private String[] mode;
private static final int mode0 = 0;
private static final int mode1 = 1;
private static final int mode2 = 2;
private static final int mode3 = 3;
private static final int mode4 = 4;
private static final int mode5 = 5;
private int currentMode = mode0;
private Set<Integer> mSelecteds = new HashSet<Integer>();
private OnDoubleClickListener OnDoubleClickListener;
private int parmPosition;
private OnSelectNumChangeListener OnSelectNumChangeListener;
private void onLongPress() {
AlertDialog dialog = DialogUtils.singleChoice(getContext(), mode, currentMode, new SingleChoiceListener() {
@Override
public void onClick(DialogInterface dialog, int selected, CharSequence choice) {
currentMode = selected;
if (selected == mode3) {
currentMode = mode0;
reverseSelect();
} else if (selected == mode4) {
currentMode = mode0;
SelectAll();
} else if (selected == mode5) {
currentMode = mode0;
init();
}
dialog.dismiss();
}
});
dialog.show();
}
private void SelectAll() {
int selectNum = mSelecteds.size();
int end = getCount();
for (int i = 0; i < end; i++) {
mSelecteds.add(i);
}
mObj.invalidateViews();
if (selectNum != mSelecteds.size()) {
if (OnSelectNumChangeListener != null)
OnSelectNumChangeListener.selectChange(mSelecteds.size(), getCount());
}
}
private int getCount() {
return (listCount == -1) ? mObj.getAdapter().getCount() : listCount;
}
private void checkPostionKeep() {
if (mSelecteds.size() == 0) {
mFirstSelect = -1;
} else if (mSelecteds.size() == 1 || mFirstSelect == -1) {
mFirstSelect = (Integer) mSelecteds.toArray()[0];
}
}
private void reverseSelect() {
int selectNum = mSelecteds.size();
int start = 0;
int end = getCount();
for (int i = start; i < end; i++) {
if (!mSelecteds.remove(i))
mSelecteds.add(i);
}
checkPostionKeep();
mObj.invalidateViews();
if (selectNum != mSelecteds.size()) {
if (OnSelectNumChangeListener != null)
OnSelectNumChangeListener.selectChange(mSelecteds.size(), getCount());
}
}
public boolean isChecked(int pos){
return mSelecteds.contains(pos);
}
public void setOnDoubleClickListener(OnDoubleClickListener l) {
this.OnDoubleClickListener = l;
}
public interface OnSelectNumChangeListener {
void selectChange(int selectNum, int count);
}
public void setOnSelectChangeListener(OnSelectNumChangeListener l) {
this.OnSelectNumChangeListener = l;
}
public interface OnDoubleClickListener {
void onClick(int position);
}
public void init() {
int size = mSelecteds.size();
mSelecteds.clear();
checkPostionKeep();
if (OnSelectNumChangeListener != null&&size>0)
OnSelectNumChangeListener.selectChange(0, getCount());
if(size>0)
mObj.invalidateViews();
}
public Collection<? extends Integer> getSelecteds() {
return mSelecteds;
}
}
public class SelectableListView extends ListView {
private SelectHelper mOperationList;
public SelectableListView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public SelectableListView(Context context) {
super(context);
init(context, null);
}
public SelectableListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
mOperationList = new SelectHelper(context, attrs, this);
}
@SuppressLint("ClickableViewAccessibility")
public boolean onTouchEvent(MotionEvent ev) {
pos = pointToPosition((int) ev.getX(), (int) ev.getY());
mOperationList.onTouchEvent(ev, pos);
return super.onTouchEvent(ev);
}
@Override
public void setAdapter(ListAdapter adapter) {
throw new RuntimeException();
}
public void setAdapter(SelectableListViewAdapter adapter) {
super.setAdapter(adapter);
}
public static abstract class SelectableListViewAdapter extends BaseAdapter{
@Override
public final View getView(int position, View convertView, ViewGroup parent) {
SelectableListView listView = (SelectableListView) parent;
return getView(position, convertView, parent,listView.isChildChecked(position));
}
public abstract View getView(int position, View convertView, ViewGroup parent,boolean isChecked);
}
private boolean isChildChecked(int pos) {
return mOperationList.isChecked(pos);
}
private int pos = -1;
public void setOnDoubleClickListener(final OnDoubleClickListener l) {
mOperationList.setOnDoubleClickListener(new SelectHelper.OnDoubleClickListener() {
@Override
public void onClick(int position) {
l.onClick(position);
}
});
}
public interface OnDoubleClickListener {
void onClick(int position);
}
public interface OnSelectNumChangeListener {
/**
*
* @param position
* OnTouchPos
* @param selectNum
* @param count
*/
void selectChange(int position, int selectNum, int count);
}
public void setOnSelectChangeListener(final OnSelectNumChangeListener l) {
mOperationList.setOnSelectChangeListener(new SelectHelper.OnSelectNumChangeListener() {
@Override
public void selectChange(int selectNum, int count) {
l.selectChange(pos, selectNum, count);
}
});
}
public List<Integer> getSelecteds() {
List<Integer> list = new ArrayList<Integer>();
list.addAll(mOperationList.getSelecteds());
return list;
}
public void clear() {
mOperationList.init();
}
public void initSelectMode() {
mOperationList.initSelectMode();
}
}
public class SelectableExpandListView extends ExpandableListView {
public final static int CHILD = 1;
public final static int GROUP = 2;
private Object mChildConnector;
private Method getUnflattenedPosMethod;
private Field positionField;
private Field typeField;
private Field childPosField;
private Field groupPosField;
private Method recycleMethod;
private SelectHelper mOperationList;
private int mCurrentGroupPosition = -1;
public SelectableExpandListView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public SelectableExpandListView(Context context) {
super(context, null);
}
public SelectableExpandListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
mOperationList = new SelectHelper(context, attrs, this);
}
@SuppressLint("ClickableViewAccessibility")
public boolean onTouchEvent(MotionEvent ev) {
int x = (int) ev.getX();
int y = (int) ev.getY();
int pos = pointToPosition(x, y);
Object posMetadata = null;
try {
posMetadata = getUnflattenedPosMethod.invoke(mChildConnector, pos);
Object postion = positionField.get(posMetadata);
int type = typeField.getInt(postion);
groupPos = groupPosField.getInt(postion);
if (type == GROUP) {
if (ev.getAction() == MotionEvent.ACTION_UP && groupPos != -1)
checkGroupPosKeep(groupPos);
} else if (type == CHILD) {
childPos = childPosField.getInt(postion);
if (ev.getAction() == MotionEvent.ACTION_UP)
checkGroupPosKeep(groupPos);
mOperationList.onTouchEvent(ev, childPos);
}
} catch (Exception e) {
} finally {
try {
recycleMethod.invoke(posMetadata);
} catch (Exception e) {
}
}
return super.onTouchEvent(ev);
}
private void checkGroupPosKeep(int groupPos) {
if (mCurrentGroupPosition != groupPos) {
if (mCurrentGroupPosition != -1)
mOperationList.init();
mCurrentGroupPosition = groupPos;
mOperationList.setListCount(getExpandableListAdapter()
.getChildrenCount(groupPos));
}
}
@Override
public void setAdapter(ExpandableListAdapter adapter) {
throw new RuntimeException("");
}
public void setAdapter(SelectableExpandableListAdapter adapter) {
super.setAdapter(adapter);
try {
Field mConnectorField = SelectableExpandListView.class.getSuperclass().getDeclaredField("mConnector");
mConnectorField.setAccessible(true);
mChildConnector = mConnectorField.get(this);
Class<?> clazz = Class.forName("android.widget.ExpandableListConnector");
getUnflattenedPosMethod = clazz.getDeclaredMethod("getUnflattenedPos", int.class);
getUnflattenedPosMethod.setAccessible(true);
clazz = Class.forName("android.widget.ExpandableListConnector$PositionMetadata");
recycleMethod = clazz.getDeclaredMethod("recycle");
positionField = clazz.getDeclaredField("position");
positionField.setAccessible(true);
clazz = Class.forName("android.widget.ExpandableListPosition");
typeField = clazz.getDeclaredField("type");
typeField.setAccessible(true);
childPosField = clazz.getDeclaredField("childPos");
childPosField.setAccessible(true);
groupPosField = clazz.getDeclaredField("groupPos");
groupPosField.setAccessible(true);
} catch (Exception e) {
}
}
public static abstract class SelectableExpandableListAdapter extends BaseExpandableListAdapter {
@Override
public final View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
SelectableExpandListView listView = (SelectableExpandListView) parent;
return getChildView(groupPosition, childPosition, isLastChild, convertView, parent, listView.isChildChecked(groupPosition, childPosition));
}
public abstract View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent, boolean isChecked);
}
private boolean isChildChecked(int groupPosition, int childPosition) {
return groupPosition == mCurrentGroupPosition && mOperationList.isChecked(childPosition);
}
public interface OnSelectNumChangeListener {
/**
* @param childPos OnTouchPos
* @param selectNum
* @param count
* @param groupPos
*/
void selectChange(int groupPos, int childPos, int selectNum, int count);
}
public void setOnSelectChangeListener(final OnSelectNumChangeListener l) {
mOperationList.setOnSelectChangeListener(new SelectHelper.OnSelectNumChangeListener() {
public void selectChange(int selectNum, int count) {
l.selectChange(groupPos, childPos, selectNum, count);
}
});
}
public interface OnDoubleClickListener {
void onClick(int group, int position);
}
private int childPos;
private int groupPos;
public void setOnDoubleClickListener(final OnDoubleClickListener l) {
mOperationList.setOnDoubleClickListener(new SelectHelper.OnDoubleClickListener() {
public void onClick(int position) {
l.onClick(mCurrentGroupPosition, position);
}
});
}
@SuppressLint("UseSparseArrays")
public Map<Integer, Collection<? extends Integer>> getSelecteds() {
Collection<? extends Integer> selecteds = mOperationList.getSelecteds();
Map<Integer, Collection<? extends Integer>> map = new HashMap<Integer, Collection<? extends Integer>>();
map.put(mCurrentGroupPosition, selecteds);
return map;
}
public void clear() {
mOperationList.init();
}
public void initSelectMode() {
mOperationList.initSelectMode();
}
}