I have a problem. I have an ExpandableListView
and a custom Adapter that is extended from BaseExpandableListAdapter
. And I have an ArrayList
that contains my groups and groups' children items.
The problem is:
1) Expand the 3rd group.
2) Delete the first item from ArrayList and notifyDataSetInvalidated
, notifyDataSetChanged
3) getGroupView
is requested right, and I can set it that I need.
4) But getChildView
is requested for old position(3). When the first item is deleted, indexes are changed. But ListView
tries to get old position's childview.
This is my understanding. Where can be the problem?
This is My Adapter:
public class DashboardAdapter extends BaseExpandableListAdapter implements View.OnClickListener {
private static final String TAG = DashboardAdapter.class.getSimpleName();
private static final int groupTypeCount = GroupType.values().length;
private final Activity activity;
private final ExpandableListView exListView;
private final List<Object> dashboard = new ArrayList<>();
private String lastExpandedProductIban = null;
public DashboardAdapter(final Activity activity, ExpandableListView expandableListView) {
this.activity = activity;
exListView = expandableListView;
}
public void setDashboard(final List<Object> dashboard) {
if (Verify.notNull(dashboard)) {
this.dashboard.clear();
this.dashboard.addAll(dashboard);
}
@Override
public int getGroupTypeCount() {
return groupTypeCount;
}
@Override
public int getGroupType(int groupPosition) {
final Object group = getGroup(groupPosition);
if (group instanceof Dashboard.Products.Accordion)
return GroupType.PRODUCT_ACCORDION.ordinal();
return -1;
}
@Override
public int getGroupCount() {
return dashboard.size();
}
@Override
public int getChildrenCount(int groupPosition) {
if (getGroupType(groupPosition) == GroupType.PRODUCT_ACCORDION.ordinal())
return ((Dashboard.Products.Accordion) getGroup(groupPosition)).actions.size();
return 0;
}
@Override
public Object getGroup(int groupPosition) {
if (groupPosition >= 0 && groupPosition < dashboard.size())
return dashboard.get(groupPosition);
return null;
}
@Override
public Object getChild(int groupPosition, int childPosition) {
if (getGroupType(groupPosition) == GroupType.PRODUCT_ACCORDION.ordinal()) {
final Dashboard.Products.Accordion group = (Dashboard.Products.Accordion) getGroup(groupPosition);
if (group != null && childPosition >= 0 && childPosition < group.actions.size())
return group.actions.get(childPosition);
}
return null;
}
@Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
@Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public View getGroupView(final int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
// initialise all possible ViewHolders
ViewHolderAccordion viewHolderAccordion = null;
// get GroupType of the current View
final GroupType groupType = GroupType.valueOf(getGroupType(groupPosition));
// Inflate the layout if convertView is null
if (convertView == null) {
LayoutInflater inflater = activity.getLayoutInflater();
switch (groupType) {
case PRODUCT_ACCORDION:
convertView = inflater.inflate(R.layout.item_dashboard_accordion, parent, false);
viewHolderAccordion = new ViewHolderAccordion(convertView);
break;
}
}
// reuse convertView if it isn't null
else {
switch (groupType) {
case PRODUCT_ACCORDION:
viewHolderAccordion = ((ViewHolderAccordion) convertView.getTag()).reset();
break;
}
}
if (viewHolderAccordion != null) {
final Dashboard.Products.Accordion accordion = (Dashboard.Products.Accordion) getGroup(groupPosition);
// set alternating background color
//if(dashboardGroups.accounts.indexOf(account) % 2 == 0)
//convertView.setBackgroundColor(activity.getResources().getColor(R.color.dashboard_account_alternate_color));
// populate view with content
if (accordion != null) {
setText(viewHolderAccordion.tvProductAccordionName, accordion.title);
setText(viewHolderAccordion.tvProductAccordionDetail, accordion.detail);
setText(viewHolderAccordion.tvProductAccordionBalance, accordion.balance);
boolean _isExpanded = getPositionFromIban(lastExpandedProductIban) == groupPosition;
accordion.isExpanded = _isExpanded;
viewHolderAccordion.groupExpander.setImageResource(_isExpanded ? R.drawable.expander_white_close : R.drawable.expander_open);
viewHolderAccordion.contentView.setActivated(_isExpanded ? true : false);
viewHolderAccordion.dashboard_product_accordion_divider.setVisibility(_isExpanded ? View.GONE : View.VISIBLE);
}
convertView.setTag(viewHolderAccordion);
}
return convertView;
}
private void deleteCell(final View view, final int groupPosition) {
final Animation.AnimationListener animationListener = new Animation.AnimationListener() {
@Override
public void onAnimationEnd(Animation arg0) {
dashboard.remove(groupPosition);
notifyDataSetInvalidated();
notifyDataSetChanged();
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationStart(Animation animation) {
}
};
collapse(view, animationListener);
}
private void collapse(final View view, final Animation.AnimationListener al) {
final int initialHeight = view.getMeasuredHeight();
Animation anim = new Animation() {
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
if (interpolatedTime == 1) {
view.setVisibility(View.GONE);
} else {
view.getLayoutParams().height = initialHeight - (int) (initialHeight * interpolatedTime);
view.requestLayout();
}
}
@Override
public boolean willChangeBounds() {
return true;
}
};
if (al != null) {
anim.setAnimationListener(al);
}
anim.setDuration(200);
view.startAnimation(anim);
}
@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
if (getGroupType(groupPosition) == GroupType.PRODUCT_ACCORDION.ordinal()) {
final Dashboard.Products.Accordion.Action action =
(Dashboard.Products.Accordion.Action) getChild(groupPosition, childPosition);
if (action != null) {
ViewHolderAccordionLink viewHolderAccordionLink;
if (convertView == null) {
LayoutInflater inflater = activity.getLayoutInflater();
convertView = inflater.inflate(R.layout.item_dashboard_accordion_link, parent, false);
viewHolderAccordionLink = new ViewHolderAccordionLink(convertView);
} else {
viewHolderAccordionLink = ((ViewHolderAccordionLink) convertView.getTag()).reset();
}
// populate view with content
if (viewHolderAccordionLink != null) {
if (Verify.notNull(action.icon)) {
viewHolderAccordionLink.ivAccordionLink.setVisibility(View.VISIBLE);
} else {
viewHolderAccordionLink.ivAccordionLink.setVisibility(View.GONE);
}
if (action.title != null) {
setText(viewHolderAccordionLink.tvAccordionLinkLabel, action.title);
}
convertView.setTag(viewHolderAccordionLink);
}
}
}
return convertView;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return getChildrenCount(groupPosition) > 0;
}
@Override
public boolean areAllItemsEnabled() {
return false;
}
@Override
public boolean isEmpty() {
return dashboard.size() == 0;
}
@Override
public void onGroupExpanded(int groupPosition) {
}
@Override
public void onGroupCollapsed(int groupPosition) {
}
private void setText(final TextView textView, final DashboardText text) {
if (text != null) {
textView.setVisibility(View.VISIBLE);
if (Verify.notNull(text.text)) {
textView.setText(Html.fromHtml(text.text));
}
switch (text.color) {
case BLACK:
textView.setTextColor(activity.getResources().getColorStateList(R.color.selector_dashboard_black));
break;
case ORANGE:
textView.setTextColor(activity.getResources().getColorStateList(R.color.selector_dashboard_orange));
break;
case RED:
textView.setTextColor(activity.getResources().getColorStateList(R.color.selector_dashboard_red));
break;
case GREEN:
textView.setTextColor(activity.getResources().getColorStateList(R.color.selector_dashboard_green));
break;
case GREY:
textView.setTextColor(activity.getResources().getColorStateList(R.color.selector_dashboard_grey));
break;
case WHITE:
textView.setTextColor(activity.getResources().getColor(R.color.white));
break;
}
} else {
textView.setVisibility(View.GONE);
}
}
public String getIbanAtPosition(int groupPosition) {
if (getGroupType(groupPosition) == GroupType.PRODUCT_ACCORDION.ordinal()) {
final Dashboard.Products.Accordion group = (Dashboard.Products.Accordion) getGroup(groupPosition);
if (group != null) {
return group.detail.text;
}
}
return null;
}
public int getPositionFromIban(String iban) {
if (dashboard != null) {
for (int groupPosition = 0; groupPosition < dashboard.size(); groupPosition++) {
if (getGroupType(groupPosition) == GroupType.PRODUCT_ACCORDION.ordinal()) {
Dashboard.Products.Accordion group = (Dashboard.Products.Accordion) getGroup(groupPosition);
if (group.detail.text.equalsIgnoreCase(iban))
return groupPosition;
}
}
}
return -1;
}
public void setLastExpandedProductIban(String lastExpandedProductIban) {
this.lastExpandedProductIban = lastExpandedProductIban;
}
private enum GroupType {
PERSONALIZATION, PRODUCT_ACCORDION;
// Converts from an ordinal value to the Groups
public static GroupType valueOf(final int index) {
final GroupType[] values = GroupType.values();
if (index < 0 || index >= values.length) {
return PERSONALIZATION;
}
return values[index];
}
}
class ViewHolderAccordion {
public View contentView;
public ImageView groupExpander;
public TextView tvProductAccordionName;
public TextView tvProductAccordionDetail;
public TextView tvProductAccordionBalance;
public View dashboard_product_accordion_divider;
public ViewHolderAccordion(View contentView) {
this.contentView = contentView;
groupExpander = (ImageView) contentView.findViewById(R.id.groupExpander);
tvProductAccordionName = (TextView) contentView.findViewById(R.id.tvProductAccordionName);
tvProductAccordionDetail = (TextView) contentView.findViewById(R.id.tvProductAccordionDetail);
tvProductAccordionBalance = (TextView) contentView.findViewById(R.id.tvProductAccordionBalance);
dashboard_product_accordion_divider = contentView.findViewById(R.id.dashboard_product_accordion_divider);
}
public ViewHolderAccordion reset() {
//contentView.setBackgroundColor(0xFFffffff);
tvProductAccordionName.setText("");
tvProductAccordionDetail.setText("");
tvProductAccordionDetail.setVisibility(View.VISIBLE);
tvProductAccordionBalance.setText("");
return this;
}
}}
I can solve this problem with using
@Override
public long getGroupId(int groupPosition) {
return dashboard.get(groupPosition).hashCode();
}
instead of
@Override
public long getGroupId(int groupPosition) {
return groupPosition;
}