我正在使用带有自定义适配器的ExpandableListView,该适配器显示具有在线状态的联系人列表。如果他们离线,那么我从列表中删除联系人。反之亦然,如果他们在线,那么我将他们添加到列表中。每次添加或删除联系人时,我都会notifyDataSetChanged()
。随着时间的推移,我看到的是一些列表视图在列表底部重复。当我展开并折叠这些重复联系人的组时,它们就会消失。它看起来像是已经删除了这些重复的视图,但它们仍然存在并且视图被回收。发生了什么事?
通过广播改变在场状态。联系人的扩展名#是意图的附加内容。
private class PresenceReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equalsIgnoreCase(SIGNAL_ROSTER_CHANGE)) {
int extension = intent.getIntExtra("EXTRA_EXTENSION", -1);
if (extension != -1) {
mHandler.post(new Runnable() {
@Override
public void run() {
Contact contact = findContactByExtension(Integer.toString(extension));
if (contact != null) {
int presence = getPresenceForExtension(contact.getExtension());
if (presence == STATUS_UNKNOWN || presence == STATUS_INVISIBLE || presence == STATUS_OFFLINE) {
removeFromList(contact);
} else {
addToList(contact);
}
}
}
});
}
}
}
}
删除逻辑:遍历列表并删除与联系人匹配的所有实例。从列表中删除联系人时,请检查列表是否为空。如果列表为空,则删除组和列表。这是删除代码:
private void removeFromList(Contact contact) {
if (listAdapter != null) {
// Get adapter's child items
List<List<Contact>> adapterListOfLists = listAdapter.getContactLists(); // Child items
Iterator<List<Contact>> listIterator = adapterListOfLists.iterator();
int groupIndex = 0;
// Iterate through list to find instances of the contact
while (listIterator.hasNext()) {
List<Contact> listPart = listIterator.next();
Iterator<Contact> contactIterator = listPart.iterator();
while (contactIterator.hasNext()) {
Contact compareContact = contactIterator.next();
if (contact.equals(compareContact)) {
contactIterator.remove();
listAdapter.notifyDataSetChanged();
}
}
// Check if list is empty after removal
if (listPart.size() == 0) {
// Remove the list's group
List<String> adapterGroups = listAdapter.getContactGroup();
adapterGroups.remove(groupIndex);
// Remove the empty list
listIterator.remove();
listAdapter.notifyDataSetChanged();
}
groupIndex++;
}
}
}
联系人按名字排序,并按名称第一个字符的字母分组。如果名称的第一个字符不是字符,则将其归类于组:“#”。
添加联系人的逻辑: 检查联系人的组是否存在。如果存在,则存在子数据,并且我们对插入联系人的位置进行二进制搜索。如果该组不存在,则子数据不存在。我进行二进制搜索以查找组所属的位置并创建它。然后在子数据的相应索引中,我创建一个新列表,将联系人添加到列表中,并将列表插入与其组对应的索引中。这是插入代码:
private void addToList(Contact contact) {
String contactName;
String indexKey = "#";
if (contact.getFirstName().length() > 0) {
contactName = contact.getFirstName();
} else {
contactName = contact.getLastName();
}
if (contactName.length() > 0 && Character.isLetter(contactName.charAt(0))) {
indexKey = Character.toString(contactName.charAt(0));
}
insertContact(contact, indexKey);
}
private void insertContact(Contact contact, String group) {
if (listAdapter != null) {
List<List<Contact>> adapterListOfLists = listAdapter.getContactLists();
List<String> adapterGroups = listAdapter.getContactGroup();
String capitalizeFirstLetter = group.substring(0, 1).toUpperCase(Locale.getDefault());
group = capitalizeFirstLetter;
int groupIndex = adapterGroups.indexOf(group);
if (groupIndex == -1) {
int insertGroupTitleIndex = Collections.binarySearch(adapterGroups, group, new GroupTitleComparator());
if (insertGroupTitleIndex < 0) {
insertGroupTitleIndex = -insertGroupTitleIndex - 1;
}
adapterGroups.add(insertGroupTitleIndex, group);
List<Contact> newList = new ArrayList<Contact>();
newList.add(contact);
adapterListOfLists.add(insertGroupTitleIndex, newList);
} else {
List<Contact> groupList = adapterListOfLists.get(groupIndex);
int insertIndex = Collections.binarySearch(groupList, contact, new ContactComparator(sortType.SORT_BY_FIRST_NAME);
if (insertIndex < 0) {
insertIndex = -insertIndex - 1;
}
groupList.add(insertIndex, contact);
}
listAdapter.notifyDataSetChanged();
}
}
这是适配器的getGroupView():
@Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
GroupViewHolder groupHolder;
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.directory_alphabet_title, parent, false);
groupHolder = new GroupViewHolder();
groupHolder.title = (TextView) convertView.findViewById(R.id.group_title);
groupHolder.childrenCount = (TextView) convertView.findViewById(R.id.group_title_child_count);
convertView.setTag(groupHolder);
} else {
groupHolder = (GroupViewHolder) convertView.getTag();
}
if (getChildrenCount(groupPosition) > 0) {
groupHolder.title.setVisibility(View.VISIBLE);
groupHolder.childrenCount.setVisibility(View.VISIBLE);
groupHolder.title.setText((String) getGroup(groupPosition));
groupHolder.childrenCount.setText(Integer.toString(getChildrenCount(groupPosition)));
} else { // If contactGroup has no contactLists then hide it
groupHolder.title.setVisibility(View.GONE);
groupHolder.childrenCount.setVisibility(View.GONE);
}
return convertView;
}
getChildView():
@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
ChildViewHolder childHolder;
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.list_item_contact, parent, false);
childHolder = new ChildViewHolder();
childHolder.contactName = (TextView) convertView.findViewById(R.id.contact_name);
childHolder.statusIcon = (ImageView) convertView.findViewById(R.id.status_icon);
convertView.setTag(childHolder);
} else {
childHolder = (ChildViewHolder) convertView.getTag();
}
final Contact contact = (Contact) getChild(groupPosition, childPosition);
if (contact.getContactType().equals("corporate")) {
int presence = 0;
presence = getPresenceForExtension(contact.getExtension());
switch (presence) {
case STATUS_ONLINE:
childHolder.statusIcon.setImageResource(R.drawable.icon_status_available);
break;
case STATUS_BUSY:
childHolder.statusIcon.setImageResource(R.drawable.icon_status_busy);
break;
case STATUS_DND:
childHolder.statusIcon.setImageResource(R.drawable.icon_status_dnd);
break;
case INCALL:
childHolder.statusIcon.setImageResource(R.drawable.icon_status_oncall);
break;
case AWAY:
childHolder.statusIcon.setImageResource(R.drawable.icon_status_away);
break;
case STATUS_UNKNOWN:
case STATUS_OFFLINE:
case STATUS_INVISIBLE:
default:
childHolder.statusIcon.setImageResource(R.drawable.icon_status_invisible);
break;
}
}
childHolder.contactName.setText(contact.getName());
childHolder.contactName.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// Open detail page of this contact
}
});
childHolder.callButton.setFocusable(false);
childHolder.chatButton.setFocusable(false);
childHolder.favoriteButton.setFocusable(false);
return convertView;
}
适配器的组和子数据,位于适配器类内:
private List<List<Contact>> contactLists;
private List<String> contactGroup;
public List<List<Contact>> getContactLists() {
return contactLists;
}
public void setContactLists(List<List<Contact>> contactLists) {
this.contactLists = contactLists;
}
public List<String> getContactGroup() {
return contactGroup;
}
public void setContactGroup(List<String> contactGroup) {
this.contactGroup = contactGroup;
}
更新:我找到的唯一选择是保存第一个可见项目的位置。然后使用修改后的列表创建一个新的适配器,将其设置为列表视图,然后移回到之前记录的第一个可见项目的位置。还有更好的方法吗?