web-dev-qa-db-ja.com

RecyclerViewを最新のデータで動的に更新する

アプリに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();
            }
        }
    }
}
4
Micro

何が必要かわかりませんが、このメソッドをアダプターに追加して、データがプルされたら呼び出す必要があると思います

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

これが役立つことを願っています:D

3
WinHtaikAung

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) {
}
 }
1
Akash Jain

あなたの質問から、あなたはデータベースからデータをロードしていて、どこかにデータベースを更新しているコードがあると思います。そして、あなたがあなたのRecyclerViewを更新したいすべての更新で、これが事実であるならば、読み続けてください。これを完全に説明するつもりはありませんが、これを説明する情報源はたくさんあります。

BroadcastRecieverを使用:データベースを更新する場所でsendBroadcast()。また、アクティビティではBroadcastReceiver example を使用し、onReceive()関数呼び出しでArrayListにデータをロードし、adapter.notifyDataSetChanged()を呼び出します。

1
Deepak Baliga

カーソルを毎秒更新

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);

この方法にパフォーマンスの問題があるか、これよりも優れた方法があることを確認してください

持っている場合はここに注意してください

0

ContentProviderの変更を「リッスン」するには、 ContentObserverContentProviderに統合して、ContentProviderでトランザクションが実行されたときに必要なイベントをトリガーできるようにします。その後、CONTENT_URIContentObserverを宣言し、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はロックを取得するための単なるインスタンスオブジェクトであり、mDataRecyclerViewに提供したアイテムのリストです。あなたは要点を理解します。 :D

それが役に立てば幸い。

0
deubaka