title: Why ListView expand/collapse animation appears much slower in DialogFragment than in Activity
body: <p>Recently, I find out the <code>ListView</code> expand/collapse animation appears much slower in <code>DialogFragment</code> than in <code>Activity</code>.</p>
  
  <p><strong>In <code>DialogFragment</code>, the animation FPS is 10 fps. Each animation takes 100ms.</strong></p>
  
  <p><strong>In <code>Activity</code>, the animation FPS is 50 fps. Each animation takes 20ms.</strong></p>
  
  <p>I took this wonderful open source library as benchmark example : <a href="https://github.com/nhaarman/ListViewAnimations" rel="nofollow noreferrer">https://github.com/nhaarman/ListViewAnimations</a></p>
  
  <p>I made modification on <a href="https://github.com/nhaarman/ListViewAnimations/blob/master/example/src/com/haarman/listviewanimations/itemmanipulationexamples/ItemManipulationsExamplesActivity.java#L51" rel="nofollow noreferrer">https://github.com/nhaarman/ListViewAnimations/blob/master/example/src/com/haarman/listviewanimations/itemmanipulationexamples/ItemManipulationsExamplesActivity.java#L51</a>, to test between performance in <code>DialogFragment</code> and <code>Activity</code>.</p>
  
  <pre><code>    public void onExpandListItemAdapterClicked(View view) {
          // For Activity testing.
          Intent intent = new Intent(this, ExpandableListItemActivity.class);
          startActivity(intent);
  
          // For DialogFragment testing.
          //FragmentManager fm = this.getSupportFragmentManager();
          //DemoDialogFragment demoDialogFragment = new DemoDialogFragment();
          //demoDialogFragment.show(fm, "DemoDialogFragment");        
      }
  </code></pre>
  
  <p>I made a minor modification on <a href="https://github.com/nhaarman/ListViewAnimations/blob/master/example/src/com/haarman/listviewanimations/itemmanipulationexamples/ExpandableListItemActivity.java" rel="nofollow noreferrer">https://github.com/nhaarman/ListViewAnimations/blob/master/example/src/com/haarman/listviewanimations/itemmanipulationexamples/ExpandableListItemActivity.java</a></p>
  
  <pre><code>public class ExpandableListItemActivity extends MyListActivity {
  
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
  
          MyExpandableListItemAdapter myExpandableListItemAdapter = new MyExpandableListItemAdapter(this, getItems());
          //AlphaInAnimationAdapter alphaInAnimationAdapter = new AlphaInAnimationAdapter(myExpandableListItemAdapter);
          //alphaInAnimationAdapter.setAbsListView(getListView());
          getListView().setAdapter(myExpandableListItemAdapter);
  
          Toast.makeText(this, R.string.explainexpand, Toast.LENGTH_LONG).show();
      }
  </code></pre>
  
  <p>I created my own <code>DialogFragment</code> class for testing. Based entirely from <code>ExpandableListItemActivity</code></p>
  
  <pre><code>package com.haarman.listviewanimations.itemmanipulationexamples;
  
  import java.util.ArrayList;
  import java.util.List;
  
  import android.app.AlertDialog;
  import android.app.Dialog;
  import android.content.Context;
  import android.graphics.Bitmap;
  import android.graphics.BitmapFactory;
  import android.os.Bundle;
  import android.support.v4.app.DialogFragment;
  import android.support.v4.util.LruCache;
  import android.view.LayoutInflater;
  import android.view.View;
  import android.view.ViewGroup;
  import android.widget.ImageView;
  import android.widget.ListView;
  import android.widget.TextView;
  import android.widget.Toast;
  
  import com.haarman.listviewanimations.ArrayAdapter;
  import com.haarman.listviewanimations.R;
  import com.haarman.listviewanimations.itemmanipulation.ExpandableListItemAdapter;
  
  public class DemoDialogFragment extends DialogFragment {
  
      private ListView mListView;
  
      @Override
      public Dialog onCreateDialog(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          mListView = new ListView(getActivity());
          mListView.setDivider(null);
  
          MyExpandableListItemAdapter myExpandableListItemAdapter = new MyExpandableListItemAdapter(getActivity(), getItems());
          //AlphaInAnimationAdapter alphaInAnimationAdapter = new AlphaInAnimationAdapter(myExpandableListItemAdapter);
          //alphaInAnimationAdapter.setAbsListView(getListView());
          getListView().setAdapter(myExpandableListItemAdapter);
  
          Toast.makeText(getActivity(), R.string.explainexpand, Toast.LENGTH_LONG).show();
  
          final AlertDialog dialog = new AlertDialog.Builder(this.getActivity())            
          .setView(mListView)
          .create();
  
          return dialog;
      }
  
      public ListView getListView() {
          return mListView;
      }
  
      protected ArrayAdapter&lt;Integer&gt; createListAdapter() {
          return new MyListAdapter(getActivity(), getItems());
      }
  
      public static ArrayList&lt;Integer&gt; getItems() {
          ArrayList&lt;Integer&gt; items = new ArrayList&lt;Integer&gt;();
          for (int i = 0; i &lt; 1000; i++) {
              items.add(i);
          }
          return items;
      }
  
      private static class MyListAdapter extends ArrayAdapter&lt;Integer&gt; {
  
          private Context mContext;
  
          public MyListAdapter(Context context, ArrayList&lt;Integer&gt; items) {
              super(items);
              mContext = context;
          }
  
          @Override
          public long getItemId(int position) {
              return getItem(position).hashCode();
          }
  
          @Override
          public boolean hasStableIds() {
              return true;
          }
  
          @Override
          public View getView(int position, View convertView, ViewGroup parent) {
              TextView tv = (TextView) convertView;
              if (tv == null) {
                  tv = (TextView) LayoutInflater.from(mContext).inflate(R.layout.list_row, parent, false);
              }
              tv.setText("This is row number " + getItem(position));
              return tv;
          }
      }
  
      private static class MyExpandableListItemAdapter extends ExpandableListItemAdapter&lt;Integer&gt; {
  
          private Context mContext;
          private LruCache&lt;Integer, Bitmap&gt; mMemoryCache;
  
          /**
           * Creates a new ExpandableListItemAdapter with the specified list, or an empty list if
           * items == null.
           */
          private MyExpandableListItemAdapter(Context context, List&lt;Integer&gt; items) {
              super(context, R.layout.activity_expandablelistitem_card, R.id.activity_expandablelistitem_card_title, R.id.activity_expandablelistitem_card_content, items);
              mContext = context;
  
              final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
  
              // Use 1/8th of the available memory for this memory cache.
              final int cacheSize = maxMemory;
              mMemoryCache = new LruCache&lt;Integer, Bitmap&gt;(cacheSize) {
                  @Override
                  protected int sizeOf(Integer key, Bitmap bitmap) {
                      // The cache size will be measured in kilobytes rather than
                      // number of items.
                      return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
                  }
              };
          }
  
          @Override
          public View getTitleView(int position, View convertView, ViewGroup parent) {
              TextView tv = (TextView) convertView;
              if (tv == null) {
                  tv = new TextView(mContext);
              }
              tv.setText(mContext.getString(R.string.expandorcollapsecard, getItem(position)));
              return tv;
          }
  
          @Override
          public View getContentView(int position, View convertView, ViewGroup parent) {
              ImageView imageView = (ImageView) convertView;
              if (imageView == null) {
                  imageView = new ImageView(mContext);
                  imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
              }
  
              int imageResId;
              switch (getItem(position) % 5) {
              case 0:
                  imageResId = R.drawable.img_nature1;
                  break;
              case 1:
                  imageResId = R.drawable.img_nature2;
                  break;
              case 2:
                  imageResId = R.drawable.img_nature3;
                  break;
              case 3:
                  imageResId = R.drawable.img_nature4;
                  break;
              default:
                  imageResId = R.drawable.img_nature5;
              }
  
              Bitmap bitmap = getBitmapFromMemCache(imageResId);
              if (bitmap == null) {
                  bitmap = BitmapFactory.decodeResource(mContext.getResources(), imageResId);
                  addBitmapToMemoryCache(imageResId, bitmap);
              }
              imageView.setImageBitmap(bitmap);
  
              return imageView;
          }
  
          private void addBitmapToMemoryCache(int key, Bitmap bitmap) {
              if (getBitmapFromMemCache(key) == null) {
                  mMemoryCache.put(key, bitmap);
              }
          }
  
          private Bitmap getBitmapFromMemCache(int key) {
              return mMemoryCache.get(key);
          }
      }
  
  }
  </code></pre>
  
  <p>Then, I place my benchmark code in this file : <a href="https://github.com/nhaarman/ListViewAnimations/blob/master/library/src/com/haarman/listviewanimations/itemmanipulation/ExpandableListItemAdapter.java#L277" rel="nofollow noreferrer">https://github.com/nhaarman/ListViewAnimations/blob/master/library/src/com/haarman/listviewanimations/itemmanipulation/ExpandableListItemAdapter.java#L277</a></p>
  
  <pre><code>    private ValueAnimator createHeightAnimator(int start, int end) {
          Log.i("CHEOK", start + " -&gt; " + end);
          ValueAnimator animator = ValueAnimator.ofInt(start, end);
          animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
              @Override
              public void onAnimationUpdate(ValueAnimator valueAnimator) {
                  int value = (Integer) valueAnimator.getAnimatedValue();
  
                  ViewGroup.LayoutParams layoutParams = mContentParent.getLayoutParams();
                  layoutParams.height = value;
                  Log.i("CHEOK", "height = " + value);
                  mContentParent.setLayoutParams(layoutParams);
              }
          });
          return animator;
      }
  </code></pre>
  
  <p>Here is the result I'm getting</p>
  
  <pre><code>// Using DialogFragment
  10-12 03:36:05.695: I/CHEOK(29407): height = 0
  10-12 03:36:06.258: I/CHEOK(29407): height = 0
  10-12 03:36:06.347: I/CHEOK(29407): height = 74
  10-12 03:36:06.547: I/CHEOK(29407): height = 358
  10-12 03:36:06.621: I/CHEOK(29407): height = 360
  
  
  
  // Using Activity
  10-12 03:37:02.375: I/CHEOK(29956): height = 0
  10-12 03:37:02.387: I/CHEOK(29956): height = 0
  10-12 03:37:02.406: I/CHEOK(29956): height = 3
  10-12 03:37:02.461: I/CHEOK(29956): height = 51
  10-12 03:37:02.476: I/CHEOK(29956): height = 72
  10-12 03:37:02.492: I/CHEOK(29956): height = 98
  10-12 03:37:02.512: I/CHEOK(29956): height = 131
  10-12 03:37:02.531: I/CHEOK(29956): height = 164
  10-12 03:37:02.547: I/CHEOK(29956): height = 196
  10-12 03:37:02.562: I/CHEOK(29956): height = 228
  10-12 03:37:02.582: I/CHEOK(29956): height = 260
  10-12 03:37:02.601: I/CHEOK(29956): height = 290
  10-12 03:37:02.617: I/CHEOK(29956): height = 312
  10-12 03:37:02.633: I/CHEOK(29956): height = 331
  10-12 03:37:02.652: I/CHEOK(29956): height = 348
  10-12 03:37:02.672: I/CHEOK(29956): height = 357
  10-12 03:37:02.687: I/CHEOK(29956): height = 360
  </code></pre>
  
  <p>Is there any idea, why this happens? How we can improve the performance in <code>DialogFragment</code>? </p>
  
  <p>I place the entire project files, just in case you're interested to test out.</p>
  
  <p><a href="https://www.dropbox.com/s/5q6ttn11o92g39n/ListViewAnimations-master.zip" rel="nofollow noreferrer">https://www.dropbox.com/s/5q6ttn11o92g39n/ListViewAnimations-master.zip</a></p>
  
  <h2>Update</h2>
  
  <p>By tracing using TraceView, I notice that when using <code>DialogFragment</code> to expand <code>ListView</code> item, the <code>ArrayAdapter</code> will be called frequent. I'm not exactly sure why it is so. I do another independent implementation without using the open source library. I'm still facing the same problem. </p>
  
  <p><strong>Perform animation on <code>DialogFragment</code>, the <code>ArrayAdapter</code>'s <code>getView</code> will be triggered frequently while animation is in progress.</strong></p>
  
  <p><strong>For Activity, when animation is in progress, <code>ArrayAdapter</code>'s <code>getView</code> will NOT be triggered.</strong></p>
  
  <p><strong>TraceView while using DialogFragment</strong>
  <img src="https://i.stack.imgur.com/VRmGm.png" alt="enter image description here"></p>
  
  <p><strong>TraceView while using Activity</strong>
  <img src="https://i.stack.imgur.com/ASVt9.png" alt="enter image description here"></p>