Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
android:minSdkVersion="8"
android:targetSdkVersion="18" />

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
Expand Down
20 changes: 18 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,23 @@ Here is a video of the example, showcasing the robustness of the widget:

This project is dedicated to <strong>J Withey</strong> for giving me the motivation to write and release this source.

This fork
This fork for performance tuning
==========================

Fixed bug for null-divider if user has not defined in layout-xml for the list.
There can be performance issue using this AnimatedExpandableListView when the child view getting more complex.

For example, when there are 5 children in a group, each child needs at least twice of getting the new child view, one for appending to the dummy view, and another for normal creation of the expanded list.We see this by adding method profiling and using traceview tool in Android SDK.

This perforamnce issue may not be obvious when the child view is simple, but we found it very severe when the layout is complex, and the measure spec for the child view needs set the upper boundary. Getting new child view is just too expense.

The root cause is that convertView is not given during animation, and the same child view needs to get initiated after animation. The LRU cache is used to hold the child to be reused.

The tuning result can be seen from the trace graphs. Before this tuning, a group with 5 child needs 10 times of getting new view. With this view cache, 5 times are just enough to go.

See TRACEVIEW graphs below as a comparison:

Before:
![image](https://github.com/neokidd/AnimatedExpandableListView/blob/master/docs/example_activity_no_cache_view_10times_inflate.png)

After:
![image](https://github.com/neokidd/AnimatedExpandableListView/blob/master/docs/cache_view_5times_inflate.png)
Binary file added docs/cache_view_5times_inflate.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/fix_getChildView_667ms_35percent.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
52 changes: 44 additions & 8 deletions src/com/example/animatedexpandablelistview/MainActivity.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.example.animatedexpandablelistview;

import java.lang.Long;
import java.lang.Override;
import java.util.ArrayList;
import java.util.List;

Expand All @@ -9,6 +11,10 @@
import android.os.Bundle;
import android.app.Activity;
import android.content.Context;
import android.os.Debug;
import android.os.SystemClock;
import android.util.Log;
import android.util.LruCache;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
Expand All @@ -27,6 +33,8 @@ public class MainActivity extends Activity {
private AnimatedExpandableListView listView;
private ExampleAdapter adapter;

private static final String TAG = "idun";

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Expand Down Expand Up @@ -69,13 +77,22 @@ public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition
if (listView.isGroupExpanded(groupPosition)) {
listView.collapseGroupWithAnimation(groupPosition);
} else {
String traceName = "method_trace_idun_" + SystemClock.uptimeMillis();
// Debug.startMethodTracing(traceName);
listView.expandGroupWithAnimation(groupPosition);
}
return true;
}

});
}


@Override
protected void onPause() {
super.onPause();
// Debug.stopMethodTracing();
}

private static class GroupItem {
String title;
Expand All @@ -90,6 +107,7 @@ private static class ChildItem {
private static class ChildHolder {
TextView title;
TextView hint;
int type;
}

private static class GroupHolder {
Expand All @@ -103,9 +121,12 @@ private class ExampleAdapter extends AnimatedExpandableListAdapter {
private LayoutInflater inflater;

private List<GroupItem> items;

private LruCache<Long, View> vcache;

public ExampleAdapter(Context context) {
inflater = LayoutInflater.from(context);
inflater = LayoutInflater.from(context);
vcache = new LruCache<Long, View>(20);
}

public void setData(List<GroupItem> items) {
Expand All @@ -127,18 +148,33 @@ public View getRealChildView(int groupPosition, int childPosition, boolean isLas
ChildHolder holder;
ChildItem item = getChild(groupPosition, childPosition);
if (convertView == null) {
holder = new ChildHolder();
convertView = inflater.inflate(R.layout.list_item, parent, false);
holder.title = (TextView) convertView.findViewById(R.id.textTitle);
holder.hint = (TextView) convertView.findViewById(R.id.textHint);
convertView.setTag(holder);
// when convertView is not given, we try using view from cache.
// Note: check the view type to ensure it's really resuable.
Long key = ((long)groupPosition << 32) + childPosition;
View view = vcache.get(key);
if (view != null && view.getTag() != null) {
holder = (ChildHolder) view.getTag();
if (holder.type == getRealChildType(groupPosition, childPosition) ) {
convertView = view;
Log.d(TAG, "getRealChildView: " + groupPosition + " " + childPosition + " " + "Got Cache!");
}
} else {
holder = new ChildHolder();
convertView = inflater.inflate(R.layout.list_item, parent, false);
holder.title = (TextView) convertView.findViewById(R.id.textTitle);
holder.hint = (TextView) convertView.findViewById(R.id.textHint);
convertView.setTag(holder);
vcache.put(key, convertView);
Log.d(TAG, "getRealChildView: " + groupPosition + " " + childPosition + " " + "No Cache!");
}
} else {
Log.d(TAG, "getRealChildView: " + groupPosition + " " + childPosition + " " + convertView);
holder = (ChildHolder) convertView.getTag();
}

holder.title.setText(item.title);
holder.hint.setText(item.hint);

return convertView;
}

Expand Down