Right now I am trying to use recyclerview with a cursorloader. I included the cursorloader within my recyclerview adapter based on my research. I do not have the desire to put my SQLite database data into an arraylist. Right now it looks like my code is correct but when I load the app I get a blank screen. Can anyone help me see my mistake in my code?
Here is my adapter:
public class PrescriptionRecyclerAdapter extends RecyclerView.Adapter<PrescriptionRecyclerAdapter.ViewHolder> {
private CursorAdapter mCursorAdapter;
private Context mContext;
private ViewHolder holder;
Cursor prescriptionCursor;
public PrescriptionRecyclerAdapter(Context context, Cursor c) {
mContext = context;
prescriptionCursor = c;
mCursorAdapter = new CursorAdapter(mContext, c, 0) {
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
// Inflate the view here
View v = LayoutInflater.from(context)
.inflate(R.layout.recycle_item, parent, false);
return v;
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
// Extract data from the current store row and column
int nameColumnIndex = cursor.getColumnIndex(PrescriptionContract.PrescriptionEntry.COLUMN_PRESCRIPTION_NAME);
int amountColumnIndex = cursor.getColumnIndex(PrescriptionContract.PrescriptionEntry.COLUMN_PRESCRIPTION_AMOUNT);
int durationColumnIndex = cursor.getColumnIndex(PrescriptionContract.PrescriptionEntry.COLUMN_PRESCRIPTION_FREQUENCY_DURATION);
final int columnIdIndex = cursor.getColumnIndex(PrescriptionContract.PrescriptionEntry._ID);
//Read the store attritubes from the Cursor for the current stores
String name = cursor.getString(nameColumnIndex);
String amount = cursor.getString(amountColumnIndex);
String duration = cursor.getString(durationColumnIndex);
String col = cursor.getString(columnIdIndex);
// Populate fields with extracted properties
holder.prescriptionName.setText(name);
holder.prescriptionAmount.setText(amount);
holder.prescriptionDays.setText(duration);
}
};
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView prescriptionName;
public TextView prescriptionAmount;
public TextView prescriptionDays;
final public Button prescriptionButton;
public ViewHolder(View itemView) {
super(itemView);
// Find fields to populate in inflated template
prescriptionName = (TextView) itemView.findViewById(R.id.name);
prescriptionAmount = (TextView) itemView.findViewById(R.id.amountlist);
prescriptionDays = (TextView) itemView.findViewById(R.id.daysList);
prescriptionButton = itemView.findViewById(R.id.scheduleButton);
}
}
#Override
public int getItemCount() {
return mCursorAdapter.getCount();
}
public Cursor swapCursor(Cursor cursor) {
if (prescriptionCursor == cursor) {
return null;
}
Cursor oldCursor = prescriptionCursor;
this.prescriptionCursor = cursor;
if (cursor != null) {
this.notifyDataSetChanged();
}
return oldCursor;
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
// Passing the binding operation to cursor loader
mCursorAdapter.getCursor().moveToPosition(position);
mCursorAdapter.bindView(holder.itemView, mContext, mCursorAdapter.getCursor());
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// Passing the inflater job to the cursor-adapter
View v = mCursorAdapter.newView(mContext, mCursorAdapter.getCursor(), parent);
holder = new ViewHolder(v);
return holder;
}
}
Here is my display activity.
public class DisplayActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor>{
private static final int PRESCRIPTION_LOADER = 0;
PrescriptionRecyclerAdapter mCursorAdapter;
private RecyclerView.LayoutManager mLayoutManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_display);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent(DisplayActivity.this, EditorActivity.class);
startActivity(intent);
}
});
RecyclerView prescriptionRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);
mLayoutManager = new LinearLayoutManager(getApplicationContext());
prescriptionRecyclerView.setLayoutManager(mLayoutManager);
mCursorAdapter = new PrescriptionRecyclerAdapter(this, null);
prescriptionRecyclerView.setAdapter(mCursorAdapter);
//Kick off the loader
getLoaderManager().initLoader(PRESCRIPTION_LOADER,null,this);
}
#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_display, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// User clicked on a menu option in the app bar overflow menu
switch (item.getItemId()) {
// Respond to a click on the "Delete all entries" menu option
case R.id.action_delete_all_entries:
deleteAllPrescriptions();
return true;
}
return super.onOptionsItemSelected(item);
}
/**
* Helper method to delete all items in the database.
*/
private void deleteAllPrescriptions() {
int rowsDeleted = getContentResolver().delete(PrescriptionEntry.CONTENT_URI, null, null);
Log.v("CatalogActivity", rowsDeleted + " rows deleted from prescription database");
}
#Override
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
// Since the editor shows all store attributes, define a projection that contains
// all columns from the store table
String[] projection = {
PrescriptionEntry._ID,
PrescriptionEntry.COLUMN_PRESCRIPTION_NAME,
PrescriptionEntry.COLUMN_PRESCRIPTION_AMOUNT,
PrescriptionEntry.COLUMN_PRESCRIPTION_FREQUENCY_HOURS,
PrescriptionEntry.COLUMN_PRESCRIPTION_FREQUENCY_TIMES,
PrescriptionEntry.COLUMN_PRESCRIPTION_FREQUENCY_DURATION,
PrescriptionEntry.COLUMN_PRESCRIPTION_REFILL,
PrescriptionEntry.COLUMN_PRESCRIPTION_EXPIRATION,
PrescriptionEntry.COLUMN_PRESCRIPTION_PHARMACIST_NAME,
PrescriptionEntry.COLUMN_PRESCRIPTION_PHARMACIST_NUMBER,
PrescriptionEntry.COLUMN_PRESCRIPTION_PHYSICIAN_NAME,
PrescriptionEntry.COLUMN_PRESCRIPTION_PHYSICIAN_NUMBER};
// This loader will execute the ContentProvider's query method on a background thread
return new CursorLoader(this, // Parent activity context
PrescriptionEntry.CONTENT_URI, // Query the content URI for the current store
projection, // Columns to include in the resulting Cursor
null, // No selection clause
null, // No selection arguments
null); // Default sort order
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
mCursorAdapter.swapCursor(data);
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
mCursorAdapter.swapCursor(null);
}
}
I think i got your problem. After getting new Cursor in onLoadFinished method you are calling PrescriptionRecyclerAdapter's swapCursor() method this method is updating prescriptionCursor Cursor reference. Thats OK. But updating prescriptionCursor will not effect your CursorAdapter. You are actually dependent on CursorAdapter. So you have to update theCursor of your CursorAdapter. Because your mCursorAdapter still holding the old reference of the Cursor you have provided in constructor.
So use this method to update Cursor reference mCursorAdapter.swapCursor(prescriptionCursor).
public Cursor swapCursor(Cursor cursor) {
if (prescriptionCursor == cursor) {
return null;
}
Cursor oldCursor = prescriptionCursor;
this.prescriptionCursor = cursor;
if (cursor != null) {
this.notifyDataSetChanged();
// update your Cursor for CursorAdapter
mCursorAdapter.swapCursor(prescriptionCursor);
}
return oldCursor;
}
I think you have made it complex by maintaining two Adapter. You can use RecyclerView.Adapter with List or Cursor. There is not need to make it complex.
Hope it will help you. Let me know it solve your problem.
Related
I'm trying to populate a recyclerview with a loader but the adapter will only bind the first five items on the database and then repeat for every other item in the database.
To make it clear, it looks like this:
item 1
item 2
item 3
item 4
item 5
item 1
item 2
...
The number of items still matches the number of items on the database though. I've also tested the cursor and it prints every item correctly so I'm assuming the problem is the adapter. This is what I'm using:
class LibraryAdapter extends RecyclerView.Adapter<LibraryAdapter.LibraryViewHolder> {
private Context mContext;
private Cursor mCursor;
LibraryAdapter(Context context, Cursor cursor){
this.mContext = context;
this.mCursor = cursor;
setHasStableIds(true);
}
static class LibraryViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private TextView titleText;
private TextView numText;
LibraryViewHolder(View itemView) {
super(itemView);
titleText = (TextView) itemView.findViewById(R.id.titleText);
numText = (TextView) itemView.findViewById(R.id.numText);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
....
}
}
#Override
public LibraryAdapter.LibraryViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item, parent, false);
return new LibraryViewHolder(view);
}
#Override
public void onBindViewHolder(LibraryAdapter.LibraryViewHolder holder, int position) {
LibraryModel item = getData(position);
holder.titleText.setText(item.getTitle());
holder.numText.setText(item.getNum() + " items");
}
#Override
public int getItemCount() {
return (mCursor != null) ? mCursor.getCount() : 0;
}
private Cursor swapCursor(Cursor cursor){
if(mCursor == cursor){
return null;
}
Cursor oldCursor = mCursor;
this.mCursor = cursor;
if(cursor != null){
this.notifyDataSetChanged();
}
return oldCursor;
}
void changeCursor(Cursor cursor){
Cursor oldCursor = swapCursor(cursor);
if (oldCursor != null){
oldCursor.close();
}
}
private LibraryModel getData(int position){
mCursor.moveToPosition(position);
String title = mCursor.getString(mCursor.getColumnIndex(DatabaseContract.LibraryEntry.COLUMN_TITLE));
int num = mCursor.getInt(mCursor.getColumnIndex(DatabaseContract.LibraryEntry.COLUMN_NUMBER));
LibraryModel item = new LibraryModel();
item.setTitle(title);
item.setNum(num);
return item;
}
}
And on the fragment:
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_alltracks, container, false);
libraryRecyclerview = (RecyclerView) root.findViewById(R.id.list);
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getContext());
libraryRecyclerview.setLayoutManager(mLayoutManager);
mAdapter = new LibraryAdapter(getContext(), null, allTracks);
libraryRecyclerview.setAdapter(mAdapter);
return root;
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
getLoaderManager().initLoader(1, null, this);
}
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
Uri uri = DatabaseContract.LibraryEntry.CONTENT_URI;
String[] projection = {
DatabaseContract.LibraryEntry.TABLE_NAME + "." + DatabaseContract.LibraryEntry._ID,
DatabaseContract.LibraryEntry.COLUMN_TITLE,
DatabaseContract.LibraryEntry.COLUMN_NUM
};
return new CursorLoader(getContext(), uri, projection, null, null, null);
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
mAdapter.changeCursor(data);
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
mAdapter.changeCursor(null);
}
For anyone still stumbling into this question:
I had the same problem, repeating the same 5 entries in my recycler view.
What fixed my problem was simply to remove
adapter.setHasStableIds(true)
Granted, this got me up to other problems, but I managed to display all my entries with this.
I checked a lot of stack overflow question, and none of them helped.
How to refresh my listView after deleting item with onLongClickListener?
As you'll see nor adapter.notifyDataSetChanged(), nor listView.invalidateViews() don't work.
This is implementation with just necessary methods to figure out idea.
public class MainActivity extends AppCompatActivity {
public FloatingActionButton fabAddWord;
public Toolbar toolbar;
public ListView listView;
private RjecnikCursorAdapter adapter;
private RjecnikDB dbRjecnik;
private SQLiteDatabase db;
private Cursor cursor;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
fabAddWord = (FloatingActionButton) findViewById(R.id.fabAddWord);
listView = (ListView) findViewById(R.id.listView);
dbRjecnik = new RjecnikDB(this);
db = dbRjecnik.getWritableDatabase();
String query = "SELECT * FROM " + RjecnikDB.TABLE;
cursor = db.rawQuery(query, null);
adapter = new RjecnikCursorAdapter(this, cursor);
listView.setAdapter(adapter);
listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
deleteOnLongClick(cursor.getString(cursor.getColumnIndex(RjecnikDB.COLUMN_RIJEC)));
adapter.notifyDataSetChanged();
listView.invalidateViews();
return true;
}
});
}
public void deleteOnLongClick(String rijec) {
SQLiteDatabase db = dbRjecnik.getWritableDatabase();
db.delete(RjecnikDB.TABLE, RjecnikDB.COLUMN_RIJEC + " = ?", new String[] {rijec} );
this.adapter.notifyDataSetChanged();
this.listView.invalidateViews();
db.close();
}
CustomAdapter
public class RjecnikCursorAdapter extends CursorAdapter {
public RjecnikCursorAdapter (Context context, Cursor cursor) {
super(context, cursor, 0);
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return LayoutInflater.from(context).inflate(R.layout.item_word, parent, false);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
TextView tvSingleLineWord = (TextView) view.findViewById(R.id.tvSingleLineWord);
// Extract properties from cursor
String rijec = cursor.getString(cursor.getColumnIndex(RjecnikDB.COLUMN_RIJEC));
// Populate fields with extracted properties
tvSingleLineWord.setText(rijec);
}
}
The Adapter have a data cache, so data in ListView will not change when data in database changed. You should change the cursor.
#Override
public void changeCursor(Cursor cursor) {
mIndexer.setCursor(cursor);
super.changeCursor(cursor);
}
put code below to your OnItemLongClickListener
cursor = db.rawQuery(query, null);
adapter.changeCurosr(cursor);
You have to use just notifyDataSetChanged() to refresh your listView data, invalidateViews() will only redraw the visible items yet nothing on them has changed (changing font for example).
Note that is recommanded to run your notifyDataSetChanged() on UI thread.
EDIT 2 : you can use BaseAdapter instead of CursorAdapter like below
public class RjecnikCursorAdapter extends BaseAdapter {
private Activity activity;
private LayoutInflater inflater;
private List<String> rjecnikList;
public RjecnikCursorAdapter(Activity activity, List<String> rjecnikList) {
this.activity = activity;
this.rjecnikList = rjecnikList;
}
#Override
public int getCount() {
return rjecnikList.size();
}
#Override
public Object getItem(int location) {
return rjecnikList.get(location);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (inflater == null)
inflater = (LayoutInflater) activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (convertView == null)
convertView = inflater.inflate(R.layout.item_word, null);
TextView tvSingleLineWord = (TextView) view.findViewById(R.id.tvSingleLineWord);
tvSingleLineWord.setText(String.valueOf(rjecnikList.get(position)));
return convertView;
}
}
And in your activity :
public class MainActivity extends AppCompatActivity {
public FloatingActionButton fabAddWord;
public Toolbar toolbar;
public ListView listView;
public List<String> mylist;
private RjecnikCursorAdapter adapter;
private RjecnikDB dbRjecnik;
private SQLiteDatabase db;
private Cursor cursor;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
fabAddWord = (FloatingActionButton) findViewById(R.id.fabAddWord);
listView = (ListView) findViewById(R.id.listView);
dbRjecnik = new RjecnikDB(this);
db = dbRjecnik.getWritableDatabase();
String query = "SELECT * FROM " + RjecnikDB.TABLE;
cursor = db.rawQuery(query, null);
mylist = new ArrayList<>();
if (cursor.moveToFirst()){
do{
//change this with your column data
String data = cursor.getString(cursor.getColumnIndex("data");
mylist.add(data);
}while(cursor.moveToNext());
}
cursor.close();
adapter = new RjecnikCursorAdapter(this, mylist);
listView.setAdapter(adapter);
listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
deleteOnLongClick(cursor.getString(cursor.getColumnIndex(RjecnikDB.COLUMN_RIJEC)));
mylist.remove(position);
adapter.notifyDataSetChanged();
listView.invalidateViews();
return true;
}
});
}
public void deleteOnLongClick(String rijec) {
SQLiteDatabase db = dbRjecnik.getWritableDatabase();
db.delete(RjecnikDB.TABLE, RjecnikDB.COLUMN_RIJEC + " = ?", new String[] {rijec} );
this.adapter.notifyDataSetChanged();
this.listView.invalidateViews();
db.close();
}
I have a ChatActivity, which loads its data via a CursorLoader. The CursorLoader return a cursor with two registers, but the newView and bindView methods in adapter is never called.
My activity
public class ChatActivity extends BaseActivity implements LoaderManager.LoaderCallbacks<Cursor> {
public static final String EXTRA_AMANTEID = "amanteId";
private EditText messageET;
private ListView messagesContainer;
private Button sendBtn;
private ChatAdapter adapter;
private Long amanteId;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
amanteId = getIntent().getLongExtra(ChatActivity.EXTRA_AMANTEID, 0L);
messagesContainer = (ListView) findViewById(R.id.messagesContainer);
messageET = (EditText) findViewById(R.id.messageEdit);
sendBtn = (Button) findViewById(R.id.chatSendButton);
RelativeLayout container = (RelativeLayout) findViewById(R.id.container);
adapter = new ChatAdapter(this);
getLoaderManager().initLoader(0, null, this);
messagesContainer.setAdapter(adapter);
}
private void scroll() {
messagesContainer.setSelection(messagesContainer.getCount() - 1);
}
#Override
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
return new CursorLoader(ChatActivity.this, MensagemProvider.CONTENT_URI_CONVERSA, null, null, new String[]{Long.toString(amanteId), Long.toString(amanteId)}, null);
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
adapter.swapCursor(cursor);
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
adapter.swapCursor(null);
}
}
My adapter
public class ChatAdapter extends CursorAdapter {
private Cursor cursor;
private int dataEnvioColumnIndex;
private int idMensagemColumnIndex;
private int idRemetenteColumnIndex;
private int idDestinatarioColumnIndex;
private int apelidoRemetenteColumnIndex;
private int apelidoDestinatarioColumnIndex;
private int textoMensagemColumnIndex;
private long idColaboradorLogado;
public ChatAdapter(Context context) {
super(context, null, false);
}
public ChatMessage getItem() {
ChatMessage message = new ChatMessage();
SimpleDateFormat dt = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
Date dataEnvio = new Date(cursor.getLong(dataEnvioColumnIndex));
message.setDate(dt.format(dataEnvio));
message.setId(cursor.getLong(idMensagemColumnIndex));
Long de = cursor.getLong(idRemetenteColumnIndex);
Long logado = BaseApp.getCredentials().getId();
message.setMe(de.equals(logado));
message.setMessage(cursor.getString(textoMensagemColumnIndex));
return message;
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
LayoutInflater vi = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View retView = vi.inflate(R.layout.list_item_chat_message, null);
return retView;
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
ViewHolder holder = createViewHolder(view);;
view.setTag(holder);
ChatMessage chatMessage = getItem();
boolean myMsg = chatMessage.getIsme() ;//Just a dummy check
holder.txtMessage.setText(chatMessage.getMessage());
holder.txtInfo.setText(chatMessage.getDate());
}
private ViewHolder createViewHolder(View v) {
ViewHolder holder = new ViewHolder();
holder.txtMessage = (TextView) v.findViewById(R.id.txtMessage);
holder.content = (LinearLayout) v.findViewById(R.id.content);
holder.contentWithBG = (LinearLayout) v.findViewById(R.id.contentWithBackground);
holder.txtInfo = (TextView) v.findViewById(R.id.txtInfo);
return holder;
}
private static class ViewHolder {
public TextView txtMessage;
public TextView txtInfo;
public LinearLayout content;
public LinearLayout contentWithBG;
}
#Override
public Cursor swapCursor(Cursor cursor) {
if(cursor!=null) {
cursor.moveToFirst();
idMensagemColumnIndex = cursor.getColumnIndex(MensagemProvider.COLUMN_MENSAGEMID);
idRemetenteColumnIndex = cursor.getColumnIndex(MensagemProvider.COLUMN_DE);
idDestinatarioColumnIndex = cursor.getColumnIndex(MensagemProvider.COLUMN_PARA);
apelidoRemetenteColumnIndex = cursor.getColumnIndex(MensagemProvider.COLUMN_APELIDO_REMETENTE);
apelidoDestinatarioColumnIndex = cursor.getColumnIndex(MensagemProvider.COLUMN_APELIDO_DESTINATARIO);
textoMensagemColumnIndex = cursor.getColumnIndex(MensagemProvider.COLUMN_MENSAGEM);
}
notifyDataSetChanged();
return cursor;
}
}
what I'm doing wrong ? Can anybody help me ?
Thanks!
Overriding swapCursor() is asking for trouble. The cursor won't be positioned where the adapter expects it to be positioned (before first). And you don't call super.swapCursor() so the adapter never really hears about the new cursor.
I bet you're trying to "optimize" by getting the column indexes only once each time a new cursor is swapped.
First just try getting rid of the swapCursor() override and making the getColumnIndex() calls in your getItem() method. If that works and you still really want to have getColumnIndex() called only once per cursor, you could try something like setting all your cursor indexes to -1 when you swap the cursor, then calling getColumnIndex() inside getItem() only when the index is -1.
But don't mess with swapCursor(), especially without calling super.swapCursor() and returning its result.
So my app uses a local SQLite db through a contentProvider
In it's mainActivity I have a listView displaying the contents of a table from the above DB.
I use a custom adapter to display the listview. Each item has a button in it's (custom) layout, that when pressed, shows a custom dialog that inserts a new record in that table, then the dialog gets dismissed.
In order to achieve this behavior, I placed the button click handler inside the customAdapter.
I would like to be able to refresh the listView after the inserting is done (so when dialog is dismissed)
How can I achieve this?
I should probably need to call notifyDataSetChanged somehow from inside the custom Adapter but I can't.
In short, my custom adapter looks like this:
public class DisplayStuffAdapter extends BaseAdapter {
private Context mContext;
private ArrayList<String> id;
private ArrayList<String> iduser;
private ArrayList<String> product;
public DisplayStuffAdapter(Context c){
this.mContext = c;
}
public DisplayStuffAdapter(Context c, ArrayList<String> id, ArrayList<String> userid, ArrayList<String> product) {
this.mContext = c;
this.id = id;
this.userid = userid;
this.product = product;
}
public int getCount() {
return id.size();
}
public Object getItem(int position) {
return null;
}
public long getItemId(int position) {
return 0;
}
public class Holder {
TextView txt_id;
TextView txt_userid;
TextView txt_prod;
}
public View getView(int pos, View child, ViewGroup parent) {
Holder mHolder;
LayoutInflater layoutInflater;
if (child == null) {
layoutInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
child = layoutInflater.inflate(R.layout.myitem, null);
mHolder = new Holder();
mHolder.txt_id = (TextView) child.findViewById(R.id.tv_MkId);
mHolder.txt_userid = (TextView) child.findViewById(R.id.tv_MkUserId);
mHolder.txt_prod = (TextView) child.findViewById(R.id.tv_MkProduct);
child.setTag(mHolder);
} else {
mHolder = (Holder) child.getTag();
}
mHolder.txt_id.setText(id.get(pos));
mHolder.txt_userid.setText(userid.get(pos));
mHolder.txt_prod.setText(product.get(pos));
Button bt = (Button) child.findViewById(R.id.itemButton);
bt.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
LayoutInflater li = LayoutInflater.from(mContext);
final View promptsView = li.inflate(R.layout.bid_dialog, null);
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(mContext);
alertDialogBuilder.setView(promptsView);
alertDialogBuilder.setMessage("Input data")
.setIcon(R.drawable.add_red_24)
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.dismiss();
}
})
.setPositiveButton("Add new record", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
ContentValues values = new ContentValues();
values.put(MyProvider.TCOL_ID, myid);
values.put(MyProvider.TCOL_OTHERID, Integer.toString(getActiveUserId()));
Uri uri = mContext.getContentResolver().insert(MyProvider.CONTENT_URI_TABLE, values);
values = new ContentValues();
dialogInterface.dismiss();
}
}
}
});
// create alert dialog
final AlertDialog alertDialog = alertDialogBuilder.create();
// show it
alertDialog.show();
alertDialog.setCanceledOnTouchOutside(false);
....
}
});
....
I remove some parts from the code to make it more readable.
Now, in my MainActivity, I set the adapter like this:
public class MainActivity extends Activity{
private ArrayList<String> id = new ArrayList<String>();
private ArrayList<String> userid = new ArrayList<String>();
private ArrayList<String> product = new ArrayList<String>();
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fillListView();
}
...
private void fillListView(){
id.clear();
userid.clear();
product.clear();
String[] col = {MyProvider.TCOL_ID_ID, MyProvider.TCOL_USERID, MyProvider.TCOL_PROD};
String where = "done = 1";
Cursor mCursor = MainActivity.this.getContentResolver().query(MyProvider.CONTENT_URI_TABLE, col, where, null, MyProvider.TCOL_DATE + " desc");
if (mCursor != null) {
if (mCursor.moveToFirst()) {
do {
id.add(Integer.toString(mCursor.getInt(0)));
userid.add(Integer.toString(mCursor.getInt(1)));
product.add(mCursor.getString(2));
} while (mCursor.moveToNext());
}
}
DisplayStuffAdapter disadpt = new DisplayStuffAdapter(MainActivity.this,id,userid,product);
disadpt.notifyDataSetChanged();
ListView lv = (ListView) findViewById(R.id.mylistView);
lv.setAdapter(disadpt);
}
So this all works great, except that when I add a new record to the table using the customdialog described above... the dialog closes, and the listview remains unchanged.
How can I refresh the listView?
In general, when you're querying data from a DB, you should use ContentProvider and CursorLoader. You can configure your content provider to automatically notify loaders when some data changes, using ContentResolver notifyChange() method. Place the call to this method in your ContentProvider implementation (for example after the insert). This is an example of adapter you can use (but you can use SimpleCursorAdapter providing a view binder too).
public class CustomCursorAdapter extends CursorAdapter {
private LayoutInflater mInflater;
public CustomCursorAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
if(cursor.getPosition()%2==1) {
view.setBackgroundColor(context.getResources().getColor(R.color.background_odd));
}
else {
view.setBackgroundColor(context.getResources().getColor(R.color.background_even));
}
TextView content = (TextView) view.findViewById(R.id.row_content);
content.setText(cursor.getString(cursor.getColumnIndex(Table.CONTENT)));
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return mInflater.inflate(R.layout.listitem, parent, false);
}
}
public class DisplayStuffAdapter extends BaseAdapter {
private Context mContext;
private ArrayList<String> id;
private ArrayList<String> iduser;
private ArrayList<String> product;
public DisplayStuffAdapter(Context c){
this.mContext = c;
}
public void loadData(){
id.clear();
userid.clear();
product.clear();
String[] col = {MyProvider.TCOL_ID_ID, MyProvider.TCOL_USERID, MyProvider.TCOL_PROD};
String where = "done = 1";
Cursor mCursor = MainActivity.this.getContentResolver().query(MyProvider.CONTENT_URI_TABLE, col, where, null, MyProvider.TCOL_DATE + " desc");
if (mCursor != null) {
if (mCursor.moveToFirst()) {
do {
id.add(Integer.toString(mCursor.getInt(0)));
userid.add(Integer.toString(mCursor.getInt(1)));
product.add(mCursor.getString(2));
} while (mCursor.moveToNext());
}
}
notifyDataSetChanged();
}
...
}
public class MainActivity extends Activity{
private DisplayStuffAdapter disadpt = null;
ContentObserver displayStuffObserver = new ContentObserver(new Handler()){
#Override
public void onChange(boolean selfChange) {
if(disadpt != null) {
disadpt.loadData();
}
}
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
disadpt = new DisplayStuffAdapter(this);
ListView lv = (ListView) findViewById(R.id.mylistView);
lv.setAdapter(disadpt);
disadpt.loadData();
getContentResolver().registerContentObserver(MyProvider.CONTENT_URI_TABLE,true, displayStuffObserver);
}
}
Do not forget to unregister your content observer
First of all, I am not getting why you are sending three different arraylists to the adapter. You can simply make a modal class having all the fields that you require in your adapter. Considering your current scenario it will be something like this
public class ModalClass {
private String id = "";
private String userId = "";
private String product = "";
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getProduct() {
return product;
}
public void setProduct(String product) {
this.product = product;
}
}
So this is your modal class with getters and setters. Now all you have to do is you have to make an ArrayList of this modal class like this
List<ModalClass> modalClassList=new ArrayList<ModalClass>();
and you have to set all the data that you want to display in your list in this arraylist by making use of modal class setter functions. Like this
if (mCursor != null) {
if (mCursor.moveToFirst()) {
do {
ModalClass modalClass=new ModalClass();
modalClass.setId(Integer.toString(mCursor.getInt(0)));
modalClass.setUserId(Integer.toString(mCursor.getInt(1)));
modalClass.setProduct(mCursor.getString(2));
modalClassList.add(modalClass);
} while (mCursor.moveToNext());
}
}
and now you have your arraylist ready, so you can set it to your listview like this
ListView lv = (ListView) findViewById(R.id.mylistView);
DisplayStuffAdapter disadpt = new DisplayStuffAdapter(MainActivity.this,modalClassList);
lv.setAdapter(disadpt);
And accordingly you have to modify your adapter constructor which i think you can do by your own.
Also how to set values in your adapter, you can make use of your modal class getter methods like this.
ModalClass modalClass=modalClassList.get(pos);
mHolder.txt_id.setText(modalClass.getId());
mHolder.txt_userid.setText(modalClass.getUserId());
mHolder.txt_prod.setText(modalClass.getProduct());
Now when you want to insert a new row in your adapter, you have to simply create an object of ModalClass and set all the new values in that like we have done in our MainActivity class and then finally add that to your modalClassList followed by notifyDataSetChanged();
ModalClass modalClass=new ModalClass();
modalClass.setId(yourNewInsertedRowId);
modalClass.setUserId(yourNewInsertedRowUserId);
modalClass.setProduct(yourNewInsertedRowProduct);
modalClassList.add(modalClass);
notifyDataSetChanged();
And this time your list will be notify for sure. Cheers :)
OK so I have a rather annoying issue. I am simply attempting to list all the songs on the device using a CursorLoader and LoaderCallbacks. My problem is simply that nothing is being displayed. I am using EXACTLY the same method as I am to load all albums, artists and playlists on the device. Using some debugging I have discovered that the problem is that newView() is only being called once within the CursorAdapter
Here is my CursorAdapter:
private class SongItemAdapter extends CursorAdapter
{
public SongItemAdapter(Context context)
{
super(context, null, false);
}
#Override
public void bindView(View view, Context context, Cursor cursor)
{
final int albumId = cursor.getInt(cursor
.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID));
final String songName = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.TITLE));
final String artistName = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST));
final String duration =
Utilities.milliSecondsToTimer(
cursor.getLong(cursor.getColumnIndex(MediaStore.Audio.Media.DURATION)));
final ImageView albumCover = (ImageView) view.findViewById(R.id.all_songs_album_cover);
final TextView songNameTextView = (TextView) view.findViewById(R.id.all_songs_song_name);
final TextView artistNameTextView = (TextView) view.findViewById(R.id.all_songs_artist_name);
final TextView durationTextView = (TextView) view.findViewById(R.id.all_songs_song_duration);
ImageLoader.getInstance().displayImage(
ContentUris.withAppendedId(
sArtworkUri, albumId).toString(), albumCover);
songNameTextView.setText(songName);
artistNameTextView.setText(artistName);
durationTextView.setText(duration);
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent)
{
return LayoutInflater.from(context).inflate(R.layout.song_list_item, parent, false);
}
}
Here is my LoaderCallbacks:
private final LoaderCallbacks<Cursor> mCursorCallbacks = new LoaderCallbacks<Cursor>()
{
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args)
{
return new CursorLoader(getActivity(),
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, Song.FILLED_PROJECTION, MediaStore.Audio.Media.IS_MUSIC + "!=0", null,
MediaStore.Audio.Media.TITLE + " ASC");
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data)
{
mAdapter.swapCursor(data);
}
#Override
public void onLoaderReset(Loader<Cursor> loader)
{
mAdapter.swapCursor(null);
}
};
And here is the initialisation of my adapter and listview and whatnot:
mAdapter = new SongItemAdapter(this.getActivity());
mListView = (ListView) rootView.findViewById(R.id.all_songs_list);
mListView.setOnItemClickListener(this);
mListView.setOnScrollListener(new PauseOnScrollListener(ImageLoader.getInstance(), false, false));
mListView.setAdapter(mAdapter);
mListView.setFastScrollEnabled(true);
mListView.setFastScrollAlwaysVisible(true);
mListView.setRecyclerListener(new RecyclerListener()
{
#Override
public void onMovedToScrapHeap(View view)
{
// Release strong reference when a view is recycled
final ImageView imageView = (ImageView) view.findViewById(R.id.all_songs_album_cover);
imageView.setImageBitmap(null);
}
});
getLoaderManager().initLoader(LOADER_CURSOR, null, mCursorCallbacks);
As I stated previously, this is exactly the method I use for loading albums, artists and playlists and those are working fine.