title: Bad layout performance due to view measuring?
body: <p>I've ran into a performance problem, which I think is caused by measuring of my views. 
  My layout is built programmatically, so I cannot show the xml of my layout. But here are some views which might contribute to the problem:</p>
  
  <ul>
  <li>A ScrollView for wrapping all the content (width: MATCH_PARENT, height: MATCH_PARENT)</li>
  <li>A LinearLayout containing all the differnt content parts (width: MATCH_PARENT, height: MATCH_PARENT, a child of the previous view)</li>
  <li>Two <a href="https://github.com/frankiesardo/LinearListView" rel="nofollow noreferrer">LinearListViews</a> (LinearLayout) which are populated asynchronously (width: MATCH_PARENT, height: WRAP_CONTENT, children of the previous view)</li>
  </ul>
  
  <p>The problem only seems to occur when I populate the LinearListView; adding views seems to trigger View.measure being called many times:</p>
  
  <p><img src="https://i.stack.imgur.com/YRTUt.png" alt="Method profiling result"></p>
  
  <p>Why is the View.measured called this many times? The LinearLayout having MATH_PARENT for its size means it doesn't have to measure it again when a child is added, right?</p>
  
  <p>I'm not really sure how to prevent this from happening, or how to debug the problem. I've tried to set the visibility of the LinearListView to GONE untill all the items are added, but this didn't make a difference.</p>
  
  <p>I hope I provided enough information for someone to point me in the right direction.</p>
  
  <p><strong>Edit #1 (07/01):</strong></p>
  
  <p>Here's some parts of the base class of the Adapters used for the LinearListViews:</p>
  
  <pre><code>public abstract class DataProviderAdapter&lt;T&gt; extends BaseAdapter
  {
      ...
  
      @Override
      public View getView(int position, View convertView, ViewGroup parent)
      {
          View row = convertView;
          T item = getItem(position);
  
          Log.d(TAG, "getView(): Position: " + position + ", ConvertView: "
                  + (convertView == null ? "null" : convertView) + ", TotalCount: " + getCount());
  
          if (row == null) {
              row = buildRow(position);
              markRowAsLoading(row);
          }
  
          if (item == null) {
  
              if (convertView != null) {
                  markRowAsLoading(row);
              }
  
              int skip;
              if (itemsRequestParams != null) {
                  int pageSize = itemsRequestParams.getTake();
  
                  // determine how many items to skip
                  skip = position - (position % pageSize);
              }
              else {
                  skip = 0;
              }
  
              // if the page isn't already being fetched
              if (!pagesReceiving.contains(skip)) {
                  pagesReceiving.add(skip);
                  AsyncTaskUtil.executeMultiThreaded(new ReceiveItemsTask(), skip);
              }
          }
          else {
              Log.d(TAG, getClass().getSimpleName() + " - PrepareRow: " + getItemId(position)
                      + ", Pos: " + position);
              prepareRow(row, item, position);
          }
          return row;
      }
  
      protected void processDataResult(int skip, DataResult&lt;T&gt; result)
      {
          if (result instanceof GroupedDataResult&lt;?&gt;) {
              GroupedDataResult&lt;T&gt; groupedResult = (GroupedDataResult&lt;T&gt;)result;
              totalItemCount = groupedResult.getTotalGroupCount() + groupedResult.getTotalItemCount();
          }
          else {
              totalItemCount = result.getTotalItemCount();
          }
  
          for (int i = 0; i &lt; result.getItems().size(); i++) {
              itemList.put(skip + i, result.getItems().get(i));
          }
  
          Log.d(TAG, "processDataResult(" + skip + ")");
          notifyDataSetChanged();
      }
  
      static protected class CellHolder
      {
          public ProgressBar ProgressSpinner;
          public List&lt;android.widget.ImageView&gt; Images;
          public List&lt;TextView&gt; Labels;
          private final HashMap&lt;String, Object&gt; _extras = new HashMap&lt;String, Object&gt;();
  
          public void putExtra(String key, Object value)
          {
              _extras.put(key, value);
          }
  
          @SuppressWarnings("unchecked")
          public &lt;T&gt; T getExtra(String key)
          {
              return _extras.containsKey(key) ? (T)_extras.get(key) : null;
          }
      }
  
      ...
  
      private class ReceiveItemsTask extends AsyncTask&lt;Integer, Void, DataResult&lt;T&gt;&gt;
      {
          private int skip;
  
          @Override
          protected DataResult&lt;T&gt; doInBackground(Integer... params)
          {
              skip = params[0];
              DataResult&lt;T&gt; items = getItems(skip);
              Log.d(TAG, "doInBackground(" + skip + ")");
              return items;
          }
  
          @Override
          protected void onPostExecute(DataResult&lt;T&gt; result)
          {
              Log.d(TAG,
                      "onPostExecute("
                              + (result == null ? "null" : result.getItems().size() + "/"
                                      + result.getTotalItemCount()) + ")");
              if (result == null) {
                  return;
              }
              processDataResult(skip, result);
              Log.d(TAG, "onPostExecute end");
          }
      }
  }
  </code></pre>
  
  <p><strong>Edit #2 (07/01):</strong></p>
  
  <p>I subclassed RelativeLayout and overridden onMeasure so I could log which views were being measured, and in which order.
  These are the results:</p>
  
  <pre><code>07-01 15:37:37.434: V/DefaultLayout(8172): DefaultLayout.onMeasure
  07-01 15:37:37.434: V/DefaultLayout(8172): TabView.onMeasure
  07-01 15:37:37.434: V/DefaultLayout(8172): ItemDetailsContainerView.onMeasure
  07-01 15:37:37.438: V/DefaultLayout(8172): HeaderView.onMeasure
  07-01 15:37:37.438: V/DefaultLayout(8172): ItemDetailsInfoFieldsLayout.onMeasure
  07-01 15:37:37.438: V/DefaultLayout(8172): ItemReviewTableView.onMeasure
  07-01 15:37:37.438: V/DefaultLayout(8172): InfoFieldsLayout.onMeasure
  07-01 15:37:37.438: V/DefaultLayout(8172): InfoFieldsLayout.onMeasure
  07-01 15:37:37.442: V/DefaultLayout(8172): InfoFieldsLayout.onMeasure
  07-01 15:37:37.442: V/DefaultLayout(8172): InfoFieldsLayout.onMeasure
  07-01 15:37:37.442: V/DefaultLayout(8172): InfoFieldsLayout.onMeasure
  07-01 15:37:37.442: V/DefaultLayout(8172): InfoFieldsLayout.onMeasure
  07-01 15:37:37.442: V/DefaultLayout(8172): InfoFieldsLayout.onMeasure
  07-01 15:37:37.442: V/DefaultLayout(8172): InfoFieldsLayout.onMeasure
  07-01 15:37:37.446: V/DefaultLayout(8172): ItemReviewTableView.onMeasure
  07-01 15:37:37.446: V/DefaultLayout(8172): InfoFieldsLayout.onMeasure
  07-01 15:37:37.446: V/DefaultLayout(8172): InfoFieldsLayout.onMeasure
  07-01 15:37:37.446: V/DefaultLayout(8172): InfoFieldsLayout.onMeasure
  07-01 15:37:37.446: V/DefaultLayout(8172): InfoFieldsLayout.onMeasure
  07-01 15:37:37.450: V/DefaultLayout(8172): InfoFieldsLayout.onMeasure
  07-01 15:37:37.450: V/DefaultLayout(8172): InfoFieldsLayout.onMeasure
  07-01 15:37:37.450: V/DefaultLayout(8172): InfoFieldsLayout.onMeasure
  07-01 15:37:37.450: V/DefaultLayout(8172): InfoFieldsLayout.onMeasure
  07-01 15:37:37.450: V/DefaultLayout(8172): RelatedItemsTableView.onMeasure
  07-01 15:37:37.454: V/DefaultLayout(8172): RatingView.onMeasure
  07-01 15:37:37.458: V/DefaultLayout(8172): RatingView.onMeasure
  07-01 15:37:37.462: V/DefaultLayout(8172): RatingView.onMeasure
  07-01 15:37:37.466: V/DefaultLayout(8172): RatingView.onMeasure
  07-01 15:37:37.470: V/DefaultLayout(8172): RatingView.onMeasure
  07-01 15:37:37.474: V/DefaultLayout(8172): RatingView.onMeasure
  07-01 15:37:37.478: V/DefaultLayout(8172): RatingView.onMeasure
  07-01 15:37:37.478: V/DefaultLayout(8172): RatingView.onMeasure
  07-01 15:37:37.482: V/DefaultLayout(8172): RatingView.onMeasure
  07-01 15:37:37.482: V/DefaultLayout(8172): RatingView.onMeasure
  07-01 15:37:37.486: V/DefaultLayout(8172): RatingView.onMeasure
  07-01 15:37:37.486: V/DefaultLayout(8172): RatingView.onMeasure
  07-01 15:37:37.490: V/DefaultLayout(8172): RatingView.onMeasure
  07-01 15:37:37.490: V/DefaultLayout(8172): RatingView.onMeasure
  07-01 15:37:37.494: V/DefaultLayout(8172): RatingView.onMeasure
  07-01 15:37:37.494: V/DefaultLayout(8172): RatingView.onMeasure
  07-01 15:37:37.498: V/DefaultLayout(8172): RelatedItemsTableView.onMeasure
  07-01 15:37:37.498: V/DefaultLayout(8172): RatingView.onMeasure
  07-01 15:37:37.498: V/DefaultLayout(8172): RatingView.onMeasure
  07-01 15:37:37.502: V/DefaultLayout(8172): RatingView.onMeasure
  07-01 15:37:37.502: V/DefaultLayout(8172): RatingView.onMeasure
  07-01 15:37:37.506: V/DefaultLayout(8172): RatingView.onMeasure
  07-01 15:37:37.506: V/DefaultLayout(8172): RatingView.onMeasure
  07-01 15:37:37.510: V/DefaultLayout(8172): RatingView.onMeasure
  07-01 15:37:37.510: V/DefaultLayout(8172): RatingView.onMeasure
  07-01 15:37:37.514: V/DefaultLayout(8172): RatingView.onMeasure
  07-01 15:37:37.514: V/DefaultLayout(8172): RatingView.onMeasure
  07-01 15:37:37.518: V/DefaultLayout(8172): RatingView.onMeasure
  07-01 15:37:37.518: V/DefaultLayout(8172): RatingView.onMeasure
  07-01 15:37:37.522: V/DefaultLayout(8172): RatingView.onMeasure
  07-01 15:37:37.522: V/DefaultLayout(8172): RatingView.onMeasure
  07-01 15:37:37.526: V/DefaultLayout(8172): RatingView.onMeasure
  07-01 15:37:37.526: V/DefaultLayout(8172): RatingView.onMeasure
  ...
  </code></pre>
  
  <p>This list goes on for a while. But what seems weird to me, is that it starts with measuring the top view (DefaultLayout.onMeasure), and then also measures ALL the children (so all the views). It does this multiple times!</p>
  
  <p>What could be causing onMeasure to be called on the top view?</p>
  
  <p><strong>Edit #3 (07/02):</strong></p>
  
  <p>Could it be that <code>notifyDataSetChanged()</code> triggers the measuring of the top view?</p>
  
  <p><strong>Edit #4 (07/03):</strong></p>
  
  <p>Yes, it does seem like <code>notifyDataSetChanged()</code> triggers the top-level view to be measured (I was able to reproduce this in a new test project). And since the way my adapter works, <code>notifyDataSetChanged()</code> is called multiple times.</p>
  
  <p>What is the reason all views are measured when i call <code>notifyDataSetChanged()</code>? Is there a way I can prevent this from happening?</p>