androidandroid-recyclerviewmaintainscrollpositionon

Update RecyclerView while keeping its scroll position


How can I keep the current scroll position in a RecyclerView after updating the contents?

In the onCreate I set a task to repeat as follows:

private void setRepeatingAsyncTask() {

    final Handler handler = new Handler();
    Timer timer = new Timer();

    TimerTask task = new TimerTask() {
        @Override
        public void run() {
            handler.post(new Runnable() {
                public void run() {
                    try {
                        new getArrivals().execute(station_id);
                    } catch (Exception e) {
                        // error, do something
                    }
                }
            });
        }
    };
    timer.schedule(task, 0, 30000);  // interval of 30 seconds
}

The AsyncTask will query the database for the latest list contents:

private class getArrivals extends AsyncTask<Long, Void, Long>{

        @Override
        protected void onPreExecute(){
            //emptyMessage.setVisibility(VISIBLE);
            //recyclerView.setVisibility(GONE);
        }
        @Override
        protected Long doInBackground(Long... params) {
            long station_id = params[0];
            station = db.stationModel().getStationById(station_id);
            arrivals = db.arrivalModel().getNextArrivalsByStation(station_id, StaticClass.getDays());
            return station_id;
        }

        @Override
        protected void onPostExecute(Long result){
            if(getSupportActionBar() != null) {
                getSupportActionBar().setTitle(capitalize(station.name));
            }

            refreshList();
        }
}

Upon completion of the task I then call for the list to be refreshed:

private void refreshList(){

    arrivalListAdapter = new ArrivalListAdapter(getApplicationContext(), arrivals);
    staggeredGridLayoutManager = new StaggeredGridLayoutManager(1, StaggeredGridLayoutManager.VERTICAL);
    recyclerView.setLayoutManager(staggeredGridLayoutManager);
    recyclerView.setAdapter(arrivalListAdapter);
    Log.d("arrivals", arrivals.size()+"");
    arrivalListAdapter.notifyDataSetChanged();

    if(arrivals.size() == 0){
        emptyMessage.setVisibility(VISIBLE);
        recyclerView.setVisibility(GONE);

        new fetchArrivalsFromSource().execute(station.id);
    }else{
        emptyMessage.setVisibility(GONE);
        recyclerView.setVisibility(VISIBLE);
    }
}

I'm pretty sure that the reason for my issue is that I'm setting the adapter everytime. I've tried setting that with the initial task but that leads to the list not being updated at all.


Solution

  • You can get the current position, refresh the adapter and smooth scroll to the previous position like this:

    RecyclerView.SmoothScroller smoothScroller = new LinearSmoothScroller(context) {
      @Override protected int getVerticalSnapPreference() {
        return LinearSmoothScroller.SNAP_TO_START;
      }
    };
    

    Then set the position to scroll to:

    smoothScroller.setTargetPosition(position);
    layoutManager.startSmoothScroll(smoothScroller);