3

У меня есть база данных SQLite в моем приложении, для которого я сделал ContentProvider класс.Refresh RecyclerView с последними данными динамически

У меня также есть RecyclerView, в который я загружаю ArrayList объектов в свой адаптер, чтобы заполнить RecyclerView.

В настоящее время, когда активность начинается я получаю Cursor через мой ContentProvider, петля через Cursor создать ArrayList объектов, которые я затем установить как часть моего RecyclerView.Adapter.

Все, что работает, но то, что я действительно хочу, чтобы мой RecyclerView динамически обновлять как новые данные загружаются в базу данных SQLite через контент-провайдера.

Я видел сообщения с этой библиотекой CursorRecyclerAdapter, но я не хочу использовать его, потому что вы не получаете приятные анимации RecyclerView на вставке/удалении.

Я пытался каким-то образом использовать методы обратного вызова LoaderManager.LoaderCallbacks<Cursor>, чтобы получить курсор, конвертировать в arraylist, а затем поменять его в моем адаптере RecyclerView, но не смог понять это.

Может кто-нибудь, пожалуйста, покажет мне пример кода о том, как настроить его в моей деятельности, чтобы RecyclerView обновлялся, когда новые данные записываются в локальную базу данных через локальный поставщик контента?

Вот что мой RecyclerView.Adapter выглядит следующим образом:

public class MyAdapter extends RecyclerView.Adapter<AdapterTodoList.Holder> { 

    private List<TodoItem> 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<TodoItem> 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); 
     } 
    } 
} 

Вот что моя активность выглядит так далеко:

public class HomeRec extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor>{ 

    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<TodoItem> itemList = new ArrayList<TodoItem>(); 

    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<Cursor> onCreateLoader(int id, Bundle args) { 
     //Not sure what to do here or how to make this work. 
    } 

    @Override 
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) { 
     //Not sure what to do here or how to make this work.  
    } 

    @Override 
    public void onLoaderReset(Loader<Cursor> loader) { 
     //Not sure what to do here or how to make this work. 
    } 

    public class MessagesAsyncTask extends AsyncTask<Void, Void, List<TodoItem>> { 

     //the cursor for the query to content provider 
     private Cursor mCursor; 

     @Override 
     protected void onPreExecute() { 

     } 

     @Override 
     protected List<TodoItem> 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() < 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<TodoItem> 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(); 
      } 
     } 
    } 
} 
+0

только расширить этот класс, как это абстрактный один и реализовать 'onBindViewHolderCursor' метод, если вы не уверены, что' параметр Cursor' для в 'onBindViewHolderCursor' попытаться вызвать' DatabaseUtils # dumpCurrentRow() ' – pskink

+0

@pskink Извините. Я до сих пор не понимаю. Мне нужен более полный ответ с примером кода, объясняющим это, поэтому я задал новый вопрос. Однако, спасибо. – Micro

+0

как я сказал, расширьте его: 'class MyAdapter расширяет CursorRecyclerAdapter {....' – pskink

ответ

2

Я не уверен, что вам нужно, но я думаю, вы должны добавьте этот метод. Адаптер и звонок после того, как ваши данные были вытащены.

public void swapItems(List<TodoItem> todolist){ 
    this.mTodoList = todolist; 
    notifyDataSetChanged();  
} 

Надеюсь, это поможет: D

+0

Это очень странная проверка нулевой дозы, доза не дает NPE, когда todolist имеет значение null? (Просто интересно узнать). – Chitrang

+0

Nice. Это решило мою проблему. – SagarVimal

0

Для «прослушивания», чтобы ваши ContentProvider изменений, вы могли бы попытаться интегрировать ContentObserver в вашу ContentProvider, так что он может вызвать необходимые события, когда транзакция выполняется на вашем ContentProvider. После чего, вы будете провозгласить ContentObserver к вашим CONTENT_URI, то вы можете запустить обновление для вашего RecyclerView.

Более подробная информация о реализации ContentObserverhere.

пример кода для обновления элемента в вашем RecyclerView будет,

public void update(T data){ 
    synchronized (mLock){ 
     if(data != null && mData.contains(data)){ 
      int index = mData.indexOf(data); 
      mData.set(index, data); 
      notifyItemChanged(index); 
     } 
    } 
} 

В чем T тип объекта, если ваши возвращения строки, mLock это просто экземпляр объекта, чтобы получить блокировку, mData список предметов, которые вы предоставили вашему RecyclerView. Вы получаете суть. : D

Надеется, что это помогает.

1

из вашего вопроса, я предполагаю, что вы загружаете данные из базы данных и где-то есть код, который обновляет базу данных.И при каждом обновлении вы хотите обновить RecyclerView, если это так, продолжайте чтение. Я не собираюсь объяснять это полностью, но есть много источников, которые объяснят вам это.

Использование BroadcastReciever: В месте, где вы обновляете базу данных sendBroadcast(). И в своей деятельности использует BroadcastReceiver example и в вызове onReceive() функции загрузки данных в ArrayList и вызовите adapter.notifyDataSetChanged()

0

Вместо того, чтобы новый адаптер каждый раз, когда в onPostExecute и установить его снова recyclerview вы можете уведомить адаптер после изменения элементов списка.

ИЛИ

Если вы хотите, чтобы сделать адаптер с помощью ArrayList вместо CursorAdapter с помощью загрузчика я сделал выборку для вас с данными, предоставленными вами. Вы можете использовать это в качестве справки:

public class DataBaseActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor> { 

private List itemList; 
private MyAdapter mAdapter; 
@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_data_base); 
    RecyclerView recycle=(RecyclerView)findViewById(R.id.rv_data); 
    SwipeRefreshLayout swipeRefreshLayout= (SwipeRefreshLayout) findViewById(R.id.srl_data); 
    recycle.setLayoutManager(new LinearLayoutManager(this)); 
    itemList=new ArrayList(); 
    mAdapter= new MyAdapter(this, itemList); 
    recycle.setAdapter(mAdapter); 
    swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { 
     @Override 
     public void onRefresh() { 
      getContentResolver().notifyChange(DataProvider.CONTENT_URI_TODO, null); //if you are using content provider 
    //getSupportLoaderManager().restartLoader(100, null, DataBaseActivity.this); // if you are using support lib 
//getLoaderManager().restartLoader(100, null, DataBaseActivity.this); //if you are not using support lib 
     } 
    }); 

    // getLoaderManager().initLoader(100, null, this); //if you are not using support lib 
    getSupportLoaderManager().initLoader(100, null, this); 
} 

@Override 
public Loader<Cursor> onCreateLoader(int id, Bundle args) { 
    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"; 
    return new CursorLoader(this,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); 
} 

@Override 
public void onLoadFinished(Loader<Cursor> loader, Cursor data) { 
    if(data!=null && data.getCount()>0) 
    { 
     itemList.clear(); 
     while (data.moveToNext()) { 
      itemList.add(new TodoItem(data.getInt(data.getColumnIndex(DataProvider.COL_ID)), 
        data.getString(data.getColumnIndex(DataProvider.COL_MESSAGE)), 
        data.getLong(data.getColumnIndex(DataProvider.COL_DATETIME)), 
        data.getInt(data.getColumnIndex(DataProvider.COL_CHECKED)) 
      )); 
     } 
    } 
    else 
     Toast.makeText(getApplicationContext(), "No data to display", Toast.LENGTH_LONG).show(); 

    if(data!=null) 
    data.close(); 
    mAdapter.notifyDataSetChanged(); 
} 

@Override 
public void onLoaderReset(Loader<Cursor> loader) { 
} 
} 
Смежные вопросы