アプリにContentProvider
クラスを作成したSQLiteデータベースがあります。
また、ArrayList
のオブジェクトをアダプタにロードしてRecyclerViewにデータを入力するRecyclerViewもあります。
現在、アクティビティが開始されると、Cursor
を介してContentProvider
を取得し、Cursor
をループして、オブジェクトのArrayList
を作成し、それを一部として設定します。私のRecyclerView.Adapter
の。
それはすべて機能しますが、私が本当に望んでいるのは、コンテンツプロバイダーを介して新しいデータがSQLiteデータベースにロードされるときにRecyclerViewが動的に更新されることです。
このライブラリをリストしている投稿を見たことがあります CursorRecyclerAdapter ですが、挿入/削除時にNice 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();
}
}
}
}
何が必要かわかりませんが、このメソッドをアダプターに追加して、データがプルされたら呼び出す必要があると思います
public void swapItems(List< TodoItem > todolist){
this.mTodoList = todolist;
notifyDataSetChanged();
}
これが役立つことを願っています:D
OnPostExecuteで毎回新しいアダプターを作成し、それをrecyclerviewに再度設定する代わりに、リスト要素を変更した後にアダプターに通知できます。
[〜#〜]または[〜#〜]
ローダーを使用してcursoradapterの代わりにarraylistを使用してアダプターを作成したい場合は、提供されたデータを使用してサンプルを作成しました。これを参照として使用できます。
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) {
}
}
あなたの質問から、あなたはデータベースからデータをロードしていて、どこかにデータベースを更新しているコードがあると思います。そして、あなたがあなたのRecyclerViewを更新したいすべての更新で、これが事実であるならば、読み続けてください。これを完全に説明するつもりはありませんが、これを説明する情報源はたくさんあります。
BroadcastRecieverを使用:データベースを更新する場所でsendBroadcast()
。また、アクティビティではBroadcastReceiver example を使用し、onReceive()
関数呼び出しでArrayListにデータをロードし、adapter.notifyDataSetChanged()
を呼び出します。
カーソルを毎秒更新
final Handler handler = new Handler();
final int delay = 1000; //milliseconds
handler.postDelayed(new Runnable(){
public void run(){
//Call cursor loader to refresh cursor
getSupportLoaderManager().restartLoader(LOADER_ID, null, MainActivity.this);
handler.postDelayed(this, delay);
}
}, delay);
この方法にパフォーマンスの問題があるか、これよりも優れた方法があることを確認してください
持っている場合はここに注意してください
ContentProvider
の変更を「リッスン」するには、 ContentObserver
をContentProvider
に統合して、ContentProvider
でトランザクションが実行されたときに必要なイベントをトリガーできるようにします。その後、CONTENT_URI
にContentObserver
を宣言し、RecyclerView
の更新をトリガーできます。
ContentObserver
の実装に関する詳細情報 ここ 。
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
それが役に立てば幸い。