Updating ListView to strikethough item on click - android

I am using a CursorAdapter with a ListView and a cursor getting data from an sqlite database. I have this function called RenderList() which I call every time I update the database with a new item for the list or if I set the checked value of a row to one (this will add the new item or strikethough the item name).
private void renderList(){
String showWhere = show_checked ? null : DbHelper.C_CHECKED + "= '0' ";
try {
db = dbHelper.getReadableDatabase();
cursor = db.query(DbHelper.TABLE, null, showWhere, null, null, null, dbHelper.C_ID + " DESC");
groceriesList = (ListView)findViewById(R.id.listView1);
adapter = new GroceryAdapter(this, cursor);
adapter.newView(getApplicationContext(), cursor, groceriesList);
groceriesList.setAdapter(adapter);
groceriesList.setOnItemClickListener(itemListener);
} catch (Exception e) {
Log.d(TAG, "RenderList Error: ",e);
}
}
This will reset the list, so if I click an item that is way down the listview it will reset the listview to the top position. Obviously I'm missing something with how to update the listview, and the database in an efficient, and usable way?
public class GroceryAdapter extends CursorAdapter {
private final LayoutInflater mInflater;
public GroceryAdapter(Context context, Cursor cursor) {
super(context, cursor, true);
mInflater = LayoutInflater.from(context);
// mContext = context;
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
TwoLineListItem listItem = (TwoLineListItem)view;
TextView t1 = listItem.getText1();
TextView t2 = listItem.getText2();
t1.setText(cursor.getString(cursor.getColumnIndex(DbHelper.C_GROCERY)));
t2.setText("Added by: Wes");
t1.setTag(cursor.getInt(cursor.getColumnIndex(DbHelper.C_ID)));
t2.setTag(cursor.getInt(cursor.getColumnIndex(DbHelper.C_CHECKED)));
if (cursor.getInt(cursor.getColumnIndex(DbHelper.C_CHECKED)) == 1 ) {
t1.setPaintFlags(t1.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
listItem.setBackgroundColor(0xEECCCCCC);
} else {
t1.setPaintFlags(t1.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG) );
listItem.setBackgroundColor(0x00000000);
}
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
final View view = mInflater.inflate(R.layout.grocery_list_item, parent, false);
return view;
}
}

Related

How do I change custom CursorAdapter to display only specific rows in the listview for a given fragment?

Three fragments in my app, Fragment1, Fragment2, Fragment3 display contents of a single table in a listview, using a single custom CursorAdapter class, TaskCursorAdapter. Here is the class:
public class TaskCursorAdapter extends CursorAdapter {
public TaskCursorAdapter(Context context, Cursor c) {
super(context, c, 0 /* flags */);
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return LayoutInflater.from(context).inflate(R.layout.list_item_task, parent, false);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
TextView titleTextView = (TextView) view.findViewById(R.id.task_title);
TextView detailsTextView = (TextView) view.findViewById(R.id.task_details);
int titleColumnIndex = cursor.getColumnIndex(TaskEntry.COLUMN_TASK_TITLE);
int detailsColumnIndex = cursor.getColumnIndex(TaskEntry.COLUMN_TASK_DETAILS);
String taskTitle = cursor.getString(titleColumnIndex);
String taskDetails = cursor.getString(detailsColumnIndex);
if (TextUtils.isEmpty(taskDetails)) {
taskDetails = context.getString(R.string.unknown_task);
}
titleTextView.setText(taskTitle);
detailsTextView.setText(taskDetails);
}
}
The table is specified in the Contract class as TaskEntry. It also has another column named TaskEntry.COLUMN_TASK_STATUS="status". The possible values are 0, 1 or 2. Currently, all the items are displayed in both fragments. But, I want to make it so that only the rows with status=0 are displayed in Fragment1, those with status=1 in Fragment2 and those with status=2 in Fragment3.
I tried the following in bindView method:
int taskStatus = Integer.parseInt(cursor.getString(cursor.getColumnIndex(TaskEntry.COLUMN_TASK_STATUS)));
if(taskStatus==0) { //code in bindView }
This resulted in displaying only the items with status=0 in all fragments, but it left an empty inflated view in place of the item with status other than 0.
Also, I cannot find a way to pass the information to make it specific to Fragment1.
How should I conditionally display rows based on status value and fragment?
EDIT:
What worked:
Instead of trying this in TaskCursorAdapter, I used conditional query in onCreateLoader method like the following in each fragment:
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
String selectionArgs[] = {"<status>"};
String[] projection = {
TaskEntry._ID,
TaskEntry.COLUMN_TASK_TITLE,
TaskEntry.COLUMN_TASK_DETAILS};
return new CursorLoader(this.getActivity(), TaskEntry.CONTENT_URI, projection,
TaskEntry.COLUMN_TASK_STATUS + " = ?", selectionArgs, null);
}
Try this:
public class TaskCursorAdapter extends CursorAdapter {
private int statusCode;
public TaskCursorAdapter(Context context, Cursor c) {
super(context, c, 0 /* flags */);
}
public setStatusCode(int statusCode){
this.statusCode = statusCode;
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
int currentStatusCode = Integer.parseInt(cursor.getString(cursor.getColumnIndex(TaskEntry.COLUMN_TASK_STATUS)));
if(statusCode == currentStatusCode){
return LayoutInflater.from(context).inflate(R.layout.list_item_task, parent, false);
} else return null;
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
if(view != null){
TextView titleTextView = (TextView) view.findViewById(R.id.task_title);
TextView detailsTextView = (TextView) view.findViewById(R.id.task_details);
int titleColumnIndex = cursor.getColumnIndex(TaskEntry.COLUMN_TASK_TITLE);
int detailsColumnIndex = cursor.getColumnIndex(TaskEntry.COLUMN_TASK_DETAILS);
String taskTitle = cursor.getString(titleColumnIndex);
String taskDetails = cursor.getString(detailsColumnIndex);
if (TextUtils.isEmpty(taskDetails)) {
taskDetails = context.getString(R.string.unknown_task);
}
titleTextView.setText(taskTitle);
detailsTextView.setText(taskDetails);
}
}
}
and do this in each of your fragments, passing in your status codes, respectively:
yourAdapter = new TaskCursorAdapter(this, yourDataCursor);
yourAdapter.setStatusCode(YOUR_STATUS_CODE);
yourListView.setAdapter(yourAdapter);
EDIT (It turns out we can't return null from CursorAdapter#newView()
So I guess you are going to have to filter your cursor in each Fragment before instantiating a new TaskCursorAdapter, and pass in your filtered cursor instead of your original cursor. You can use CursorWrapper class for that. This answer might give you an idea: https://stackoverflow.com/a/7343721/8354184.

Request multiple cells from SQLite database in android

I am trying to call data from two different cells in my database then combine them and print them out in an activity.
I am using the following code:
public Cursor getGermanDescription(String id) {
String[] args = { id };
return (getReadableDatabase()
.rawQuery(
"SELECT _id,Column1,Column2 FROM Databasing_Details WHERE _id=?",
args));
With the above I am only getting the content of Column1 but not Column2. I am passing the String id to another activity.
My cursor adapter is:
#Override
public void onCreate(Bundle savedInstanceState) {
try {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Get our passed variable from our intent's EXTRAS
passedVar=getIntent().getStringExtra(ListViewTwo.ID_EXTRA);
//this is our ListView element, obtained by id from our XML layout
ListView myListView = (ListView)findViewById(R.id.list_view);
String string = passedVar;
int passedInt = Integer.parseInt(string);
if (passedInt==1) { passedVar1 = true;
}
creating our database Helper:
dbDescriptionHelper=new DatabaseHelper(this);
//a set of results from a database query
ourCursor=dbDescriptionHelper.getGermanDescription(passedVar);
//tell android to start managing the cursor,
startManagingCursor(ourCursor);
//create our adapter
adapter=new SlangAdapter(ourCursor);
//set the adapter!!!
myListView.setAdapter(adapter);
} catch (Exception e) {
Log.e("ERROR", "ERROR IN CODE: " + e.toString());
e.printStackTrace();
}
return;
}
The slangAdapterClass:
class SlangAdapter extends CursorAdapter {
SlangAdapter(Cursor c) {
super(ListViewFinal.this, c);
}
#Override
public void bindView(View row, Context ctxt,
Cursor c) {
DescriptionHolder holder=(DescriptionHolder)row.getTag();
holder.populateFrom(c, dbDescriptionHelper);
}
#Override
public View newView(Context ctxt, Cursor c,
ViewGroup parent) {
LayoutInflater inflater=getLayoutInflater();
View row=inflater.inflate(R.layout.main_row, parent, false);
DescriptionHolder holder=new DescriptionHolder(row);
row.setTag(holder);
return(row);
}
}
The DescriptionHolder class:
static class DescriptionHolder {
private TextView name=null;
DescriptionHolder(View row) {
name=(TextView)row.findViewById(R.id.row);
}
void populateFrom(Cursor c, DatabaseHelper r) {
name.setText(r.getName(c));
}
}
Could someone point out where I am going wrong please.
OK, I have found the answer.
My syntax was incorrect, what I needed to write for my cursor was:
public Cursor getGermanDescription(String id) {
String[] args = { id };
return (getReadableDatabase()
.rawQuery(
"SELECT _id, ObjectDescriptionGerman ||'\n\n'|| ObjectDescription FROM Databasing_Details WHERE _id=?",
args));
}
The || needs to be used instead of "," or "AND". I have also inserted line breaks between my two returned value so I do not need to do this in my database.

How to notify cursor adapter of updated database

The way my gridview activity currently stands in that on my onCreate method I kick of a async task that searches for all the videos on my phone and then stores the id and filepath in a SQLite database using a do while loop. I then call a cursor adapter that uses a background thread to use the file path to create thumbnails and display them in the gridview. However, I'm running into the problem that when I first start up the activity nothing displays in the gridview. However, when I open it again everything will display. So my problem is that when the activity first starts the cursor has nothing in it so nothing can be displayed.
My question is how would I go about updating the cursor adapter as the new data is being entered in the background thread? How could I trigger the cursor adapter to use the data that was just entered in the background thread? My code is posted below (sorry its a little sloppy).
OnCreate
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.preview);
GridView gridview = (GridView) this.findViewById(R.id.gridview);
cursor = getContentResolver().query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, mediaColumns, null, null, null);
columnindexid = cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID);
columnindexdata = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA);
entry = new GridviewData(this);
entry.open();
DataEntry putitin = new DataEntry(entry);
putitin.execute();
//the cursor used in the cursor adapter
Cursor curs = entry.adapterCursor();
videoidindex = entry.Indexfinder(curs);
videopathindex = entry.Indexfinder2(curs);
config = new ImageLoaderConfiguration.Builder(this)
.imageDownloader(new BaseImageDownloader(this))
.build();
ImageLoader.getInstance().init(config);
Log.i(TAG, "Before set adapter");
gridview.setAdapter(new VideoAdapter(this, curs, flags));
}
Asynctask that puts data into database
private class DataEntry extends AsyncTask<Void, Integer, GridviewData>{
Cursor cursor;
GridviewData dataentry;
DataEntry(GridviewData gridviewdata){
this.dataentry = gridviewdata;
this.cursor = getContentResolver().query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
mediaColumns, null, null, null);
columnindexid = cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID);
columnindexdata = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA);
}
#Override
protected GridviewData doInBackground(Void... params) {
cursor.moveToFirst();
do {
String videoid = cursor.getString(columnindexid);
String videopath = cursor.getString(columnindexdata);
int result = dataentry.findVideoID(videoid);
if (result == 1){
//this is where data is put into the database
dataentry.addVideoinfo(videoid, videopath);
}
if (result == 0){
Log.i(TAG, "Cursor wasn't processed, no getcount");
}
if(result == 2){
Log.i(TAG, "The data is already there");
}
} while (cursor.moveToNext());
Log.i(TAG, "After dowhile loop");
cursor.close();
return dataentry;
}
}
Cursor adapter
class VideoAdapter extends CursorAdapter {
Context context;
public VideoAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
this.context = context;
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
ViewHolder holder = (ViewHolder) view.getTag();
String fileid = cursor.getString(videoidindex);
String filepath = cursor.getString(videopathindex);
BitmapDownloader bitdl = new BitmapDownloader(fileid, filepath, holder.imageview);
bitdl.execute();
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View eachgrid = inflater.inflate(R.layout.eachgrid, parent, false);
ViewHolder holder = new ViewHolder();
holder.imageview = (ImageView) eachgrid
.findViewById(R.id.Imageview1);
eachgrid.setTag(holder);
return eachgrid;
}
Call notifyDataSetChanged on your adapter after your background thread is done.
Notifies the attached observers that the underlying data has been
changed and any View reflecting the data set should refresh itself.

couple questions about custom arrayadapter

so im a little confused here....
i have code that takes info from my sqlite database and populates a list, then shows the list using the standard array adapter. what i want to do is have it so that in this list, the row color is green if the "completed" table row value is "yes"
heres my db structure for the table being used:
String CREATE_ACHIEVEMENTS_TABLE = "CREATE TABLE achievements ("
+ "id INTEGER PRIMARY KEY,"
+ "name VARCHAR,"
+ "type VARCHAR,"
+ "value VARCHAR,"
+ "completed VARCHAR"
+ ")";
heres my code that gets the list from the db:
public ArrayList<String> getAchievements(Context context) {
ArrayList<String> achievementList = new ArrayList<String>();
String selectQuery = "SELECT * FROM achievements ORDER BY id asc";
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery(selectQuery, null);
if (cursor != null) {
if (cursor.moveToFirst()) {
do {
if (cursor.getString(4).equals("yes")) {
achievementList.add(cursor.getString(1)+" (completed)");
}
else {
achievementList.add(cursor.getString(1));
}
} while (cursor.moveToNext());
}
}
else {
achievementList.add(context.getResources().getString(R.string.na));
}
cursor.close();
db.close();
return achievementList;
}
heres my custom arrayadapter:
public class AchievementAdapter extends ArrayAdapter<String> {
private final Context context;
private final String[] values;
public AchievementAdapter(Context context, String[] values) {
super(context, R.layout.achievements, values);
this.context = context;
this.values = values;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
if (row == null) {
LayoutInflater inflater = (LayoutInflater)this.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
row = inflater.inflate(R.layout.achievement_item, parent, false);
}
return row;
}
}
i really have no clue where to go from here. this is my first android app and i have learned a LOT, but i cant seem to figure out how to achieve this simple thing in regards to custom arrayadapters....all the tutorials i find contain a bunch of features that i dont want. all im trying to do is make the text color of the list item green if its "completed" table value is "yes"...
First of all, I recommend using a cursorAdapter instead of an arrayAdapter. With a cursor adapter you will have a pointer to the DB so you can get all of the information from there.
If you do that... your code for the adapter should look something like this.
private class MyCursorAdapter extends CursorAdapter {
public MyCursorAdapter(Context context, Cursor c) {
super(context, c);
}
#Override
public void bindView(View v, Context context, Cursor cursor) {
if(cursor.getString(cursor.getColumnIndex("completed").equals("yes")){
TextView tv = (TextView) v.findViewById(R.id.NAMEOFTEXTVIEW);
tv.setTextColor(Color.GREEN);
}
}
#Override
public View newView(Context arg0, Cursor arg1, ViewGroup arg2) {
LayoutInflater inflater = (LayoutInflater)this.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
row = inflater.inflate(R.layout.achievement_item, parent, false);
return row;
}
}
and you create the adapter with:
Cursor cursor = db.rawQuery(selectQuery, null);
mAdapter = new MyCursorAdapter(this, cursor);
Having said all that... if you want to use the arrayAdapter and just change the textview,
in getView:
String item = (String) getItem(position);
if(item.contains("(completed)"){
TextView tv = (TextView) row.findViewById(R.id.NAMEOFTEXTVIEW);
tv.setTextColor(Color.GREEN);
}
I should note that with a cursorAdapter you should keep the cursor open, and close it in onStop (reopen it in onRestart)

Android ViewHolder in CursorAdapter causing listView to get screwed

I've been struggeling in the past few days trying to figure this out, I hope you can help me...
I have an Activity that shows a list of Players by setting a listadapter like this:
PlayerCursorAdapter playerAdapter = new PlayerCursorAdapter(this,
R.layout.players_row, c, columns, to);
setListAdapter(playerAdapter);
When clicking an item in the list, this code will be executed showing a dialog with an "Edit" and "Delete" option for editing and removing players:
private class OnPlayerItemClickListener implements OnItemClickListener {
public void onItemClick(AdapterView<?> parent, View view, int position,
long rowId) {
Toast.makeText(view.getContext(),
"Clicked Item [" + position + "], rowId [" + rowId + "]",
Toast.LENGTH_SHORT).show();
// Prepare Dialog with "Edit" and "Delete" option
final CharSequence[] choices = {
view.getContext().getString(R.string.buttonEdit),
view.getContext().getString(R.string.buttonDelete) };
AlertDialog.Builder builder = new AlertDialog.Builder(
view.getContext());
builder.setTitle(R.string.title_edit_delete_player);
builder.setItems(choices, new EditOrDeleteDialogOnClickListener(
view, rowId));
AlertDialog alert = builder.create();
// Show Dialog
alert.show();
}
Based on your choice (Edit or delete player), the following listener will be executed:
private class EditOrDeleteDialogOnClickListener implements
DialogInterface.OnClickListener {
private View view;
private long rowId;
public EditOrDeleteDialogOnClickListener(View view, long rowId) {
this.view = view;
this.rowId = rowId;
}
public void onClick(DialogInterface dialog, int item) {
if (item == 0) {
// Edit
showDialog(PlayGameActivity.DIALOG_EDIT_PLAYER_ID);
} else if (item == 1) {
// Delete from database
DatabaseHelper databaseHelper = new DatabaseHelper(
view.getContext());
databaseHelper.deletePlayer(rowId);
// Requery to update view.
((PlayerCursorAdapter) getListAdapter()).getCursor().requery();
Toast.makeText(
view.getContext(),
view.getContext().getString(
R.string.message_player_removed)
+ " " + rowId, Toast.LENGTH_SHORT).show();
}
}
}
The code for the adapter is here:
public class PlayerCursorAdapter extends SimpleCursorAdapter {
private LayoutInflater layoutInflater;
private int layout;
public PlayerCursorAdapter(Context context,
int layout, Cursor c, String[] from, int[] to) {
super(context, layout, c, from, to);
this.layout = layout;
layoutInflater = LayoutInflater.from(context);
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
Cursor c = getCursor();
View view = layoutInflater.inflate(layout, parent, false);
// Get Data
int nameCol = c.getColumnIndex(Player.COLUMN_PLAYER_NAME);
String name = c.getString(nameCol);
int gamesPlayedCol = c.getColumnIndex(Player.COLUMN_GAMES_PLAYED);
String gamesPlayed = c.getString(gamesPlayedCol);
int gamesWonCol = c.getColumnIndex(Player.COLUMN_GAMES_WON);
String gamesWon = c.getString(gamesWonCol);
// Set data on fields
TextView topText = (TextView) view.findViewById(R.id.topText);
if (name != null)
topText.setText(name);
TextView bottomText = (TextView) view.findViewById(R.id.bottomText);
if (gamesPlayed != null && gamesWon != null)
bottomText.setText(view.getContext().getString(
R.string.info_played_won)
+ gamesPlayed + "/" + gamesWon);
CheckBox checkBox = (CheckBox) view.findViewById(R.id.checkBox);
// Set up PlayerViewHolder
PlayerViewHolder playerViewHolder = new PlayerViewHolder();
playerViewHolder.playerName = name;
playerViewHolder.gamesPlayed = gamesPlayed;
playerViewHolder.gamesWon = gamesWon;
playerViewHolder.isChecked = checkBox.isChecked();
view.setTag(playerViewHolder);
return view;
}
private class PlayerViewHolder {
String playerName;
String gamesPlayed;
String gamesWon;
boolean isChecked;
}
#Override
public void bindView(View view, Context context, Cursor c) {
PlayerViewHolder playerViewHolder = (PlayerViewHolder) view.getTag();
TextView topText = (TextView) view.findViewById(R.id.topText);
topText.setText(playerViewHolder.playerName);
TextView bottomText = (TextView) view.findViewById(R.id.bottomText);
bottomText.setText(view.getContext()
.getString(R.string.info_played_won)
+ playerViewHolder.gamesPlayed
+ "/"
+ playerViewHolder.gamesWon);
CheckBox checkBox = (CheckBox) view.findViewById(R.id.checkBox);
checkBox.setChecked(playerViewHolder.isChecked);
}
}
Now, the problem is that after removing a few of the players in the list, the list gets screwed up, eg. it shows something different than what is actually available.
I've experimented a little and if I stop using the PlayerViewHolder in bindView and instead read the text from the cursor and assign it directly to the text fields, then it works.... So question is, why is my ViewHolder screwing up things???
Any help will be greatly appreciated!
Thanks!
Zyb3r
Found a solution...
Basically I reinitialize the Cursor and ListAdapter plus assigns the ListAdapter to the ListView all over again when I change the data in the database.
I'm not entirely sure why this is nessasary, but notifyDataSetChanged(), notifyDataSetInvalidated() and all the other things I tried didn't work, so now I'm using this approach. :o)
Zyb3r

Categories

Resources