I have an expandable list view where users can click a button to "save" a record. However, the listviews of my expandable listview seem to populate properly (the titles are different). But when a user "saves" element 1 it also saves elements 4,7,10,...n+3 (I know this because the view is set to invisible once it's saved). If you look below, I think I am recycling elements properly using the setTag() method in my getChildView() method.
I put some logging in my getChildView() for testing whether or not the view is cached is or drawn for the first time. The first two views are drawn and the rest are cached.
So my problem, as stated before is that when someone interacts with an element on the expandablelistview other elements are changed accordingly.
Here is my adapter:
public class SurveyListAdapter extends BaseExpandableListAdapter {
private final SparseArray<Group> groups;
public LayoutInflater inflater;
private Context appContext;
public String TAG = "SurveyListAdapter";
private static SparseArray<SparseArray<Survey>> results = new SparseArray<SparseArray<Survey>>();
//private static SparseArray<ArrayList<Boolean>> rowSubmitted = new SparseArray<ArrayList<Boolean>>();
public SurveyListAdapter(Context context, SparseArray<Group> groups) {
this.appContext = context;
this.groups = groups;
for(int i = 0;i<groups.size();i++) {
Log.d(TAG, "Creating group: " + i);
results.put(i, new SparseArray<Survey>());
}
//inflater = context.getLayoutInflater();
this.inflater = (LayoutInflater) context.getSystemService( Context.LAYOUT_INFLATER_SERVICE );
}
#Override
public Survey getChild(int groupPosition, int childPosition) {
//Log.d("getChild", "Group: " + groupPosition + "\tChild: " + childPosition);
return this.groups.get(groupPosition).children.get(childPosition);
}
#Override
public long getChildId(int groupPosition, int childPosition) {
//return Long.parseLong(this.groups.get(groupPosition).children.get(childPosition));
return childPosition;
}
public Survey getIdOfChild(int groupPosition, int childPosition) {
return this.groups.get(groupPosition).children.get(childPosition);
}
#Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
View row = convertView;
SurveyViewHolder svh;
Survey child = getChild(groupPosition, childPosition);
if (row == null) {
row = inflater.inflate(R.layout.list_view_survey_row, parent, false);
svh = new SurveyViewHolder();
svh.title = (TextView) row.findViewById(R.id.tvSurveyTitle);
svh.rg = (RadioGroup) row.findViewById(R.id.radioShared);
svh.interest = (CheckBox) row.findViewById(R.id.checkBoxInterest);
svh.privacy = (CheckBox) row.findViewById(R.id.checkBoxPrivacy);
svh.rating = (RatingBar) row.findViewById(R.id.ratingBar);
svh.submit = (Button) row.findViewById(R.id.btnSubmitRow);
row.setTag(svh);
Log.d(TAG, "Built from scratch");
} else {
Log.d(TAG, "Cached");
svh = (SurveyViewHolder) row.getTag();
}
svh.title.setText(child.getTitle());
svh.children = child.getTitle();
svh.submit.setOnClickListener(new CustomOnClickListener(row, svh, groupPosition, childPosition));
//this.registerClickListeners(groupPosition, childPosition, convertView);
return row;
}
#Override
public int getChildrenCount(int groupPosition) {
return groups.get(groupPosition).children.size();
}
#Override
public Object getGroup(int groupPosition) {
return groups.get(groupPosition);
}
#Override
public int getGroupCount() {
return groups.size();
}
#Override
public void onGroupCollapsed(int groupPosition) {
super.onGroupCollapsed(groupPosition);
}
#Override
public void onGroupExpanded(int groupPosition) {
super.onGroupExpanded(groupPosition);
}
#Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
#Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = inflater.inflate(R.layout.xplist_view_survey_group, null);
}
Group group = (Group) getGroup(groupPosition);
((TextView) convertView).setText(group.string);
//((CheckedTextView) convertView).setChecked(isExpanded);
return convertView;
}
#Override
public boolean hasStableIds() {
return false;
}
#Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return false;
}
public SparseArray<SparseArray<Survey>> getResults() {
return results;
}
public void emptyResults() {
for(int i=0; i<results.size();i++) {
for(int j=0;j<results.get(i).size();j++) {
results.get(i).remove(j);
}
}
}
public class SurveyViewHolder {
public TextView title;
public RadioGroup rg;
public RatingBar rating;
public CheckBox interest;
public CheckBox privacy;
public Button submit;
public String children;
}
public class CustomOnClickListener implements View.OnClickListener {
private View row;
private SurveyViewHolder svh;
private int group;
private int child;
public CustomOnClickListener(View r, SurveyViewHolder s, int g, int c) {
this.row = r;
this.svh = s;
this.group = g;
this.child = c;
}
#Override
public void onClick(View v) {
boolean addIt = false;
try{
if(results.get(this.group).get(this.child, null) == null) {
addIt = true;
}
} catch(NullPointerException e) {
Log.d(TAG, "NPE");
addIt = true;
}
if (addIt) {
if (svh.rg.getCheckedRadioButtonId() > 0 && svh.rating.getRating() > 0) {
Boolean[] reasons = new Boolean[]{false, false, false};
// Hide the view
this.row.setVisibility(View.INVISIBLE);
// Start saving the record
Survey s = new Survey(svh.title.getText().toString());
s.setRating(svh.rating.getRating());
if (svh.privacy.isChecked()) {
reasons[0] = true;
} else if (svh.interest.isChecked()) {
reasons[1] = true;
}
s.setReason(reasons);
results.get(this.group).put(this.child, s);
Toast.makeText(appContext, "Record Saved", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(appContext, "You must give a rating and shared status", Toast.LENGTH_SHORT).show();
}
} else {
Log.d(TAG, "Record already saved");
this.row.setVisibility(View.INVISIBLE);
}
}
}
}
Thank you for any help, I'm really struggling with this problem!
You set visibility to row in CustomOnClickListener and then reuse this invisible View for new items.
Suppose you pressed Save on second item. It is invisible now. Then if you scroll the second item's view would be reused for new item. Let say that it would be the item 5. Of course you see that 5'th item is hidden, because it is the same View as it was for second item.
Try to update visibility of row inside getChildView() in the same place where you update titles.
Related
Hey Guys I'm new to androidm, and i stucked a bit.
I want to create a Timetable with an expandable listview, with default items(eg. Subject1, Subject2, Room1,Room2...). I want to 'edit' the expandable list child on a long press.
This is my activity
public class TuesdayActivity extends AppCompatActivity {
String[] myArray = new String[]{"Subject1", "Subject2", "Subject3", "Subject4", "Subject5", "Subject6"};
public void fillData()
{
times = new ArrayList<>();
info = new HashMap<String, List<String>>();
times.add("8:00-10:00");
times.add("10:00-12:00");
times.add("12:00-14:00");
times.add("14:00-16:00");
times.add("16:00-18:00");
times.add("18:00-20:00");
List<String> from_8 = new ArrayList<>();
List<String> from_10 = new ArrayList<>();
List<String> from_12 = new ArrayList<>();
List<String> from_14 = new ArrayList<>();
List<String> from_16 = new ArrayList<>();
List<String> from_18 = new ArrayList<>();
from_8.add(myArray[0]);
from_8.add("Room1");
from_10.add("Subject2");
from_10.add("Room2");
from_12.add("Subject3");
from_12.add("Room3");
from_14.add("Subject4");
from_14.add("Room4");
from_16.add("Subject5");
from_16.add("Room5");
from_18.add("Subject6");
from_18.add("Room7");
info.put(times.get(0),from_8);
info.put(times.get(1),from_10);
info.put(times.get(2),from_12);
info.put(times.get(3),from_14);
info.put(times.get(4),from_16);
info.put(times.get(5),from_18);
}
ExpandableListView expandableListView;
List<String > times;
Map<String,List<String>> info;
ExpandableListAdapter listAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tuesday);
expandableListView = (ExpandableListView) findViewById(R.id.exList);
fillData();
listAdapter = new MyExListAdapter(this,times,info);
expandableListView.setAdapter(listAdapter);
expandableListView.setOnGroupExpandListener(new ExpandableListView.OnGroupExpandListener() {
#Override
public void onGroupExpand(int groupPosition) {
Toast.makeText(getBaseContext(),times.get(groupPosition)+ " is expanded",Toast.LENGTH_SHORT).show();
}
});
expandableListView.setOnGroupCollapseListener(new ExpandableListView.OnGroupCollapseListener() {
#Override
public void onGroupCollapse(int groupPosition) {
Toast.makeText(getBaseContext(),times.get(groupPosition)+ " is collapsed",Toast.LENGTH_SHORT).show();
}
});
expandableListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
if (ExpandableListView.getPackedPositionType(id) == ExpandableListView.PACKED_POSITION_TYPE_CHILD) {
int groupPosition = ExpandableListView.getPackedPositionGroup(id);
int childPosition = ExpandableListView.getPackedPositionChild(id);
if(childPosition == 0)
{
myArray[0] = "Trying to change text";
}
return true;
}
return false;
}
});
}
}
Here is my Adapter
public class MyExListAdapter extends BaseExpandableListAdapter {
Context context;
List<String> times;
Map<String,List<String>> info;
public MyExListAdapter(Context context, List<String> times, Map<String, List<String>> info) {
this.context = context;
this.times = times;
this.info = info;
}
#Override
public int getGroupCount() {
return times.size();
}
#Override
public int getChildrenCount(int groupPosition) {
return info.get(times.get(groupPosition)).size();
}
#Override
public Object getGroup(int groupPosition) {
return times.get(groupPosition);
}
#Override
public Object getChild(int groupPosition, int childPosition) {
return info.get(times.get(groupPosition)).get(childPosition);
}
#Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
#Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
#Override
public boolean hasStableIds() {
return false;
}
#Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
String times = (String ) getGroup(groupPosition);
if(convertView == null)
{
LayoutInflater inflater = (LayoutInflater) context.getSystemService(context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.list_parent,null);
}
TextView txtParent = (TextView) convertView.findViewById(R.id.txtParent);
txtParent.setText(times);
return convertView;
}
#Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
String info = (String) getChild(groupPosition,childPosition);
if(convertView == null)
{
LayoutInflater inflater = (LayoutInflater) context.getSystemService(context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.list_child,null);
}
TextView txtChild = (TextView) convertView.findViewById(R.id.txtChild);
txtChild.setText(info);
notifyDataSetChanged();
return convertView;
}
#Override
public boolean isChildSelectable(int i, int i1) {
return true;
}
}
Firstly i wanted to edit resources/string on a long press, but after a short reading, i got to know that is not possible in runtime.
My questions are:
How can I update the listitems if i change the value of an array?
(I can't really understand notifyDataSetChanged(); )
Are there any other way to do this?
If you know a solution please help me.
Thanks,
M. Tailor
How I normally update listViews is with a custom method. So don't update info directly, rather update it with a method that also sets the adapter for listView
void updateMyListView(foo, bar){
updateInfoMap();
expandableListView.setAdapter(listAdapter);
I have one ParentActivity which has TextView tvCount, one fragment which has expandable list view. I need to update the number of children count selected in the expandable list view.
In my fragment i have an adapter -
mExpandableListView = (ExpandableListView) view.findViewById(R.id.lvExp);
mAdapter = new ExpandableServiceListViewAdapter(getActivity(),
mGroups);
ExpandableServiceListViewAdapter.java
public class ExpandableServiceListViewAdapter extends BaseExpandableListAdapter {
private static SparseArray<GroupModel> mGroups;
public LayoutInflater mInflater;
public Activity mActivity;
public ExpandableServiceListViewAdapter(Activity act, SparseArray<GroupModel> groups) {
mActivity = act;
this.mGroups = groups;
mInflater = act.getLayoutInflater();
}
#Override
public Object getChild(int groupPosition, int childPosition) {
return mGroups.get(groupPosition).children.get(childPosition);
}
#Override
public long getChildId(int groupPosition, int childPosition) {
return 0;
}
#Override
public View getChildView(final int groupPosition, final int childPosition,
boolean isLastChild, View convertView, ViewGroup parent) {
final GroupModel children = (GroupModel) getChild(groupPosition, childPosition);
TextView title, duration, gender, actualprice, finalprice;
final TextView tvCounter, tvTotalCost;
ImageView offericon = null;
final CheckBox checkBox;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.services_item, null);
}
title = (TextView) convertView.findViewById(R.id.tv_title);
duration = (TextView) convertView.findViewById(R.id.tv_duration);
gender = (TextView) convertView.findViewById(R.id.tv_gender);
actualprice = (TextView) convertView.findViewById(R.id.tv_actualprice);
finalprice = (TextView) convertView.findViewById(R.id.tv_finalprice);
offericon = (ImageView) convertView.findViewById(R.id.iv_offer);
checkBox = (CheckBox) convertView.findViewById(R.id.cb_check);
tvTotalCost = (TextView) mActivity.findViewById(R.id.tv_cost);
tvCounter = (TextView) mActivity.findViewById(R.id.tv_counter);
title.setText(children.getmServiceModel().getmServiceTitle());
duration.setText("• " + children.getmServiceModel().getmDuration() + "min");
gender.setText("• " + children.getmServiceModel().getmGender());
actualprice.setText("Rs " + children.getmServiceModel().getmActualPrice());
actualprice.setPaintFlags(actualprice.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
finalprice.setText("\u20B9" + children.getmServiceModel().getmFinalPrice());
if (children.getmServiceModel().getmHasOffer()) {
offericon.setVisibility(View.VISIBLE);
} else {
offericon.setVisibility(View.INVISIBLE);
}
convertView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
checkBox.toggle();
if (checkBox.isChecked()) {
((BaseActivity) mActivity).showToast("Yes");
BusinessActivity.mSelectedMap.put((groupPosition * 1000) + childPosition, children);
} else {
((BaseActivity) mActivity).showToast("No");
BusinessActivity.mSelectedMap.remove((groupPosition * 1000) + childPosition);
}
}
});
return convertView;
}
#Override
public int getChildrenCount(int groupPosition) {
return mGroups.get(groupPosition).children.size();
}
#Override
public Object getGroup(int groupPosition) {
return mGroups.get(groupPosition);
}
#Override
public int getGroupCount() {
return mGroups.size();
}
#Override
public void onGroupCollapsed(int groupPosition) {
super.onGroupCollapsed(groupPosition);
}
#Override
public void onGroupExpanded(int groupPosition) {
super.onGroupExpanded(groupPosition);
}
#Override
public long getGroupId(int groupPosition) {
return 0;
}
#Override
public View getGroupView(int groupPosition, boolean isExpanded,
View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(R.layout.services_group_header, null);
}
GroupModel group = (GroupModel) getGroup(groupPosition);
TextView title = (TextView) convertView.findViewById(R.id.tv_title);
title.setText(group.getmTitle());
TextView count = (TextView) convertView.findViewById(R.id.tv_count);
count.setText("(" + group.getmCount() + ")");
ImageView iv = (ImageView) convertView.findViewById(R.id.iv_offer);
if (group.getmSubCategory().getmHasOffers())
iv.setVisibility(View.VISIBLE);
else
iv.setVisibility(View.INVISIBLE);
return convertView;
}
#Override
public boolean hasStableIds() {
return false;
}
#Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return false;
}
}
Here i am trying to create a map of selected children which i need to send it to the parent activity to update the count.
BusinessActivity.java is the parent activity which has a fragment.
I have defined mSelectedMap in the activity.I am adding the entry to this map when selected and removing when unselected.
Can someone help me how do i set the count value which is in the parent activity from this adapter.
There are 3 ways to solve your problem.
Use global static variable, define its value in your fragment and display its value in your activity.
Create method in your activity and call it from fragment with appropriate parameters.
Use EventBus.
EventBus simplifies the communication between components. It performs well with Activities, Fragments, and background threads.
Here is nice example of EventBus.
Try this...
ExpandableServiceListViewAdapter.java
public class ExpandableServiceListViewAdapter extends BaseExpandableListAdapter {
private SparseArray<GroupModel> mGroups;
public LayoutInflater mInflater;
public Activity mActivity;
private OnChildClick onChildClick;
public ExpandableServiceListViewAdapter(Activity act, SparseArray<GroupModel> groups,
OnChildClick onChildClick) {
mActivity = act;
this.mGroups = groups;
this.onChildClick = onChildClick;
mInflater = act.getLayoutInflater();
}
#Override
public View getChildView(final int groupPosition, final int childPosition,
boolean isLastChild, View convertView, ViewGroup parent) {
final GroupModel children = (GroupModel) getChild(groupPosition, childPosition);
........
convertView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
checkBox.toggle();
if (checkBox.isChecked()) {
if (null != onChildClick) {
onChildClick.onClick((groupPosition * 1000) + childPosition, children, "Yes");
}
} else {
if (null != onChildClick) {
onChildClick.onClick((groupPosition * 1000) + childPosition, children, "No");
}
}
}
});
return convertView;
}
//other override methods
......
//interface to get selected item
public interface OnChildClick {
void onClick(int position, GroupModel children, String status);
}
}
YourFragment.java
public class YourFragment extends Fragment{
private OnItemSelectedListener mCallback;
private Activity activity;
ExpandableServiceListViewAdapter mAdapter;
ExpandableListView mExpandableListView;
.....
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
mCallback = (OnItemSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnItemSelectedListener");
}
}
// interface to update UI changes in BusinessActivity at runtime
public interface OnItemSelectedListener {
void onItemPicked(int position,GroupModel children, String status);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_expandable_list, container, false);
mExpandableListView = (ExpandableListView) view.findViewById(R.id.lvExp);
return view;
}
#Override
public void onActivityCreated(Bundle savedInstanceState){
super.onActivityCreated(savedInstanceState);
mAdapter = new ExpandableServiceListViewAdapter(getActivity(),
mGroup, mOnChildClick);
}
private ExpandableServiceListViewAdapter.OnChildClick mOnChildClick
= new ExpandableServiceListViewAdapter.OnChildClick() {
#Override
public void onClick(int position,GroupModel children, String status) {
// Through interface
try {
((OnItemSelectedListener) activity).onItemPicked(position, children, status);
} catch (Exception cce) {
cce.printStackTrace();
}
//OR accsee directly
if(status.equalsIgnoreCase("Yes")){
((BusinessActivity)activity).mSelectedMap.put(position, children);
}else {
((BusinessActivity)activity).mSelectedMap.remove(position);
}
}
};
}
BusinessActivity.java
public class BusinessActivity extends Activity implements YourFragment.OnItemSelectedListener
{
.....
//Override the method here
#Override
public void onItemPicked(position, children, status)
{
//Do something with the position value passed back
Toast.makeText(activity, "status "+ status, Toast.LENGTH_LONG).show();
//Do something with the position value passed back
Toast.makeText(activity, "status "+ status, Toast.LENGTH_LONG).show();
if(status.equalsIgnoreCase("Yes")){
mSelectedMap.put(position, children)
}else {
mSelectedMap.remove(position);
}
}
.....
}
I have a multi level (3 levels, Root -> Parent -> Child) ExpandableListView containing children that are also ExpandableListViews.
I'm having no issue filling them up; however, I need to expand a specific item in the Parent level when I first display the Activity (onCreate).
I successfully expand the related Root item of the Parent but I can't seem to expand the Parent item. The given listeners are called and yet the result isn't reflected in my multi level list.
Activity in which I call the expansion:
public class Activity {
private int groupToExpand = 4, childToExpand = 3;
protected void onCreate(Bundle savedInstance) {
final ExpandableListView elv = (ExpandableListView) findViewById(R.id.elv);
if (arrayList!= null && !arrayList.isEmpty()) {
elv.setAdapter(new RootAdapter(this, arrayList);
// this selects the correct group, but doesn't expand the child.
elv.setSelectedChild(groupToExpand, childToExpand, true);
elv.expandGroup(groupToExpand); // this works.
}
}
}
My Root adapter:
public class RootAdapter extends BaseExpandableListAdapter {
private List<Objects> arrayList;
private Context context;
private LayoutInflater inflater;
public RootAdapter(Context context, List<Objects> arrayList) {
this.context = context;
this.arrayList = arrayList;
this.inflater = LayoutInflater.from(context);
}
#Override
public Object getChild(int groupPosition, int childPosition) {
final Objects parent = (Objects) getGroup(groupPosition);
return parent.arrayList.get(childPosition);
}
#Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
#Override
public View getChildView(int groupPosition, int childPosition,
boolean isLastChild, View convertView, ViewGroup parent) {
final Objects o = (Objects) getChild(groupPosition, childPosition);
CustomExpandableListView elv = (CustomExpandableListView) convertView;
ChildViewHolder holder;
if (elv == null) {
holder = new ChildViewHolder();
elv = new CustomExpandableListView(context);
elv.setGroupIndicator(null);
elv.setDivider(null);
elv.setCacheColorHint(Color.parseColor("#00000000"));
elv.setChildDivider(null);
elv.setChildIndicator(null);
elv.setScrollingCacheEnabled(false);
elv.setAnimationCacheEnabled(false);
holder.cListView = elv;
elv.setTag(holder);
} else {
holder = (ChildViewHolder) elv.getTag();
}
final ParentAdapter adapter = new ParentAdapter(context, o);
holder.cListView.setAdapter(adapter);
return elv;
}
private static class ChildViewHolder {
CustomExpandableListView cListView;
}
#Override
public int getChildrenCount(int groupPosition) {
final Objects parent = (Objects) getGroup(groupPosition);
return parent.arrayList.size();
}
#Override
public Object getGroup(int groupPosition) {
return arrayList.get(groupPosition);
}
#Override
public int getGroupCount() {
return arrayList.size();
}
#Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
#Override
public View getGroupView(int groupPosition, boolean isExpanded,
View convertView, ViewGroup parent) {
View layout = convertView;
GroupViewHolder holder;
final Objects o = (Objects) getGroup(groupPosition);
if (layout == null) {
layout = inflater.inflate(R.layout.item_to_inflate, parent, false);
holder = new GroupViewHolder();
holder.title = (TextView) layout.findViewById(R.id.title);
holder.image = (ImageView) layout.findViewById(R.id.image);
layout.setTag(holder);
} else {
holder = (GroupViewHolder) layout.getTag();
}
holder.title.setText(o.title.trim());
return layout;
}
#Override
public boolean hasStableIds() {
return true;
}
#Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
private static class GroupViewHolder {
TextView title;
ImageView image;
}
public class CustomExpandableListView extends ExpandableListView {
public CustomExpandableListView(Context context) {
super(context);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
heightMeasureSpec = MeasureSpec.makeMeasureSpec(2000, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
}
Finally my ParentAdapter :
public class ParentAdapter extends BaseExpandableListAdapter {
private Objects child;
private LayoutInflater inflater;
public ParentAdapter(Context context, Objects child) {
this.child = child;
this.inflater = LayoutInflater.from(context);
}
#Override
public Object getChild(int groupPosition, int childPosition) {
return child.arrayList.get(childPosition);
}
#Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
#Override
public View getChildView(int groupPosition, int childPosition,
boolean isLastChild, View convertView, ViewGroup parent) {
View layout = convertView;
final Objects o = (Objects) getChild(0, childPosition);
ChildViewHolder holder;
if (layout == null) {
layout = inflater.inflate(R.layout.item_to_inflate, parent, false);
holder = new ChildViewHolder();
holder.title = (TextView) layout.findViewById(R.id.title);
layout.setTag(holder);
} else {
holder = (ChildViewHolder) layout.getTag();
}
holder.title.setText(o.title.trim());
return layout;
}
#Override
public int getChildrenCount(int groupPosition) {
return child.arrayList.size();
}
#Override
public Object getGroup(int groupPosition) {
return child;
}
#Override
public int getGroupCount() {
return 1;
}
#Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
#Override
public View getGroupView(int groupPosition, boolean isExpanded,
View convertView, ViewGroup parent) {
View layout = convertView;
GroupViewHolder holder;
if (layout == null) {
layout = inflater.inflate(R.layout.item_to_inflate, parent, false);
holder = new GroupViewHolder();
holder.image = (ImageView) layout.findViewById(R.id.image);
holder.title = (TextView) layout.findViewById(R.id.title);
layout.setTag(holder);
} else {
holder = (GroupViewHolder) layout.getTag();
}
holder.title.setText(o.title.trim());
return layout;
}
#Override
public boolean hasStableIds() {
return true;
}
#Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
private static class GroupViewHolder {
TextView title;
ImageView image;
}
private static class ChildViewHolder {
TextView title;
}
}
I can't expand the child lists within the root ExpandableListView; do you know any correct way to expand the items at the Parent level?
I tried in getChildView of the RootAdapter :
if (groupToExpand == groupPosition && childToExpand == childPosition) {
elv.expandGroup(childToExpand);
}
Then in the activity i changed:
if (arrayList!= null && !arrayList.isEmpty()) {
RootAdapter adapter = new RootAdapter(this, arrayList);
elv.setAdapter(adapter);
// this selects the correct group, but doesn't expand the child.
elv.setSelectedChild(groupToExpand, childToExpand, true);
elv.expandGroup(groupToExpand); // this works.
adapter.groupToExpand = groupToExpand;
adapter.childToExpand = childToExpand;
adapter.notifyDataSetChanged();
}
This expands the Parent level item, BUT, it generates duplicate items of the Parent. How do I do this correctly? Is this the correct way but my adapter is broken, therefore generating duplicate items?
I just can't find what I'm missing here...
I ended up using a multi level ExpandableListView adapter from another post in stackoverflow. Unfortunately I just can't find the link anymore so I'll just post my code here.
Top Level of the ExpandableListView adapter.
RootAdapter.java:
public class RootAdapter extends BaseExpandableListAdapter {
private Object root;
private final LayoutInflater inflater;
public class Entry {
public final CustExpListview cls;
public final SecondLevelAdapter sadpt;
public Entry(CustExpListview cls, SecondLevelAdapter sadpt) {
this.cls = cls;
this.sadpt = sadpt;
}
}
public Entry[] lsfirst;
// you can change the constructor depending on which listeners you wan't to use.
public RootAdapter(Context context, Object root, ExpandableListView.OnGroupClickListener grpLst,
ExpandableListView.OnChildClickListener childLst, ExpandableListView.OnGroupExpandListener grpExpLst) {
this.root = root;
this.inflater = LayoutInflater.from(context);
lsfirst = new Entry[root.children.size()];
for (int i = 0; i < root.children.size(); i++) {
final CustExpListview celv = new CustExpListview(context);
SecondLevelAdapter adp = new SecondLevelAdapter(root.children.get(i));
celv.setAdapter(adp);
celv.setGroupIndicator(null);
celv.setOnChildClickListener(childLst);
celv.setOnGroupClickListener(grpLst);
celv.setOnGroupExpandListener(grpExpLst);
lsfirst[i] = new Entry(celv, adp);
}
}
#Override
public Object getChild(int groupPosition, int childPosition) {
return childPosition;
}
#Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
#Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
View convertView, ViewGroup parent) {
// second level list
return lsfirst[groupPosition].cls;
}
#Override
public int getChildrenCount(int groupPosition) {
return 1;
}
#Override
public Object getGroup(int groupPosition) {
return root.children.get(groupPosition);
}
#Override
public int getGroupCount() {
return root.children.size();
}
#Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
#Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
ViewGroup parent) {
// first level
View layout = convertView;
GroupViewHolder holder;
final Object item = (Object) getGroup(groupPosition);
if (layout == null) {
layout = inflater.inflate(R.layout.item_root, parent, false);
holder = new GroupViewHolder();
holder.title = (TextView) layout.findViewById(R.id.itemRootTitle);
layout.setTag(holder);
} else {
holder = (GroupViewHolder) layout.getTag();
}
holder.title.setText(item.title.trim());
return layout;
}
private static class GroupViewHolder {
TextView title;
}
#Override
public boolean hasStableIds() {
return true;
}
#Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
}
Custom ExpandableListView with the solution to items sometimes not being displayed. There is also a workaround to a known bug for IllegalArgumentException being called when destroying the view (by pressing the back button essentially).
CustExpListview.java:
public class CustExpListview extends ExpandableListView {
public CustExpListview(Context context) {
super(context);
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// the value (2000) should not be fixed and be calculated
// as follows: cell_height x root_items_count x root_items_children_count
heightMeasureSpec = MeasureSpec.makeMeasureSpec(2000, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
#Override
protected void onDetachedFromWindow() {
try {
super.onDetachedFromWindow();
} catch (IllegalArgumentException e) {
// TODO: Workaround for http://code.google.com/p/android/issues/detail?id=22751
}
}
}
Finally, the code to the last level adapter.
public class SecondLevelAdapter extends BaseExpandableListAdapter {
public Object child;
public SecondLevelAdapter(Object child) {
this.child = child;
}
#Override
public Object getChild(int groupPosition, int childPosition) {
return child.children.get(groupPosition).children.get(childPosition);
}
#Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
// third level
#Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
View convertView, ViewGroup parent) {
View layout = convertView;
final Object item = (Object) getChild(groupPosition, childPosition);
ChildViewHolder holder;
if (layout == null) {
layout = inflater.inflate(R.layout.item_child, parent, false);
holder = new ChildViewHolder();
holder.title = (TextView) layout.findViewById(R.id.itemChildTitle);
layout.setTag(holder);
} else {
holder = (ChildViewHolder) layout.getTag();
}
holder.title.setText(item.title.trim());
return layout;
}
#Override
public int getChildrenCount(int groupPosition) {
return child.children.get(groupPosition).children.size();
}
#Override
public Object getGroup(int groupPosition) {
return child.children.get(groupPosition);
}
#Override
public int getGroupCount() {
return child.children.size();
}
#Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
// Second level
#Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
ViewGroup parent) {
View layout = convertView;
ViewHolder holder;
final Object item = (Object) getGroup(groupPosition);
if (layout == null) {
layout = inflater.inflate(R.layout.item_parent, parent, false);
holder = new ViewHolder();
holder.title = (TextView) layout.findViewById(R.id.itemParentTitle);
layout.setTag(holder);
} else {
holder = (ViewHolder) layout.getTag();
}
holder.title.setText(item.title.trim());
return layout;
}
#Override
public void registerDataSetObserver(DataSetObserver observer) {
super.registerDataSetObserver(observer);
}
#Override
public void unregisterDataSetObserver(DataSetObserver observer) {
Log.d("SecondLevelAdapter", "Unregistering observer");
if (observer != null) {
super.unregisterDataSetObserver(observer);
}
}
#Override
public boolean hasStableIds() {
return true;
}
#Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
private static class ViewHolder {
TextView title;
}
private static class ChildViewHolder {
TextView title;
}
}
I hope this helps!!!
Update
This is an example of how I call the above adapter.
MainActivity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final List<Object> objects = yourItems;
if (!objects.isEmpty()) {
final ExpandableListView elv = (ExpandableListView) findViewById(R.id.yourExpandableListView);
/* Item click listeners below */
// First level items in the ExpandableListView
elv.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
#Override
public boolean onGroupClick(ExpandableListView eListView, View view, int groupPosition,
long id) {
// TODO: whatever you need
return false /* or true depending on what you need */;
}
});
// Second level items in the ExpandableListView
ExpandableListView.OnGroupClickListener grpLst = new ExpandableListView.OnGroupClickListener() {
#Override
public boolean onGroupClick(ExpandableListView eListView, View view, int groupPosition,
long id) {
// TODO: whatever you need
return false /* or true depending on what you need */;
}
};
// Third (and last) level items in the ExpandableListView
ExpandableListView.OnChildClickListener childLst = new ExpandableListView.OnChildClickListener() {
#Override
public boolean onChildClick(ExpandableListView eListView, View view, int groupPosition,
int childPosition, long id) {
// TODO: whatever you need
return false /* or true depending on what you need */;
}
};
ExpandableListView.OnGroupExpandListener grpExpLst = new ExpandableListView.OnGroupExpandListener() {
#Override
public void onGroupExpand(int groupPosition) {
/* this one is not required of course, you can delete it from the RootAdapter Constructor
* it is just an example as to how to implement Listeners on the second level items */
}
};
final RootAdapter adapter = new RootAdapter(this, objects, grpLst, childLst, grpExpLst);
elv.setAdapter(adapter);
}
}
This is how my class Object would look like.
Object.java:
public class Object {
public String title; // use getters and setters instead
public List<Object> children; // same as above
public Object() {
children = new ArrayList<Object>();
}
}
This is the best example I have found and used also:
http://androidcodesnips.blogspot.in/2011/09/three-level-expandable-list.html
Its working, I have changed it as my need.
tags: 3 level expandablelistview, multi level listview
I am developing three level expandable listview in android. but my child nodes are populating in duplicate order. Here is my source code:
public class CarPanel extends ExpandableListActivity {
static ArrayList<String> groupItem = new ArrayList<String>();
static ArrayList<String> childItem = new ArrayList<String>();
static ArrayList<Object> grandChildItem = new ArrayList<Object>();
static public ArrayList<String> childValue;
static public ArrayList<String> grandChildValue;
EditText modelType, price, dyp, insurance;
static int dummyFlag = 0;
public LayoutInflater minflater;
public Activity activity;
private MyDBManager dbManager;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Remove title bar
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
//Remove notification bar
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
dbManager = new MyDBManager(this);
dbManager.open();
ExpandableListView expandbleLis = getExpandableListView();
expandbleLis.setDividerHeight(2);
expandbleLis.setGroupIndicator(null);
expandbleLis.setClickable(true);
// 1st level data
setGroupData();
// 2nd level data
setChildGroupData();
// 3rd level data
setGrandChildGroupData();
ParentLevel parentAdapter = new ParentLevel(groupItem);
parentAdapter.setInflater((LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE), this);
getExpandableListView().setAdapter(parentAdapter);
expandbleLis.setOnChildClickListener(this);
}
public class ParentLevel extends BaseExpandableListAdapter {
public ParentLevel(ArrayList<String> groupList) {
groupItem = groupList;
}
public Object getChild(int arg0, int arg1) {
return arg1;
}
public void setInflater(LayoutInflater mInflater, Activity act) {
minflater = mInflater;
activity = act;
}
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
CustExpListview SecondLevelexplv = new CustExpListview(CarPanel.this);
SecondLevelexplv.setAdapter(new SecondLevelAdapter(childItem, grandChildItem ));
SecondLevelexplv.setGroupIndicator(null);
return SecondLevelexplv;
}
public int getChildrenCount(int groupPosition) {
Log.v("childItem.size()", "childItem.size() " +childItem.size());
return childItem.size();
}
public Object getGroup(int groupPosition) {
return groupPosition;
}
public int getGroupCount() {
Log.v("groupItem.size()", "groupItem.size() " +groupItem.size());
return groupItem.size();
}
public long getGroupId(int groupPosition) {
return groupPosition;
}
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = minflater.inflate(R.layout.grouprow, null);
}
((CheckedTextView) convertView).setText(groupItem.get(groupPosition));
((CheckedTextView) convertView).setChecked(isExpanded);
return convertView;
}
public boolean hasStableIds() {
return true;
}
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
}
class CustExpListview extends ExpandableListView {
int intGroupPosition, intChildPosition, intGroupid;
public CustExpListview(Context context) {
super(context);
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
widthMeasureSpec = MeasureSpec.makeMeasureSpec(960, MeasureSpec.AT_MOST);
heightMeasureSpec = MeasureSpec.makeMeasureSpec(600,MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
class SecondLevelAdapter extends BaseExpandableListAdapter {
public LayoutInflater minflater;
public Context activity;
public SecondLevelAdapter(ArrayList<String> childList, ArrayList<Object> grandChildList) {
childItem = childList;
grandChildItem = grandChildList;
}
public void setInflater(LayoutInflater mInflater, Context parentLevel) {
minflater = mInflater;
activity = parentLevel;
}
public Object getChild(int groupPosition, int childPosition) {
return childPosition;
}
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
#SuppressWarnings("unchecked")
public View getChildView(int groupPosition, final int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
grandChildValue = (ArrayList<String>) grandChildItem.get(groupPosition);
EditText modelType, price, dyp, insurance;
if (convertView == null) {
LayoutInflater layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = layoutInflater.inflate(R.layout.grandchildrow, null);
}
modelType = (EditText) convertView.findViewById(R.id.et_ModelType);
price = (EditText) convertView.findViewById(R.id.et_Price);
dyp = (EditText) convertView.findViewById(R.id.et_DYP);
insurance = (EditText) convertView.findViewById(R.id.et_Insurance);
modelType.setText(grandChildValue.get(childPosition));
convertView.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Toast.makeText(getBaseContext(), grandChildValue.get(childPosition), Toast.LENGTH_SHORT).show();
}
});
return convertView;
}
public int getChildrenCount(int groupPosition) {
return ((ArrayList<String>) grandChildItem.get(groupPosition)).size();
}
public Object getGroup(int groupPosition) {
return groupPosition;
}
public int getGroupCount() {
return childItem.size();
}
public long getGroupId(int groupPosition) {
return groupPosition;
}
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = layoutInflater.inflate(R.layout.childrow, null);
}
((CheckedTextView) convertView).setText(childItem.get(groupPosition));
((CheckedTextView) convertView).setChecked(isExpanded);
return convertView;
}
public boolean hasStableIds() {
return true;
}
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
}
public void setGroupData() {
groupItem.clear();
Cursor cursor = dbManager.execQuery("SELECT DISTINCT carName FROM model");
final List<ModelBean> modelList = new ArrayList<ModelBean>();
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
ModelBean beanObj = cursorToModel(cursor);
modelList.add(beanObj);
cursor.moveToNext();
}
// Make sure to close the cursor
cursor.close();
ModelBean modelObj = null;
if (modelList.size() > 0 )
{
for (int i = 0; i < modelList.size(); i++)
{
modelObj = modelList.get(i);
String carName = modelObj.getCarName();
groupItem.add(carName);
}
}
}
public void setChildGroupData() {
childItem.clear();
childItem.add("Android");
childItem.add("iOS");
childItem.add("HTC");
}
public void setGrandChildGroupData() {
grandChildItem.clear();
ArrayList<String> child = new ArrayList<String>();
child.add("PASSAT 1");
child.add("PASSAT 2");
grandChildItem.add(child);
child = new ArrayList<String>();
child.add("POLO 1");
child.add("POLO 2");
grandChildItem.add(child);
child = new ArrayList<String>();
child.add("VITZ 1");
child.add("VITZ 2");
grandChildItem.add(child);
}
private ModelBean cursorToModel(Cursor cursor)
{
ModelBean modelBean = new ModelBean();
modelBean.setCarName(cursor.getString(0));
return modelBean;
}
}
Now I want this code of block to repeat only once for each parent group however it repeat three times. I have searched much more and similar SO questions but did not came into solution.
Android
iOS
HTC
I struggled with this same problem and could not find the solution anywhere online/SO.
After doing some logging I came to the conclusion that the parent level BaseExpandbleAdapater will call/create a childView corresponding to the number of children in your dataset/group. Unfortunately this is not desired in a three-level nested list scenario.
So when generating a Three-level list, simply make the function getChildrenCount in the parent adapter like this:
#Override
public int getChildrenCount(int groupPosition){
return 1;
}
Now it will only build "1" iteration of children list items, even though the 1 nested list can contain any number of children, which from the view point of the child adapater then becomes the group.
It is confusing because your child/nested adapter handles displaying the actual "children", whereas in a single nested list, you children are handled within that same parent adapter class. In this scenario the child/nested adapter is going to handle displaying what would be the children as its own groupView, making the number of children in the parent adapter irrelevant. At least that is what my results and environment are showing me...
Hope I could be of help, this had me stumped for a good half a day.
I can't get a custom layout in expandablelistview to work, that refreshes on a click of a child child.
In the example you have the method MyExpandableListAdapter -> getChildView, where the child inflates a custom layout R.layout.list_out_item (what is some textviews and buttons).
Now I want that if I click a button that the child and parent of that childview is updated. How can I achieve this? I was thinking about refreshing the parent and child, because collapsing forces an update, but other solutions are also welcome.
For an example
public class Sample extends ExpandableListActivity {
ExpandableListAdapter mAdapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set up our adapter
mAdapter = new MyExpandableListAdapter();
setListAdapter(mAdapter);
}
}
public class MyExpandableListAdapter extends BaseExpandableListAdapter {
private Map<Ability, Integer> abilityMods;
private List<Skill> skills = SkillList();
private List<Skill> SkillList() {
List<Skill> skills = new Vector<Skill>();
abilityMods = Player.getAbilityModifier();
skills.add(new Skill(abilityMods, "Appraise", 2, Ability.Dex, 2));
skills.add(new Skill(abilityMods, "Autohypnosis", 0, Ability.Int,
-7));
skills.add(new Skill(abilityMods, "Craft", 0, Ability.Dex, 0));
return skills;
}
public MyExpandableListAdapter() {
super();
abilityMods = Player.getAbilityModifier();
}
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
public int getChildrenCount(int groupPosition) {
return 1;
}
public View getChildView(int groupPosition, int childPosition,
boolean isLastChild, View convertView, final ViewGroup parent) {
LayoutInflater infalInflater = getLayoutInflater();
convertView = infalInflater.inflate(R.layout.list_out_item, null);
final Skill skill = getChild(groupPosition, childPosition);
TextView tv = (TextView) convertView.findViewById(R.id.abilityMod);
tv.setText(toString(skill.abillityMod()));
Button rankMod = (Button) convertView.findViewById(R.id.rankMod);
rankMod.setText(toString(skill.Ranks));
rankMod.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
skill.Ranks++;
}
});
return convertView;
}
private String toString(int value) {
if (value >= 0)
return ("+" + String.valueOf(value));
else
return (String.valueOf(value));
}
public Skill getGroup(int groupPosition) {
return skills.get(groupPosition);
}
#Override
public Skill getChild(int groupPosition, int childPosition) {
return getGroup(groupPosition);
}
public int getGroupCount() {
return skills.size();
}
public long getGroupId(int groupPosition) {
return groupPosition;
}
public View getGroupView(int groupPosition, boolean isExpanded,
View convertView, ViewGroup parent) {
LayoutInflater infalInflater = getLayoutInflater();
convertView = infalInflater
.inflate(R.layout.list_header_item, null);
TextView skillName = (TextView) convertView
.findViewById(R.id.skillName);
Skill skill = getGroup(groupPosition);
TextView skillPower = (TextView) convertView
.findViewById(R.id.skillPower);
int TotalMod = skill.TotalModifier();
if (TotalMod >= 0)
skillPower.setText("+" + String.valueOf(TotalMod));
else
skillPower.setText(String.valueOf(TotalMod));
return convertView;
}
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
public boolean hasStableIds() {
return true;
}
}
See this link for a customized BaseExpandableListViewAdapter. Using a List<View> to storage parent view, when btn clicked, get parent view and operate viewholder.