title: Refresh RecyclerView with latest data dynamically
body: <p>I have a SQLite database in my app for which I made a <code>ContentProvider</code> class. </p>
  
  <p>I also have a RecyclerView into which I load an <code>ArrayList</code> of objects into its adapter to populate the RecyclerView.</p>
  
  <p>Currently, when the activity starts I get a <code>Cursor</code> via my <code>ContentProvider</code>, loop through the <code>Cursor</code> to create an <code>ArrayList</code> of objects that I then set as part of my <code>RecyclerView.Adapter</code>.</p>
  
  <p>All that works, but what I really want is for my RecyclerView to dynamically update as new data is loaded into the SQLite database via the content provider. </p>
  
  <p>I have seen posts listing this library <a href="https://gist.github.com/Shywim/127f207e7248fe48400b" rel="noreferrer">CursorRecyclerAdapter</a> but I do not want to use it because you do not get the nice RecyclerView animations on insert/delete.</p>
  
  <p>I was trying to somehow use the <code>LoaderManager.LoaderCallbacks&lt;Cursor&gt;</code> call back methods to get a cursor, convert to arraylist, then swap that in my RecyclerView adapter but couldn't figure it out.</p>
  
  <p>Could someone please show me some example code on how to set it up in my Activity so that the <code>RecyclerView</code> will refresh when new data is written into the local database via a local content provider?</p>
  
  <p>Here is what my RecyclerView.Adapter looks like:</p>
  
  <pre><code>public class MyAdapter extends RecyclerView.Adapter&lt;AdapterTodoList.Holder&gt; {
  
      private List&lt;TodoItem&gt; itemList;
      private Context mContext;
  
      //data
      String message;
      Long datetime;
  
      //this class takes a context and a list of the items you want to populate into the recycler view
      public AdapterTodoList(Context context, List&lt;TodoItem&gt; itemList) {
          this.itemList = itemList;
          this.mContext = context;
      }
  
      @Override
      public Holder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
          //our xml showing how one row looks
          View row = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.recycler_view_todo_item, viewGroup, false);
          Holder holder = new Holder(row);
          return holder;
      }
  
      @Override
      public void onBindViewHolder(Holder holder, final int position) {
          holder.recyclerLinearLayout.setOnClickListener(new View.OnClickListener() {
              @Override
              public void onClick(View view) {
                  Toast.makeText(mContext, "Recycle Click" + position, Toast.LENGTH_SHORT).show();
              }
          });
  
          //get one item
          TodoItem data = itemList.get(position);
          Log.d("Test", "onBindViewHolder position " + position);
  
          message = data.getMessage();
          datetime = data.getDatetime();
  
          //convert long to date
          String dateString = new SimpleDateFormat("MM/dd/yyyy").format(new Date(datetime));
  
          //set the holder
          holder.messageTextView.setText(message); 
      }
  
      @Override
      public int getItemCount() {
          return itemList.size();
      }
  
      public class Holder extends RecyclerView.ViewHolder {
          protected ImageView checkBoxImageView;
          protected TextView messageTextView;
          protected LinearLayout recyclerLinearLayout;
  
          public Holder(View view) {
              super(view);
              //checkBoxImageView = (ImageView) view.findViewById(R.id.checkBoxImageView);
              messageTextView = (TextView) view.findViewById(R.id.messageTextView);
              //the whole view
              recyclerLinearLayout = (LinearLayout) view.findViewById(R.id.recyclerItemLinearLayout);
          }
      }
  }
  </code></pre>
  
  <p>Here is what my Activity looks like so far:</p>
  
  <pre><code>public class HomeRec extends AppCompatActivity implements LoaderManager.LoaderCallbacks&lt;Cursor&gt;{
  
      private Toolbar mToolbar;
  
      //recyclerview and adapter
      private RecyclerView mRecyclerView;
      private MyAdapter adapter;
  
      //the swipe refresh layout that wraps the recyclerview
      private SwipeRefreshLayout mSwipeRefreshLayout;
  
      //this will hold all of our results from our query.
      List&lt;TodoItem&gt; itemList = new ArrayList&lt;TodoItem&gt;();
  
      private Cursor mCursor;
      //resources from layout
      EditText toDoEditText;
      Button cancelButton;
      Button addButton;
  
      //variables
      private String message;
      private long datetime;
  
      //loader
      private SimpleCursorAdapter mTodoAdapter;
      private static final int TODO_LOADER = 0;
  
      // These indices are tied to Projection.  If Projection changes, these
      // must change.
      public static final int COL_ID = 0;
      public static final int COL_MESSAGE = 1;
      public static final int COL_DATETIME = 2;
      public static final int COL_CHECKED = 3;
  
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_home_rec);
  
          mToolbar = (Toolbar) findViewById(R.id.app_bar);
          //set the Toolbar as ActionBar
          setSupportActionBar(mToolbar);
  
          // Initialize recycler view //
          mRecyclerView = (RecyclerView) findViewById(R.id.todoRecyclerView);
          mRecyclerView.hasFixedSize();
          mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
          //set a grey line divider for each item in recycler view
          mRecyclerView.addItemDecoration(
                  new DividerItemDecoration(this, null, false, true));
          // END Initialize recycler view //
  
          //initiate the swipe to refresh layout
          mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipeRefreshLayout);
          mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
              @Override
              public void onRefresh() {
                  // Refresh items
                  refreshItems();
              }
  
              void refreshItems() {
                  // Load items
                  // ...
                  // Load complete
                  onItemsLoadComplete();
              }
  
              void onItemsLoadComplete() {
                  // Update the adapter and notify data set changed
                  // ...
  
                  // Stop refresh animation
                  mSwipeRefreshLayout.setRefreshing(false);
              }
          });
          //set colors for swipe to refresh
          mSwipeRefreshLayout.setColorSchemeResources(
                  R.color.refresh_progress_2,
                  R.color.refresh_progress_3);
  
          //fire my asynctask to get data for the first time
          new MessagesAsyncTask().execute();
      }
  
      @Override
      public boolean onCreateOptionsMenu(Menu menu) {
          // Inflate the menu; this adds items to the action bar if it is present.
          getMenuInflater().inflate(R.menu.menu_home_rec, menu);
          return true;
      }
  
      @Override
      public boolean onOptionsItemSelected(MenuItem item) {
          // Handle action bar item clicks here. The action bar will
          // automatically handle clicks on the Home/Up button, so long
          // as you specify a parent activity in AndroidManifest.xml.
          int id = item.getItemId();
  
          //noinspection SimplifiableIfStatement
          if (id == R.id.action_settings) {
              return true;
          }
  
  
          return super.onOptionsItemSelected(item);
      }
  
      @Override
      public Loader&lt;Cursor&gt; onCreateLoader(int id, Bundle args) {
          //Not sure what to do here or how to make this work.
      }
  
      @Override
      public void onLoadFinished(Loader&lt;Cursor&gt; loader, Cursor data) {
          //Not sure what to do here or how to make this work.    
      }
  
      @Override
      public void onLoaderReset(Loader&lt;Cursor&gt; loader) {
          //Not sure what to do here or how to make this work.
      }
  
      public class MessagesAsyncTask extends AsyncTask&lt;Void, Void, List&lt;TodoItem&gt;&gt; {
  
          //the cursor for the query to content provider
          private Cursor mCursor;
  
          @Override
          protected void onPreExecute() {
  
          }
  
          @Override
          protected List&lt;TodoItem&gt; doInBackground(Void... params) {
              // A "projection" defines the columns that will be returned for each row
              String[] projection =
                      {
                              DataProvider.COL_ID,    // Contract class constant for the COL_ID column name
                              DataProvider.COL_MESSAGE,   // Contract class constant for the COL_MESSAGE column name
                              DataProvider.COL_DATETIME,  // Contract class constant for the COL_DATETIME column name
                              DataProvider.COL_CHECKED  // Contract class constant for the COL_CHECKED column name
                      };
  
              // Defines a string to contain the selection clause
              String selectionClause = null;
  
              // An array to contain selection arguments
              String[] selectionArgs = null;
  
              // An ORDER BY clause, or null to get results in the default sort order
              String sortOrder = DataProvider.COL_DATETIME + " DESC";
  
              // Does a query against the table and returns a Cursor object
              mCursor = getContentResolver().query(
                      DataProvider.CONTENT_URI_TODO,  // The content URI of the Todo table
                      projection,                       // The columns to return for each row
                      selectionClause,                   // Either null, or the word the user entered
                      selectionArgs,                    // Either empty, or the string the user entered
                      sortOrder);                       // The sort order for the returned rows
  
              // Some providers return null if an error occurs, others throw an exception
              if (null == mCursor) {
                  // Insert code here to handle the error.
              } else if (mCursor.getCount() &lt; 1) {
                  // If the Cursor is empty, the provider found no matches
              } else {
                  // Insert code here to do something with the results
              }
  
              //convert cursor to arraylist of objects
              while (mCursor.moveToNext()) {
                  itemList.add(new TodoItem(mCursor.getInt(mCursor.getColumnIndex(DataProvider.COL_ID)),
                          mCursor.getString(mCursor.getColumnIndex(DataProvider.COL_MESSAGE)),
                          mCursor.getLong(mCursor.getColumnIndex(DataProvider.COL_DATETIME)),
                          mCursor.getInt(mCursor.getColumnIndex(DataProvider.COL_CHECKED))
                  ));
              }
              mCursor.close();
              return itemList;
          }
  
  
          @Override
          protected void onPostExecute(List&lt;TodoItem&gt; itemList) {
              if (!itemList.isEmpty()) {
                  adapter = new MyAdapter(HomeRec.this, itemList);
                  mRecyclerView.setAdapter(adapter);
              } else {
                  Toast.makeText(getApplicationContext(), "No data to display", Toast.LENGTH_LONG).show();
              }
          }
      }
  }
  </code></pre>