Scenario:
- RecyclerViewwith a- RecyclerView.Adapter. The- notifyDataSetChanged()-method is called every 10ms because time sensitive information is shown, so the ViewHolders (class is extending- RecyclerView.ViewHolder) are updated frequently.
- The underlying data comes from a native C-library, so the Adapter-methods are overridden to query the data from the native library.
- derived from this best practice video an interface was implemented in the Adapter-class to pass the onTouch-Events from the Adapter to the Activity. Therefore the ViewHolder-class implements View.OnTouchListenerand passes all touch events and the listItem-position (queried bygetAdapterPosition()) via the interface to the Activity.
- various onTouch-events (Click, LongClick, Swipe etc.) for each ListItem should be recognized in the activity.
Problem:
When a ListItem is touched shortly afternotifyDataSetChanged() has been called, the received values are:
1 getAdapterPosition(): -1
2 motionEvent.getAction():  ACTION_DOWN directly followed by ACTION_CANCEL
The android-documentation says:
Note that if you've called notifyDataSetChanged(), until the next layout pass, the return value of this method will be NO_POSITION.
So I guess the error occurs everytime the listItem is touched while notifyDatasetChanges() refreshes the viewHolders: getAdapterPosition() returns -1 according to the documentation. And, probably because the elements in the viewHolder are refreshed, the onTouchEvent throws a ACTION_CANCEL.
what I tried:
- I tried to stop the refreshing of the RecyclerView on ACTION_DOWN, but because of theACTION_CANCELevent, I do not receive anyACTION_UPevent and can not restart the refreshing of the data.
- I added a RecyclerView.OnItemTouchListener()to the recyclerView in the activity to receive the TouchEvent inonInterceptTouchEvent()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 andACTION_CANCEL.
Question:
What is the best practice to handle onTouch events in a RecyclerView which is frequently updating its data?
Activity-class
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);
    }
}
Adapter-class
public class DataStoreDataAdapter extends RecyclerView.Adapter<DataStoreDataAdapter.ViewHolder> {
      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;
    }
}
 
    