I've got a problem with using variable i in methods setViewValue and setOnItemClickListener.
So I need to check value of row in table and then add it to another class.
But for this checking I need to use i in both methods or do it another way.
Can you helpm me with it?
String[] from = new String[] { DB.COLUMN_MON, DB.COLUMN_YEAR };
int[] to = new int[] { R.id.textMonth, R.id.textYear };
scAdapter = new SimpleCursorAdapter(this, R.layout.list_item, cursor, from, to);
scAdapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() {
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
for (int i=0; i < 98; i++){
if (cursor.getString(cursor.getColumnIndex(DB.COLUMN_MON)).equals(dataMonths[0]) && cursor.getString(cursor.getColumnIndex(DB.COLUMN_YEAR)).equals(dataYears[i])){
list.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Intent intent = new Intent(Main_month.this, month.class);
intent.putExtra("year", dataYears[i]);
intent.putExtra("month", dataMonths[0]);
startActivity(intent);
finish();
}
});
}
}
return false;
}
});
list.setAdapter(scAdapter);
THANK YOU FOR HELP!
I solve my problem this way:
scAdapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() {
public boolean setViewValue(View view, final Cursor cursor, int columnIndex) {
list.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Intent intent = new Intent(Main_month.this, month.class);
intent.putExtra("year", cursor.getString(cursor.getColumnIndex(DB.COLUMN_YEAR)));
intent.putExtra("month", cursor.getString(cursor.getColumnIndex(DB.COLUMN_MON)));
startActivity(intent);
finish();
}
});
return false;
}
});
There are only two ways to use a variable in an onClick method as well as in other methods.
You can declare the variable in the onClick method and pass it as a parameter to the other method you need it in.
You can declare a class level variable (outside both methods) that has proper scope to be accessed by both methods.
And if you're going for the second method, you need a far more descriptive variable name than just i.
I think I see what you are doing. You are adding an item listener to your ListView, except you are doing it over and over and over again. Bad. nhgrif basically answered the question at hand. I will answer by telling you to not loop. In its current context, you cannot save i without declaring a global variable. Only set the setOnItemClickListener once! This is the reason for position variable included as a parameter; so you know which view was clicked. Check the documentation. Hopefully that helps clarify it.
Edit: I should probably clarify, do not loop and set every item listener. Set the lister ONCE and then loop if you have to, within the click listener that is.
Related
I have a database app.I want to make each item disappear from main screen after deleting it. It gets deleted after long press but becomes invisible after I exit the app and then again enter. So, how to disappear each item from main screen instantly after a long press?
Here is my code snippet to delete each item----
listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
FriendsDbHelper fdb = new FriendsDbHelper(MainActivity.this);
SQLiteDatabase db = fdb.getWritableDatabase();
db.delete(TABLE_NAME,_ID+"=?",new String[]{Long.toString(id)});
return true;
}
});
As you appear to be using a Cursor Adapter (i.e. you are deleting according to the id) then you need to
a) re-build the Cursor and then
b) use either the adapter's swapCursor or notifyDataSetChanged methods.
Note! If you are not using a Cursor Adapter then you will probably
encounter issues if you use the id passed to the
setOnItemLongClickListener method, as this will not necessarily be
the row's id but it will be the same value as position. At first it
would appear to work but could the start deleting the wrong rows or
not delete any rows.
You code would be along the lines of :-
listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
FriendsDbHelper fdb = new FriendsDbHelper(MainActivity.this);
SQLiteDatabase db = fdb.getWritableDatabase();
db.delete(TABLE_NAME,_ID+"=?",new String[]{Long.toString(id)});
mycursor = fdb.your_method_to_get_the_cursor(); //<<<< will need changing
your_adapter.swapCursor(mycursor); //<<<< will need changing
return true;
}
});
Here's a working example :-
// Get the ListView according to it's id
imagelist = (ListView) findViewById(R.id.imagelist);
// Get the Cursor
images = imgdbhlpr.getAllImages();
// Instantiate the Cursor Adapter
sca = new SimpleCursorAdapter(this,
android.R.layout.simple_list_item_1,
images,
new String[]{ImgDBHelper.DSCR_COL},
new int[]{android.R.id.text1},
0
);
//
imagelist.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
imgdbhlpr.deleteImageRow(id);
images = imgdbhlpr.getAllImages();
sca.swapCursor(images);
return true;
}
});
imagelist.setAdapter(sca);
You need to remove it from the listView. You need to call your list adapter and then call the method notifyDataSetChanged()
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 that I'm populating with a CursorAdapter like this:
SimpleCursorAdapter.ViewBinder viewBinder = new SimpleCursorAdapter.ViewBinder() {
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
if(columnIndex == cursor.getColumnIndex(MyTableColumns._ID))
{
view.setTag(cursor.getInt(columnIndex));
}
// some other stuff
}
};
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.my_item_renderer, cursor, from, to);
adapter.setViewBinder(viewBinder);
The aim is to get the ID from the list item clicked:
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
Object obj = v.getTag();
int myId = Integer.parseInt(obj.toString());
}
However this is always returning null. What am I overlooking? For now I'm just using a hidden text field but I'd like to know what I was doing wrong.
onListItemClick() provides you with a view that is the row in the list. ViewBinder binds values to the TextViews inside this row. Thus the view you call setTag() on is not the same as the view you call getTag() on.
You can either extend SimpleCursorAdapter so you can call setTag() on the correct view, or you can get the first child view of v in onListItemClick() and get the tag of that.
Have you tried this when setting your tag?
view.setTag(new Integer(cursor.getInt(columnIndex)));
Maybe your "if" statement simply returns false and thus no Tag value gets set?
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.
I am using the following code to set the adapter (SimpleCursorAdapter) for an AutoCompleteTextView
mComment = (AutoCompleteTextView) findViewById(R.id.comment);
Cursor cComments = myAdapter.getDistinctComments();
scaComments = new SimpleCursorAdapter(this,R.layout.auto_complete_item,cComments,new String[] {DBAdapter.KEY_LOG_COMMENT},new int[]{R.id.text1});
mComment.setAdapter(scaComments);
auto_complete_item.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
and thi is the xml for the actual control
<AutoCompleteTextView
android:id="#+id/comment"
android:hint="#string/COMMENT"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="18dp"/>
The dropdown appears to work correctly, and shows a list of items. When I make a selection from the list I get a sqlite object ('android.database.sqlite.SQLiteCursor#'... ) in the textview.
Anyone know what would cause this, or how to resolve this?
thanks
Ok I am able to hook into the OnItemClick event, but the TextView.setText() portion of the AutoCompleteTextView widget is updated after this point. The OnItemSelected() event never gets fired, and the onNothingSelected() event gets fired when the dropdown items are first displayed.
mComment.setOnItemClickListener( new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) {
// TODO Auto-generated method stub
SimpleCursorAdapter sca = (SimpleCursorAdapter) arg0.getAdapter();
String str = getSpinnerSelectedValue(sca,arg2,"comment");
TextView txt = (TextView) arg1;
txt.setText(str);
Toast.makeText(ctx, "onItemClick", Toast.LENGTH_SHORT).show();
}
});
mComment.setOnItemSelectedListener(new OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> arg0, View arg1,
int arg2, long arg3) {
Toast.makeText(ctx, "onItemSelected", Toast.LENGTH_SHORT).show();
}
#Override
public void onNothingSelected(AdapterView<?> arg0) {
// TODO Auto-generated method stub
Toast.makeText(ctx, "onNothingSelected", Toast.LENGTH_SHORT).show();
}
});
Anyone alse have any ideas on how to override the updating of the TextView?
thanks
patrick
I don't think you should have to update the text for the AutoCompleteTextView. It should do it automatically. It does this by calling the [CursorAdapter.convertToString(...)][1] method. if you read the description of the method it points this out. So if you were writing your own CursorAdapter you would override that method to return whatever text you would want to show up in the list of suggestions. This guy does a good job of explaining how to do it:
Line 86 - http://thinkandroid.wordpress.com/2010/02/08/writing-your-own-autocompletetextview/
However, since you are using a SimpleCursorAdapter, you can't override this method. Instead you need implement/create a [SimpleCursorAdapter.CursorToStringConverter][2] and pass it into [SimpleCursorAdapter.setCursorToStringConverter(...)][3]:
SimpleCursorAdapter adapter = new SimpleCursorAdapter(context, layout, cursor, from, to);
CursorToStringConverter converter = new CursorToStringConverter() {
#Override
public CharSequence convertToString(Cursor cursor) {
int desiredColumn = 1;
return cursor.getString(desiredColumn);
}
};
adapter.setCursorToStringConverter(converter);
Or if you don't want to create a CursorToStringConverter then use the [SimpleCursorAdapter. setStringConversionColumn(...)][4] method. But I think you still have to explicitly set the CursorToStringConverter to null:
int desiredColumn = 1;
adapter.setCursorToStringConverter(null);
adapter.setStringConversionColumn(desiredColumn);
Sorry, but the spam blocker won't let me post the links to the Android Documentation that describes the links I posted above. But a quick google search will point you to the correct doc pages.
[Late answer, just for the record. EDITed to remove my suggestion that subclassing is necessary.]
To use SimpleCursorAdapter with an AutoCompleteTextView, you need to set two handlers on the adapter: The CursorToStringConverter, and the FilterQueryProvider. Pseudocode follows:
adapter.setCursorToStringConverter(new CursorToStringConverter() {
public String convertToString(android.database.Cursor cursor) {
// Assume that "someColumn" contains the strings that we want to
// use to identify rows in the result set.
final int columnIndex = cursor.getColumnIndexOrThrow("someColumn");
final String str = cursor.getString(columnIndex);
return str;
}
});
adapter.setFilterQueryProvider(new FilterQueryProvider() {
public Cursor runQuery(CharSequence constraint) {
// runSomeQuery will look for all rows in the database
// that match the given constraint.
Cursor cursor = runSomeQuery(constraint);
return cursor;
}
});
When I make a selection from the list
I get a sqlite object
('android.database.sqlite.SQLiteCursor#'...
) in the textview.
You do not say what this "textview" is or how it relates to the Spinner.
I am going to take an educated guess and assume that you are simply assigning the selected item out of the Spinner into the TextView.
The selected item from a Spinner using a SimpleCursorAdapter is a Cursor, pointing at the row the user selected. The toString() implementation of Cursor will give you something akin to android.database.sqlite.SQLiteCursor# depending on where the Cursor came from.
More likely, you are going to want to call getString() on that Cursor, to retrieve some column value, and assign it to the TextView in question.
To solve the problem I just extended SimpleCursorAdapter and implemented the method convertToString(). Then I created an instance and set it as the adapter.
In order to allow filtering in AutoCompleteTextView when using CursorAdapters I also used setFilterQueryProvider(). See this question.
My extended class inside the Activity looks as:
private static class AutoCompleteCursorAdapter extends SimpleCursorAdapter {
public AutoCompleteCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {
super(context, layout, c, from, to);
}
#Override
public CharSequence convertToString(Cursor cursor) {
// This is the method that does the trick (return the String you need)
return cursor.getString(cursor.getColumnIndex("name"));
}
}