在我的应用程序中有一个 ExpandableListView,我需要在其中使用上下文操作菜单对多个组的几个子项进行操作。
在我的研究中,我发现对可扩展列表视图进行多项选择是不可能的,或者很难实现。所以我决定按如下方式实现我的自定义解决方案(我已经发布了下面的代码以进行澄清,它是代码的草稿,它不是最终的,我刚刚实现/硬编码了一些东西,看看它是否工作与否):
- 我点击一个孩子打开上下文操作菜单,我在孩子的视图上打勾,我改变了那个孩子的背景
- 每次点击每个孩子时,我都会打开相同的上下文操作菜单
- 关闭我还没有实现的上下文菜单(我想看看这是否可行)
- 我不会做从与我提供给适配器的地图相对应的地图中提取子项的操作
- 我的代码一直有效,直到我单击另一个组来选择另一个孩子
- 然后,当我打开另一个组时,选定的子项(刻度和背景)移动到该组中的子项(我刚刚打开相应的组以选择某些内容)并且最后选择的项目未选中
- 我张贴截图澄清
我没有观察到任何行为模式
我不知道为什么会这样。
适配器:
public class CoordinateExpandableListAdapter extends BaseExpandableListAdapter {
private Context context;
private Map<String, List<String>> coordinateList;
private List<String> groupList;
public CoordinateExpandableListAdapter(Context context, List<String> groupList, Map<String, List<String>> coordinateList) {
this.context = context;
this.groupList = groupList;
this.coordinateList = coordinateList;
}
@Override
public Object getChild(int groupPosition, int childPosition) {
return coordinateList.get(groupList.get(groupPosition)).get(childPosition);
}
@Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
class ChildRowHolder {
TextView childRowTitle;
public ChildRowHolder(View view) {
childRowTitle = (TextView) view.findViewById(R.id.child_item_expandable_list_view_title);
}
}
@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
View childRow = convertView;
ChildRowHolder childRowHolder = null;
String childRowTitle = (String) getChild(groupPosition, childPosition);
if (childRow == null) {
LayoutInflater inflater = ((Activity) context).getLayoutInflater();
childRow = inflater.inflate(R.layout.child_expandable_list_view_item, null);
childRowHolder = new ChildRowHolder(childRow);
childRow.setTag(childRowHolder);
} else {
childRowHolder = (ChildRowHolder) childRow.getTag();
}
childRowHolder.childRowTitle.setText(childRowTitle);
return childRow;
}
@Override
public int getChildrenCount(int groupPosition) {
return coordinateList.get(groupList.get(groupPosition)).size();
}
@Override
public Object getGroup(int groupPosition) {
return groupList.get(groupPosition);
}
@Override
public int getGroupCount() {
return groupList.size();
}
@Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
class GroupRowHolder {
TextView groupRowTitle;
public GroupRowHolder(View view) {
groupRowTitle = (TextView) view.findViewById(R.id.group_item_expandable_list_view);
}
}
@Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
View groupRow = convertView;
GroupRowHolder groupRowHolder = null;
String coodinateCategory = (String) getGroup(groupPosition);
if (groupRow == null) {
LayoutInflater inflator = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
groupRow = inflator.inflate(R.layout.group_expandable_list_view_item, null);
groupRowHolder = new GroupRowHolder(groupRow);
groupRow.setTag(groupRowHolder);
} else {
groupRowHolder = (GroupRowHolder) groupRow.getTag();
}
groupRowHolder.groupRowTitle.setText(coodinateCategory);
groupRowHolder.groupRowTitle.setTypeface(null, Typeface.BOLD);
return groupRow;
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
}
可扩展列表视图的片段:
expandableListView = (ExpandableListView) getActivity().findViewById(R.id.coordinate_expandable_list_view);
CoordinateExpandableListAdapter expandableListAdapter = new CoordinateExpandableListAdapter(getActivity(), groupList, coordinateList);
expandableListView.setAdapter(expandableListAdapter);
registerForContextMenu(expandableListView);
expandableListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
@Override
public boolean onChildClick(ExpandableListView parent, View childView, int groupPosition, int childPosition, long id) {
// Start the CAB using the ActionMode.Callback defined above
actionMode = getActivity().startActionMode(actionModeCallback);
ArrayList<Integer> positions = new ArrayList<Integer>();
positions.add(groupPosition);
positions.add(childPosition);
Integer key = -1;
if ((key = isPositionsAdded(positions)) == null) {
selectedMap.put(selectedMapKey++, positions);
ImageView tick = (ImageView) childView.findViewById(R.id.child_item_expandable_list_view_selected);
tick.setVisibility(View.VISIBLE);
childView.setBackgroundColor(getResources().getColor(R.color.backgroung_expandable_list_view_child));
} else {
selectedMap.remove(key);
ImageView tick = (ImageView) childView.findViewById(R.id.child_item_expandable_list_view_selected);
tick.setVisibility(View.GONE);
childView.setBackgroundColor(getResources().getColor(R.color.background_color));
}
return true;
}
});
expandableListView.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
@Override
public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
// TODO Auto-generated method stub
return false;
}
});
}
private Integer isPositionsAdded(ArrayList<Integer> positions) {
if (selectedMap.containsValue(positions)) {
for (Map.Entry<Integer, ArrayList<Integer>> entry : selectedMap.entrySet()) {
if (entry.getValue().equals(positions)) {
return entry.getKey();
}
}
}
return null;
}
private ActionMode.Callback actionModeCallback = new ActionMode.Callback() {// Called when the action mode is created; startActionMode() was called
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
// Inflate a menu resource providing context menu items
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.coordinate_list_context_menu, menu);
return true;
}
// Called each time the action mode is shown. Always called after onCreateActionMode, but
// may be called multiple times if the mode is invalidated.
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false; // Return false if nothing is done
}
// Called when the user selects a contextual menu item
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
case R.id.delete:
deleteCurrentItems();
mode.finish(); // Action picked, so close the CAB
return true;
default:
return false;
}
}
// Called when the user exits the action mode
@Override
public void onDestroyActionMode(ActionMode mode) {
actionMode = null;
}
};
屏幕截图: - 首先我从组 2 中选择项目 1、2
- 第二我打开了第 1 组,选择从所选项目到第 1 组的子项
最糟糕的是,这种行为并不一致,有时它会按预期工作,但大多数时候它不会,也许它这样做的时候纯粹是巧合
编辑
正如我在下面的评论中提到的,我发布了我的工作自定义代码草稿,供人们在这个问题中寻找答案。它不是最终代码,但它正在工作并提供有关解决方案的想法。
Jay Soyer 关于 3rd 方库的建议看起来相当不错,并在生产中进行了测试。
当我单击一个孩子时,会调用 setOnChildClickListener onClick。对位置进行第一次验证(是否添加到自定义数据结构中),然后我相应地选择/取消选择子项。
expandableListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
@Override
public boolean onChildClick(ExpandableListView parent, View childView, int groupPosition, int childPosition, long id) {
// check / not check a child
ArrayList<Object> position = new ArrayList<Object>();
position.add(groupPosition);
position.add(childPosition);
position.add(childView);
if (expandableListAdapter.isPositionAleadyAdded(position) == null) {
expandableListAdapter.selectChild(position);
} else {
expandableListAdapter.deSelectChild(position);
}
// Start the CAB using the ActionMode.Callback
// defined above
actionMode = getActivity().startActionMode(actionModeCallback);
// set title of contextual action mode
setContextualMenuTitle((ActionMode) actionMode);
return true;
}
});
之后,当我单击一个组以展开/收缩该组时,将调用适配器 getGroupView 和 getChildView 方法。在这些方法中,我再次进行了类似的验证。我将组位置、子位置和视图保存在列表 (ArrayList) 中。
private List<ArrayList<Object>> selectedChildren = new ArrayList<ArrayList<Object>>();
@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
View childView = convertView;
ChildRowHolder childRowHolder = null;
String childRowTitle = (String) getChild(groupPosition, childPosition);
if (childView == null) {
LayoutInflater inflater = ((Activity) context).getLayoutInflater();
childView = inflater.inflate(R.layout.child_expandable_list_view_item, null);
childRowHolder = new ChildRowHolder(childView);
childView.setTag(childRowHolder);
} else {
childRowHolder = (ChildRowHolder) childView.getTag();
}
childRowHolder.childRowTitle.setText(childRowTitle);
// select / deselect child
ArrayList<Object> position = new ArrayList<Object>();
position.add(groupPosition);
position.add(childPosition);
position.add(childView);
if (isPositionAleadyAdded(position) != null) {
selectChild(position);
} else {
deSelectChild(position);
}
return childView;
}
/**
* This method return the key in the selected child map if it exists already. Null otherwise.
*
* @param position the group and child position
* @return the key of the element of the map if it exists already, null otherwise
*/
public ArrayList<Object> isPositionAleadyAdded(ArrayList<Object> position) {
for (ArrayList<Object> entry : selectedChildren) {
if (entry.get(0) == position.get(0)
&& entry.get(1) == position.get(1)) {
return position;
}
}
return null;
}
/**
* This method does the selection of a child.
*
* @param childView view of child
* @param position position of child
*/
public void selectChild(ArrayList<Object> position) {
if (isPositionAleadyAdded(position) == null) {
selectedChildren.add(position);
}
LinearLayout childView = (LinearLayout) position.get(2);
ImageView tick = (ImageView) childView.findViewById(R.id.child_item_expandable_list_view_selected);
tick.setVisibility(View.VISIBLE);
childView.setBackgroundColor(context.getResources().getColor(R.color.backgroung_expandable_list_view_child));
}
/**
* This method un selects a child.
*
* @param childView view of child
* @param position position of child
*/
public void deSelectChild(ArrayList<Object> position) {
ArrayList<Object> pos = null;
int elemNo = -1;
if ((pos = isPositionAleadyAdded(position)) != null) {
elemNo = getNoElemInSelectedChildred(pos);
selectedChildren.remove(elemNo);
}
LinearLayout childView = (LinearLayout) position.get(2);
ImageView tick = (ImageView) childView.findViewById(R.id.child_item_expandable_list_view_selected);
tick.setVisibility(View.GONE);
childView.setBackgroundColor(context.getResources().getColor(R.color.background_color));
}
private int getNoElemInSelectedChildred(ArrayList<Object> position) {
int index = 0;
for (ArrayList<Object> entry : selectedChildren) {
if (entry.get(0) == position.get(0) && entry.get(1) == position.get(1)) {
return index;
} else {
index++;
}
}
return -1;
}
/*
* This method clears the map of all children.
*/
public void unSelectAllChildren() {
for (ArrayList<Object> entry : selectedChildren) {
LinearLayout childView = (LinearLayout) entry.get(2);
ImageView tick = (ImageView) childView.findViewById(R.id.child_item_expandable_list_view_selected);
tick.setVisibility(View.GONE);
childView.setBackgroundColor(context.getResources().getColor(R.color.background_color));
}
selectedChildren.clear();
}
public List<ArrayList<Object>> getSelectedChildren() {
return selectedChildren;
}
我设法在单击侦听器上捕获上下文菜单刻度按钮以调用 unSelectAllChildren 方法
private ActionMode.Callback actionModeCallback = new ActionMode.Callback() {
...
// Called when the user exits the action mode
@Override
public void onDestroyActionMode(final ActionMode mode) {
int doneButtonId = Resources.getSystem().getIdentifier("action_mode_close_button", "id", "android");
LinearLayout layout = (LinearLayout) getActivity().findViewById(doneButtonId);
ImageView doneview = (ImageView) layout.getChildAt(0);
doneview.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
expandableListAdapter.unSelectAllChildren();
setContextualMenuTitle(mode);
}
});
}
};