title: Handle Touch Events for RecyclerView with frequently changing data
body: <p><strong>Scenario:</strong></p>
  
  <ul>
  <li><code>RecyclerView</code> with a <code>RecyclerView.Adapter</code>. The <code>notifyDataSetChanged()</code>-method is called every 10ms because time sensitive information is shown, so the ViewHolders (class is extending <code>RecyclerView.ViewHolder</code>) are updated frequently.</li>
  <li>The underlying data comes from a native C-library, so the Adapter-methods are overridden to query the data from the native library.</li>
  <li>derived from <a href="https://www.youtube.com/watch?v=69C1ljfDvl0" rel="nofollow noreferrer">this best practice video</a> an interface was implemented in the Adapter-class to pass the onTouch-Events from the Adapter to the Activity. Therefore the ViewHolder-class implements <code>View.OnTouchListener</code> and passes all touch events and the listItem-position (queried by<code>getAdapterPosition()</code>) via the interface to the Activity.</li>
  <li>various onTouch-events (Click, LongClick, Swipe etc.) for each ListItem should be recognized in the activity.</li>
  </ul>
  
  <p><strong>Problem:</strong></p>
  
  <p>When a ListItem is touched shortly after<code>notifyDataSetChanged()</code> has been called, the received values are:</p>
  
  <p><a href="https://www.youtube.com/watch?v=69C1ljfDvl0" rel="nofollow noreferrer">1</a> <code>getAdapterPosition()</code>: -1</p>
  
  <p><a href="https://developer.android.com/reference/android/support/v7/widget/RecyclerView.ViewHolder.html#getAdapterPosition()" rel="nofollow noreferrer">2</a> <code>motionEvent.getAction()</code>:  <code>ACTION_DOWN</code> directly followed by <code>ACTION_CANCEL</code></p>
  
  <p>The <a href="https://developer.android.com/reference/android/support/v7/widget/RecyclerView.ViewHolder.html#getAdapterPosition()" rel="nofollow noreferrer">android-documentation</a> says:</p>
  
  <blockquote>
    <p>Note that if you've called notifyDataSetChanged(), until the next
    layout pass, the return value of this method will be NO_POSITION.</p>
  </blockquote>
  
  <p>So I guess the error occurs everytime the listItem is touched while <code>notifyDatasetChanges()</code> refreshes the viewHolders: <code>getAdapterPosition()</code> returns -1 according to the documentation. And, probably because the elements in the viewHolder are refreshed, the onTouchEvent throws a <code>ACTION_CANCEL</code>.</p>
  
  <p><strong>what I tried:</strong></p>
  
  <ol>
  <li>I tried to stop the refreshing of the RecyclerView on <code>ACTION_DOWN</code>, but because of the <code>ACTION_CANCEL</code> event, I do not receive any <code>ACTION_UP</code> event and can not restart the refreshing of the data.</li>
  <li>I added a <code>RecyclerView.OnItemTouchListener()</code> to the recyclerView in the activity to receive the TouchEvent in <code>onInterceptTouchEvent()</code> before it is passed to the listItem and its onTouchListener and stop the refreshing of the recyclerview there. 
  But, because I still need information about what item was clicked, I still need the items onTouchListener which is still returning -1 and <code>ACTION_CANCEL</code>.</li>
  </ol>
  
  <p><strong>Question:</strong></p>
  
  <p><strong>What is the best practice to handle onTouch events in a RecyclerView which is frequently updating its data?</strong></p>
  
  <p>Activity-class</p>
  
  <pre><code>public class Activity extends AppCompatActivity implements DataStoreDataAdapter.OnListItemTouchListener {
  
  
  
      Handler dataUpdateHandler = new Handler();
  
      Runnable timerRunnable = new Runnable() {
          @Override
          public void run() {
              dataStoreDataAdapter.notifyDataSetChanged();
  
              dataUpdateHandler.postDelayed(this, 10);
          }
      };
  
  
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity);
  
          dataStoreDataAdapter = new DataStoreDataAdapter(getApplicationContext(),this);
  
          recyclerView = findViewById(R.id.list_view);
          layoutManager = new LinearLayoutManager(this);
          recyclerView.setLayoutManager(layoutManager);
          recyclerView.setAdapter(dataStoreDataAdapter);
  
          recyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
              @Override
              public boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
                  Log.i(TAG, "onInterceptTouchEvent: " + " touched by type " + e);
                  int action = e.getAction();
                  if (action == MotionEvent.ACTION_DOWN) {
                      dataUpdateHandler.removeCallbacks(timerRunnable);
                  } else if (action == MotionEvent.ACTION_UP) {
                      dataUpdateHandler.post(timerRunnable);
                  }
                  return false;
              }
  
              @Override
              public void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
                  Log.i(TAG, "onTouchEvent: " + " touched by type " + e);
              }
  
  
              @Override
              public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
  
              }
          });
  
  
          //set update Handler
          dataUpdateHandler.post(timerRunnable);
      }
  
      @Override
      public void onListItemTouch(int position, MotionEvent motionEvent) {
          Log.i(TAG, "onListItemTouch: ListItem position " + position + " motionEvent: " + motionEvent);
      }
  }
  </code></pre>
  
  <p>Adapter-class</p>
  
  <pre><code>public class DataStoreDataAdapter extends RecyclerView.Adapter&lt;DataStoreDataAdapter.ViewHolder&gt; {
  
        private OnListItemTouchListener onListItemTouchListener;
  
      static class ViewHolder extends RecyclerView.ViewHolder implements View.OnTouchListener{
          View view;
          TextView Name;
         // ...  
  
          ViewHolder(@NonNull View itemView, OnListItemTouchListener onListItemTouchListener) {
              super(itemView);
              this.Name = itemView.findViewById(R.id.NameListItem);
              // ...
  
              this.onListItemTouchListener = onListItemTouchListener;
              itemView.setOnTouchListener(this);
          }
  
          @Override
          public boolean onTouch(View view, MotionEvent motionEvent) {
              onListItemTouchListener.onListItemTouch(getAdapterPosition(), motionEvent);
              return true;
          }
      }
  
      public interface OnListItemTouchListener{
          void onListItemTouch(int position, MotionEvent motionEvent);
      }
  
      public DataStoreDataAdapter(Context context, OnListItemTouchListener onListItemTouchListener) {
          super();
          this.onListItemTouchListener = onListItemTouchListener;
      }
  
      @NonNull
      @Override
      public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
  
          LayoutInflater inflater = LayoutInflater.from(parent.getContext());
          View view = inflater.inflate(R.layout.list_item,parent,false);
  
          return new ViewHolder(view, onListItemTouchListener);
      }
  
      @Override
      public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
          // get the stored data for this position
          // ...get stuff from native library
  
          // put data into view elements...
  
      }
  
      @Override
      public int getItemCount() {
          return // ... itemCount from native library;
      }
  
  }
  </code></pre>