I have a ListView that is fed by an SQLiteDB. On each row returned, I have a clickable TextView that onClick should delete the item on that row. So I need to find the sql rowid, so I can call deleteItem(rowid).
I've tried putting in an invisible TextView that I fill with the sql rowid as the ListView is populated, it works. But I set content view to R.layout.list_view and the invisible TextView is in R.layout.list_item (custom layout for each item line), so I can't seem to access it.
How do I go about making this work or is there a better way?
Here is the code that populates the listview:
private void fillData() {
// Get all of the notes from the database and create the item list
Cursor c = mDbHelper.fetchAllNotes();
startManagingCursor(c);
String[] from = new String[] { CommonDbAdapter.KEY_NOTES };
int[] to = new int[] { R.id.text1 };
// Now create an array adapter and set it to display using our row
SimpleCursorAdapter notes =
new SimpleCursorAdapter(this, R.layout.notes_row, c, from, to);
setListAdapter(notes);
}
Here is my current click listener, I'm not sure how to differentiate between a click on the item, which returns the item to another activity and a click on the textview that would delete the item.
itemList = (ListView) findViewById(android.R.id.list);
itemList.setTextFilterEnabled(true);
itemList.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> a, View v, int position, long id) {
Intent intent = new Intent();
Bundle b = new Bundle();
b.putString("TEXT", ((TextView)v.findViewById(R.id.text1)).getText().toString());
intent.putExtras(b);
setResult(SUCCESS_RETURN_CODE, intent);
finish();
}
});
I was originally trying to get the rowid and use a simple onClick set in the layout. But had problems getting the rowid.
public void clickDelete(View view) {
mDbHelper.deleteNote(rowid);
}
here is a quick way to get a click listener for just your textview:
replace your lines:
SimpleCursorAdapter notes = new SimpleCursorAdapter(this, R.layout.notes_row, c, from, to);
setListAdapter(notes);
with this:
setListAdapter(new SimpleCursorAdapter(this, R.layout.notes_row, c, from, to) {
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = super.getView(position, convertView, parent);
final TextView t = (TextView)v.findViewById(R.id.text1);
t.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(v.getContext(), "Hello " + t.hashCode(), Toast.LENGTH_SHORT).show();
}
});
return v;
}
});
now, your text1 view will have an onclick listener, and you can do whatever you want. you need to store the rowId somewhere accessible from inside the onClick method though.
this doesn't exactly answer the question i guess, but it gives you a click listener for an individual view in the listview.
you really should implement a custom CursorAdapter class to do something like this, although you could hack around and retrieve the rowId from somewhere
or you could instead use an invisible textview and add an onclick listener to it(like i've shown above), grab it's value(which is a rowId), and perform an operation.
Are you using a CursorAdapter? If so you can query the Cursor directly for the rowid.
You may want to use OnItemClickListener instead of an OnClickListener. This will set you up with the ListView, the position and the id. I believe if you are using a CursorAdapter that the id given to you is the actual rowid of the row in the database.
Edit: if using OnItemClickListener is not possible, you'll have to change to CursorAdapter from SimpleCursorAdapter to do this. In your bindView method you'll have access to the rowid of the View you are creating, and you can reference this in the OnClickListener you create.
Related
I have a ListView backed by a SQLite db table which displays a song title (TextView) and a button (ImageButton) in the list item XML for each row. I setup an onClick() event for the button to take action on the song on the row they clicked on.
The ListView correctly displays all contents from the db table using a SimpleCursorAdapter.
Here's the question: when I click on the ImageButton it correctly calls the onClick event for the button with parameter (View view); how do I get to the Cursor row data for the row in which the button was clicked from the View passed into the event? I need the row _ID value in order to act on the correct song. I also have access to the dbAdapter in a Class field if I can get there from that object.
Notes: When I add an ImageButton to the Item List the onItemClickListener no longer fires if I click on the row containing the song or on the button.
And please, if there is a better design pattern to give the user the functionality of selecting a ListView item and performing an action on it, let me know. My intention is to eventually add 2-3 buttons per row for Delete, Info, Play, etc.
// Load ListView with previously downloaded files
dbHelper = new DBHelper(this);
// Create Cursor holding db data
Cursor cursor = dbHelper.fetchData();
// Map db columns to view ids
String[] columns = new String[]{
DBContract.Songs.COLUMN_NAME_NAME,
DBContract.Songs.COLUMN_NAME_LOADED_DATETIME
};
int[] to = new int[]{
R.id.songName,
R.id.songDateLoaded
};
// Create the dbAdapter
dbAdapter = new SimpleCursorAdapter(this, R.layout.songs, cursor, columns, to, 0);
// Assign the adapter to the ListView
ListView listView = findViewById(R.id.songsListView);
listView.setAdapter(dbAdapter);
// Anonymous OnItemClickListener
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {...
With Mike's persistent and patient assistance, I implemented the solution below.
The key is using a setViewBinder() on the SimpleCursorAdapter and then assigning the db row key to the Tag property of the ImageButton. Then, in XML, define an onClick() event and in that event, you now have access to the db row key from the view.getTag() method.
// Bind the Cursor record _ID to the ImageButton Tag property;
// So when it is called, we can delete the record with the Tag property value.
dbAdapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() {
#Override
public boolean setViewValue(View view, Cursor cursor, int i) {
if (view.getId() == R.id.songName) {
final long id = cursor.getLong(cursor.getColumnIndex(DBContract.Songs._ID));
final ImageButton delete = ((View)view.getParent()).findViewById(R.id.deleteSongButton);
delete.setTag(id);
}
return false;
}
});
public void DeleteSong(View view) {
final long id = (long) view.getTag();
dbHelper.RemoveSong(id);
dbHelper.fetchSongsAndUpdateAdapter(dbAdapter);
Toast.makeText(this, "Song removed!", Toast.LENGTH_LONG).show();
}
Thanks Mike!
I've created a ListView and bind it to an adaptor to display data from a table. It works great. I can select on a row and it display the product name that I'm selecting. Now I've added an ImageView in the layout that will act as delete buttons on each row. My problem is I can't figure out how to add the code to make it so that when a user select the ImageView (delete button), it will delete the row. I've searched and found a lot of articles on this topic and tried a lot of them and none really work my code. Do I need to create a getView function? Also I've tried inserting the getTag(), but I couldn't make it work. Can you help me with a sample code that may work with my simple code or point me to the right direction? Here is my code:
private void displayListView() {
prodinputHelper = new DBAdaptorProductInput(this);
prodinputHelper.open();
Cursor cursor = prodinputHelper.fetchAllProductInput();
// The desired columns to be bound
String[] columns = new String[] {
DBAdaptorProductInput.KEY_PRODUCTTYPE,
DBAdaptorProductInput.KEY_PRODUCTNAME,
DBAdaptorProductInput.KEY_MANUFACTURER,
DBAdaptorProductInput.KEY_VISC40,
DBAdaptorProductInput.KEY_VISC100,
DBAdaptorProductInput.KEY_VI,
DBAdaptorProductInput.KEY_DEN15C,
DBAdaptorProductInput.KEY_VISCTEXT,
DBAdaptorProductInput.KEY_BASEOILTYPE,
DBAdaptorProductInput.KEY_BASEOIL,
DBAdaptorProductInput.KEY_ADDITIVES,
DBAdaptorProductInput.KEY_OTHERADDITIVES,
DBAdaptorProductInput.KEY_THICKENER,
DBAdaptorProductInput.KEY_NLGI,
DBAdaptorProductInput.KEY_COMMENT,
DBAdaptorProductInput.KEY_PACKAGES,
DBAdaptorProductInput.KEY_AREA,
};
// the XML defined views which the data will be bound to
int[] to = new int[] {
R.id.code,
R.id.name,
R.id.manufacturer,
R.id.visc40,
R.id.visc100,
R.id.viscindex,
R.id.den15c,
R.id.visctext,
R.id.baseoiltype,
R.id.baseoil,
R.id.additives,
R.id.otheradditives,
R.id.thickener,
R.id.nlgi,
R.id.comments,
R.id.packages,
R.id.area,
};
// create the adapter using the cursor pointing to the desired data
//as well as the layout information
dataAdapter = new SimpleCursorAdapter(
this, R.layout.activity_product_review_info, cursor, columns, to, 0);
ListView listView = (ListView) findViewById(R.id.listView1);
// Assign adapter to ListView
listView.setAdapter(dataAdapter);
//SetOnItemClickListener for the ListView
listView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> listView, View view,
int position, long id) {
// Get the cursor, positioned to the corresponding row in the result set
Cursor cursor = (Cursor) listView.getItemAtPosition(position);
// Get the Customer Name from this row in the database.
String countryCode = cursor.getString(cursor.getColumnIndexOrThrow("ProductName"));
Toast.makeText(getApplicationContext(), countryCode, Toast.LENGTH_SHORT).show();
}
});
}
You need to have a custom adapter that extends BaseAdapter or SimpleCursorAdapter.
In the adapter's getView() method set the onClickListener for your ImageView.
I am developing an app in which I need a ListView whose rows have a TextView, 2 CheckBox and a Spinner.
However, I am experiencing issues with onItemSelected() of the Spinner, as it gets called each time it is displayed for each row. In this method I am updating database records with the selected option, but as Android calls it automatically, every time the items get reset because Android calls it with position 0 and this is the value updated in the database.
I have read a lot of links about the issue with onItemSelected() and some hacks, but all of them are to use without a ListView. Any points here?
I have tried to track in a List which positions are actually displayed to make it work but it does not. I think it is because of the recycling in Android that causes the troubleshooting method get called for Spinners already shown!
So the point is: How can I differenciate a real call to onItemSelected() because of a user selection from the Android call when displaying the Spinner?
Here is the code of my adapter that extends SimpleCursorAdapter.
Thank you so much in advance.
public ParticipationAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {
super(context, layout, c, from, to);
mActivity = (Activity)context;
ParticipationComment.ParticipationCommentManager commentManager = new ParticipationComment.ParticipationCommentManager(mActivity);
mParticipationCommentsCursor = commentManager.get();
mActivity.startManagingCursor(mParticipationCommentsCursor);
commentManager.detach();
mPositionsOfCursorIds = getPositionsOfCursorIds(mParticipationCommentsCursor);
mSpinnerPositionsDisplayed = new ArrayList<Integer>();
}
#Override
public View getView(final int participationPosition, View convertView, ViewGroup parent) {
final Cursor participationsCursor = getCursor();
mActivity.startManagingCursor(participationsCursor);
participationsCursor.moveToPosition(participationPosition);
View participationRow;
if (convertView == null) {
participationRow = LayoutInflater.from(mActivity).inflate(R.layout.participation_row_student, null);
} else {
mSpinnerPositionsDisplayed.remove((Integer)convertView.getTag());
participationRow = convertView;
}
participationRow.setTag(participationPosition);
Spinner commentSpinner = (Spinner)participationRow.findViewById(R.id.participation_comment_id_spinner);
SimpleCursorAdapter commentSpinnerAdapter = new SimpleCursorAdapter(
mActivity,
android.R.layout.simple_spinner_item,
mParticipationCommentsCursor,
new String[] {DatabaseManager.NAME},
new int[] {android.R.id.text1}
);
commentSpinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
commentSpinner.setAdapter(commentSpinnerAdapter);
long participationCommentId = participationsCursor.getLong(participationsCursor.getColumnIndex(DatabaseManager.PARTICIPATION_COMMENT_ID));
if (participationCommentId != 0) {
commentSpinner.setSelection(mPositionsOfCursorIds.get(participationCommentId));
}
commentSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
participationsCursor.moveToPosition(participationPosition);
if (!mSpinnerPositionsDisplayed.contains(participationPosition)) {
// Android calls this method the first time a Spinner is displayed,
// to differentiate from a real user click we check if the current Spinner's position
// in the ListView is being shown
mSpinnerPositionsDisplayed.add(participationPosition);
} else {
ParticipationComment participationComment = new ParticipationComment((Cursor)parent.getItemAtPosition(position));
Participation.ParticipationManager participationManager = new Participation.ParticipationManager(mActivity);
Participation participation = new Participation(participationsCursor);
participation.setConnectionProfileParticipationCommentId(participationComment.getConnectionProfileId());
participation.setParticipationCommentId(participationComment.getIdOpenErp());
participation.setChanged(true);
participationManager.update(participation);
participationManager.detach();
}
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
// Not used
}
});
TextView studentName = (TextView)participationRow.findViewById(R.id.participation_student_name);
studentName.setText(participationsCursor.getString(participationsCursor.getColumnIndex(DatabaseManager.NAME)));
CheckBox expectedPresent = (CheckBox)participationRow.findViewById(R.id.participation_expected_present_value);
expectedPresent.setChecked(participationsCursor.getInt(participationsCursor.getColumnIndex(DatabaseManager.EXPECTED_PRESENT)) == 1);
CheckBox present = (CheckBox)participationRow.findViewById(R.id.participation_present_value);
present.setChecked(participationsCursor.getInt(participationsCursor.getColumnIndex(DatabaseManager.PRESENT)) == 1);
return participationRow;
}
A better way is to use a AlertDialog Variant.. like this.. and create a button which initially has the first selection as its Text and its changed based on the AlertDialog choice..
What about using a small flag to discard first call of ItemSelected ?
I have a ListView which is populated by a SQLite query in OnCreate using the following code which then sets up an OnItemClickListener.
ListView menuList = (ListView) findViewById(R.id.ListView_Menu);
String sql = "SELECT EXHIBITORS, ('Stand No. ' || STANDNO) AS STANDNO, _ID FROM EXHIBITOR ORDER BY EXHIBITORS";
cursor = myDbHelper.getReadableDatabase().rawQuery(sql, null);
startManagingCursor(cursor);
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.menu_item, cursor, FROM, TO);
menuList.setAdapter(adapter);
menuList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View itemClicked, int position, long id) {
However, I want to be able to filter the ListView and then amend the query so that the user can reduce the size of the ListView by say requesting all Exhibitors that start with the letter 'A'. How can I do this, I assume by using the above code again but how do I this and still keep the OnItemClickListener working?
CursorAdapter.changeCursor() will allow you to replace your query with one that filters the Exhibitor names.
I have a main activity that takes elements from a database and displays them in a clickable listview. I use this method to accomplish the task:
private void fillData() {
// Get all of the notes from the database and create the item list
Cursor c = RequestManager.getRequests(getApplicationContext());
startManagingCursor(c);
String[] from = new String[] { DataBase.KEY_TITLE, DataBase.KEY_BODY };
int[] to = new int[] { R.id.text1, R.id.text2 };
// Now create an array adapter and set it to display using our row
SimpleCursorAdapter notes =
new SimpleCursorAdapter(this, R.layout.notes_row, c, from, to);
setListAdapter(notes);
}
I am wondering, is it possible to access a boolean field inside the database, and bold the specific element is the field is marked as unread? The elements are each in a textview, and are then placed in a listview.
Thanks
Edit: Used the suggestion to extend the CursorAdapter Class, but when any element in the list is bolded, the first element is also bolded. Once all the elements are marked as read, the first element goes back to unbolded. Any ideas?
public void bindView(View view, Context context, Cursor cursor) {
TextView textRequestNo = (TextView) view.findViewById(R.id.text1);
TextView textMessage = (TextView) view.findViewById(R.id.text2);
StringBuilder requestNo = new StringBuilder(cursor.getString(cursor
.getColumnIndex("requestNo")));
StringBuilder message = new StringBuilder(cursor.getString(cursor
.getColumnIndex("Message")));
textRequestNo.setText(requestNo);
textMessage.setText(message);
if (cursor.getString(cursor.getColumnIndex("Read")).equals("false"))
{
textRequestNo.setTypeface(null, Typeface.BOLD);
textMessage.setTypeface(null, Typeface.BOLD);
}
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
final View view = mInflater.inflate(R.layout.notes_row, parent, false);
bindView(view, context, cursor);
return view;
}
Unfortunately I think you're going to have to implement your own CursorAdapter in order to achieve this functionality. It's not so bad though; you can make subclass of ResourceCursorAdapter, and then all you need to do is write your own implementation of bindView(). Inside of this implementation, you can read the Cursor to determine if you should bold the row or not.