title: Adding items to ListView, maintaining scroll position and NOT seeing a scroll jump
body: <p>I'm building an interface similar to the Google Hangouts chat interface. New messages are added to the bottom of the list. Scrolling up to the top of the list will trigger a load of previous message history. When the history comes in from the network, those messages are added to the top of the list and should not trigger any kind of scroll from the position the user had stopped when the load was triggered. In other words, a "loading indicator" is shown at the top of the list:</p>
  
  <p><img src="https://i.stack.imgur.com/yFmqh.png" alt="enter image description here"></p>
  
  <p>Which is then replaced in-situ with any loaded history.</p>
  
  <p><img src="https://i.stack.imgur.com/AXDgg.png" alt="enter image description here"></p>
  
  <p>I have all of this working... except one thing that I've had to resort to reflection to accomplish. There are plenty of questions and answers involving merely saving and restoring a scroll position when adding items to the adapter attached to a ListView. My problem is that when I do something like the following (simplified but should be self-explanatory):</p>
  
  <pre><code>public void addNewItems(List&lt;Item&gt; items) {
      final int positionToSave = listView.getFirstVisiblePosition();
      adapter.addAll(items);
      listView.post(new Runnable() {
  
          @Override
          public void run() {
              listView.setSelection(positionToSave);
          }
      });
  }
  </code></pre>
  
  <p>Then what the user will see is a quick flash to the top of the ListView, then a quick flash back to the right location. The problem is fairly obvious and discovered <a href="https://groups.google.com/forum/#!topic/android-developers/EnyldBQDUwE" rel="nofollow noreferrer">by many people</a>: <code>setSelection()</code> is unhappy until after <code>notifyDataSetChanged()</code> and a redraw of <code>ListView</code>. So we have to <code>post()</code> to the view to give it a chance to draw. <em>But</em> that looks terrible.</p>
  
  <p>I've "fixed" it by using reflection. I hate it. At its core, what I want to accomplish is reset the first position of the <code>ListView</code> without going through the rigamarole of the draw cycle until <em>after</em> I've set the position. To do that, there's a helpful field of ListView: <code>mFirstPosition</code>. By gawd, that's exactly what I need to adjust! Unfortunately, it's package-private. Also unfortunately, there doesn't appear to be any way to set it programmatically or influence it in any way that doesn't involve an invalidate cycle... yielding the ugly behavior.</p>
  
  <p>So, reflection with a fallback on failure:</p>
  
  <pre><code>try {
      Field field = AdapterView.class.getDeclaredField("mFirstPosition");
      field.setAccessible(true);
      field.setInt(listView, positionToSave);
  }
  catch (Exception e) { // CATCH ALL THE EXCEPTIONS &lt;/meme&gt;
      e.printStackTrace();
      listView.post(new Runnable() {
  
          @Override
              public void run() {
                  listView.setSelection(positionToSave);
              }
          });
      }
  }
  </code></pre>
  
  <p>Does it work? Yes. Is it hideous? Yes. Will it work in the future? Who knows? Is there a better way? <strong>That's my question.</strong></p>
  
  <p><strong>How do I accomplish this without reflection?</strong></p>
  
  <p>An answer <em>might</em> be "write your own <code>ListView</code> that can handle this." I'll merely ask whether you've <a href="http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.1.1_r1/android/widget/ListView.java" rel="nofollow noreferrer">seen the code for <code>ListView</code></a>.</p>
  
  <p><strong>EDIT: Working solution with no reflection based on Luksprog's comment/answer.</strong></p>
  
  <p><a href="https://stackoverflow.com/users/493939/luksprog">Luksprog</a> recommended an <code>OnPreDrawListener()</code>. Fascinating! I've messed with ViewTreeObservers before, but never one of these. After some messing around, the following type of thing appears to work quite perfectly.</p>
  
  <pre><code>public void addNewItems(List&lt;Item&gt; items) {
      final int positionToSave = listView.getFirstVisiblePosition();
      adapter.addAll(items);
      listView.post(new Runnable() {
  
          @Override
          public void run() {
              listView.setSelection(positionToSave);
          }
      });
  
      listView.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
  
          @Override
          public boolean onPreDraw() {
              if(listView.getFirstVisiblePosition() == positionToSave) {
                  listView.getViewTreeObserver().removeOnPreDrawListener(this);
                  return true;
              }
              else {
                  return false;
              }
          }
      });
  }
  </code></pre>
  
  <p>Very cool.</p>