I am trying to build a simple stock application, I have a list view on my main activity which has a "Sell" button on every list item I have. The functionality of the Sell button should decrease the quantity of that particular item by updating the row for that item and setting the quantity to quantity-1.
To achieve this, I have found that setting up an on click listener in my custom cursor adapter class was the way to do it. I am using a content provider class for my Database operations. So what I tried to do is, trigger a function which is in my main activity, within the OnClickListener which is in my cursor adapter. Here is some code that would explain more. (please forgive my terrible programming skills, I am fairly new )
My approach does not seem to work for some reason, first click on Sell button does not do anything, and the second one crashes the application with the reason:
android.database.StaleDataException: Attempting to access a closed CursorWindow.Most probable cause: cursor is deactivated prior to calling this method.
p.s. I did not send the context from the adapter to decrease count method, and it was crashing of a null pointer on the getContentResolver().
Update function in my content provider:
private int updateItem (Uri uri, ContentValues values, String selection, String[] selectionArgs){
if (values.containsKey(InventoryContract.ItemEntry.COLUMN_NAME)){
String name = values.getAsString(InventoryContract.ItemEntry.COLUMN_NAME);
if (name == null){
throw new IllegalArgumentException("Item requires a name");
}
}
// If values size is zero, do not try to update the database.
if (values.size() == 0){
return 0;
}
// Otherwise, get writeable database to update the data
SQLiteDatabase database = mDbHelper.getWritableDatabase();
// Perform the update on the database and get the number of rows affected
int rowsUpdated = database.update(InventoryContract.ItemEntry.TABLE_NAME, values, selection, selectionArgs);
// If 1 or more rows were updated, then notify all listeners that the data at the
// given URI has changed
if (rowsUpdated != 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
// Return number of rows updated
return rowsUpdated;
}
The function I have written ( or tried to write ) in my main activity
public void decreaseCount(Context context, int columnId, int quantity){
quantity = quantity -1;
ContentValues values = new ContentValues();
values.put(InventoryContract.ItemEntry.COLUMN_QUANTITY, quantity);
Uri updateUri = ContentUris.withAppendedId(InventoryContract.ItemEntry.CONTENT_URI, columnId);
int rowsAffected = context.getContentResolver().update(updateUri, values,null, null);
}
and lastly, the custom OnClickListener I have added to the button (p.s. the listener is inside the overriden bindView method of the cursor adapter )
sellButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int columnIdIndex = mCursor.getColumnIndex(InventoryContract.ItemEntry._ID);
int quantityIndex = mCursor.getColumnIndex(InventoryContract.ItemEntry.COLUMN_QUANTITY);
CatalogActivity catalogActivity = new CatalogActivity();
catalogActivity.decreaseCount(context2, Integer.valueOf(mCursor.getString(columnIdIndex)), Integer.valueOf(mCursor.getString(quantityIndex)));
}
});
Thank you in advance !
The problem is very trivial. I fixed your codes. First don't create objects out of activities. Try to use boxing and unboxing technic to retrieve your context back. In your InsertCursorAdapter constructor should be like this
public ItemCursorAdapter(Context context, Cursor c) {
super(context, c);
this.context = context;
}
Then you need to save your cursor from bindView method.
Then you need to bind the context object to get your activity object back. All in all, you would have something like this:
#Override
public void bindView(View view, final Context context, Cursor cursor) {
this.mCursor = cursor;
TextView nameTextView = view.findViewById(R.id.name);
TextView quantityTextView = view.findViewById(R.id.quantity);
sellButton = view.findViewById(R.id.sell_button);
ImageView imageView = view.findViewById(R.id.item_image);
sellButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int columnIdIndex = mCursor.getColumnIndex(InventoryContract.ItemEntry._ID);
int quantityIndex = mCursor.getColumnIndex(InventoryContract.ItemEntry.COLUMN_QUANTITY);
String col= mCursor.getString(columnIdIndex);
String quan= mCursor.getString(quantityIndex);
CatalogActivity catalogActivity = (CatalogActivity) context;
catalogActivity.decreaseCount( Integer.valueOf(col), Integer.valueOf(quan));
}
});
Also I changed your decreaseCount arguments. Because this method is in activity class you don't need to pass it anytime you need to decrease the value. getContentResolver() method is a method in super class AppCompatActivity and because it is public, your activity have implemented it already.
//TODO: Decrease count by one
public void decreaseCount(int columnId, int quantity){
quantity = quantity -1;
ContentValues values = new ContentValues();
values.put(InventoryContract.ItemEntry.COLUMN_QUANTITY, quantity);
Uri updateUri = ContentUris.withAppendedId(InventoryContract.ItemEntry.CONTENT_URI, columnId);
int rowsAffected = getContentResolver().update(updateUri, values,null, null);
}
I can't get this working. I want the item from list view to be deleted when I click on the button. But I really don't know how to implement this.
Here is my adapter
public class PersonalRecordsAdapterDialog extends CursorAdapter {
public PersonalRecordsAdapterDialog(Context context, Cursor c) {
super(context, c);
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return LayoutInflater.from(context).inflate(R.layout.list_view_personal_layout, parent, false);
}
#Override
public void bindView(View view, Context context, final Cursor cursor) {
final DatabaseAdapter db = new DatabaseAdapter(context);
TextView weightTV = (TextView) view.findViewById(R.id.weight_tv);
TextView dateTV = (TextView) view.findViewById(R.id.date_tv);
final Button deleteRecord = (Button) view.findViewById(R.id.delete_record);
final String id = cursor.getString(cursor.getColumnIndex(DatabaseAdapter.DatabaseHelper.COL_1));
String weight = cursor.getString(cursor.getColumnIndex(DatabaseAdapter.DatabaseHelper.COL_3));
String date = cursor.getString(cursor.getColumnIndex(DatabaseAdapter.DatabaseHelper.COL_4));
weightTV.setText(weight);
dateTV.setText(date);
deleteRecord.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
db.remove(Integer.parseInt(id));
notifyDataSetChanged();
}
});
}
}
and here is my ListView
AlertDialog.Builder builder = new AlertDialog.Builder(holder.mContext);
View dialogView = View.inflate(holder.mContext,R.layout.dialog, null);
ListView myList = (ListView) dialogView.findViewById(R.id.dialog_list_view);
Cursor cursor = holder.myDb.getRows(exercise[position]);
PersonalRecordsAdapterDialog adapter = new PersonalRecordsAdapterDialog(holder.mContext,cursor);
myList.setAdapter(adapter);
builder.setView(dialogView)
.setTitle("History of your " + exercise[position].toLowerCase() + " records")
.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int id) {
// sign in the user ...
}
});
AlertDialog b = builder.create();
b.show();
Thank you for your help
Remove the item from your model when onClick happens n call notifyDatasetChanged() for the same adapter
and in your case you are passing cursor directly to your custom adapter which is a very bad practice as in this case your connection with the db will stay open and can lead to Memory and DB issues
So you can create your own model (ArrayList) , get values from cursor, add them into your model,Close your db connection and pass that model to your adapter. and to remove a particular item remove that from your model and call notifyDatasetChanged().(Note: Removing from model will only remove the data from list but not from db. In case you want to delete that data from your db, you also have to execute Delete Query )
for this : I don't want to delete it when I click on ListView item. I want to delete it only when I click on button in ListView item
Go to your adapter class.. get the object instance over there in OnGetView(...) method and the the onClickListener for the same over there.
To delete row from DB you can use a unique id from db table like this
public void delete_byID(int id){
sqLiteDatabase.delete(MYDATABASE_TABLE, KEY_ID+"="+id, null);
}
Where MYDATABASE_TABLE is your table to delete from.
KEY_ID is the name of the coloumn to put where condition.
id is the unique id associated with the row you want to delete
So in this case you will not need the cursor to delete a particular record
The reason that bindView method doesn't refresh by the db change is :
The cursor is steel the old cursor .
You need to requery the data base get new cursor and then
Call adapter.change cursor(passHereTheNewCursor)
Adding to above answer by Nitesh - To delete item from listview on button click, Either 1. first you need to delete item from listview's adapter like below -
deleteButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Object objToRemove = adapter.getItem([POSITION]);
adapter.remove(objToRemove);
}
}
or 2. you can delete this item from your arrayList & call notifyDataSetChanged().
But this will not delete from database. you have to implement delete query for it.
and yes as above said -
in your case you are passing cursor directly to your custom adapter which is a very bad practice as in this case your connection with the db will stay open and can lead to Memory and DB issues.
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 custom listview with three items. One of them is like "Add this to the DB" and when I click to it it inserts something to the DB.
What I want it to do is after doing the insert, change the text to "Delete this from the DB" and also the onClick method to call a method to delete that record instead a method to insert.
Is this possible?
Here is my code:
final String[] opcs = new String[]{"Resultados", "ClasificaciĆ³n", text_fav};
ArrayAdapter<String> aa = new ArrayAdapter<String>(this, R.layout.list_menutipo_item, opcs);
m_list.setAdapter(aa);
m_list.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
Intent newActivity = null;
switch( position )
{
case 0: ...
case 2: if (isConnected(m_context))
{
aƱadirFavorito();
}
break;
}
}
});
It's definitely possible. The view parameter to the onItemClick callback is the view on which you clicked, you can simply change that view's content. i.e. view.setText("Delete this from the DB").
Also you will want to distinguish whether the next click is "Add this to the DB" or "Delete this from the DB", doing a string comparison here like if("Delete this from the DB".equals(view.getText())) might not be of good practice, you can set a flag in the view like view.setTag(true) to indicate that the current view's content is "Delete this from the DB". and later you can use view.getTag() to get back the flag to do the comparison.
Boolean flag = (Boolean)view.getTag();
if(flag == null || !flag) {
view.setText("Delete this from the DB");
flag = true;
//... code to insert a record to DB
} else {
view.setText("Insert this to the DB");
flag = false;
//... code to delete a record from DB
}
view.setTag(flag);
I'm facing some difficults when I try to use the performItemClick funcion of the ListView.
All I want to do is to perform a click programatically in the first item of the list.
How can I do that? I looked up that function in the documentation, but I didn't really understand its parameters.
I tried something like:
myListView.performItemClick(myListView.getChildAt(0), 0, myListView.getChildAt(0).getId());
But it didn't work (myListView.getChildAt(0) returns null)
Thank you in advance!
mList.performItemClick(
mList.getAdapter().getView(mActivePosition, null, null),
mActivePosition,
mList.getAdapter().getItemId(mActivePosition));
Where mActivePosition is your click position!
All the best! :)
This worked for me.
listView.performItemClick(
listView.getAdapter().getView(position, null, null), position, position);
use the adapter to get the view for the position of the item. The other 2 parameters I didn't want so I left them null. Leaving convertView null causes the adapter to render a new view. It's a performance issue but since this is only happening once in a while it wont have much effect. I don't need to specify the parent for anything because I'm not using it.
position is just the spot where your item is located.
Additionally these 2 lines of code before your performItemClick create the illusion of having the list item selected. They also ensure the appropriate item is on the screen.
listView.requestFocusFromTouch();
listView.setSelection(position);
This works best for me. Run this on the main thread.
new Handler().post(new Runnable() {
#Override
public void run() {
mList.performItemClick(
mList.getChildAt(mActivePosition),
mActivePosition,
mList.getAdapter().getItemId(mActivePosition));
}
});
This is similar to Arun Jose's answer, but it will queue a message to the main thread to give the ListView some time to initiate.
I tried the code below and it worked.
getListView().performItemClick(null, 0, getListAdapter().getItemId(0));
The first parameter (view) can be null.
I went with
listView.getAdapter().getView(position, null, null).performClick();
When using Listview (simple array adapter or custom adapter) define listview and other finally make perform click.
For example:
//At onCreate function:
lv = (ListView) findViewById(R.id.listView);
lv.setAdapter(new CustomAdapter(List_item.this, list, images));
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// on click function works
}
}
int position = 0;
lv.performItemClick(lv.getAdapter().getView(position, null, null), position, lv.getAdapter().getItemId(position));
Note: After creating the setOnItemClickListener only you should call
perform click. Otherwise, it will not correctly.
this may be old but this may help :
lvList.performItemClick(null, index, lvList.getItemIdAtPosition(index) );
NOTE : the first param is null and will still work, if you have a custom adapter, convertView will be filled with custom layout and view and such.
-cheers / happy codings.
mList.performItemClick(
mList.getChildAt(mActivePosition),
mActivePosition,
mList.getAdapter().getItemId(mActivePosition));
where mActivePosition is the position of the child view in List View.
Using the code #sulal proposed, you may place it in onLoadFinished, if you use a LoaderManager. Eg something like
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
//....
// mSelectedId keeps the currently selected id
// INVID is an invalid value
if (mSelectedId == INVID) { // nothing selected
// sulal's code
new Handler().post(new Runnable() {
#Override
public void run() {
mList.performItemClick(
mList.getChildAt(mActivePosition),
mActivePosition,
mList.getAdapter().getItemId(mActivePosition));
mSelectedId = mList.getAdapter().getItemId(mActivePosition);
}
});
}
mActivePosition may be 0 (ie position on the first item) or a position kept during eg onPause
At Firstly I tried to use this code in my Fragment(Master/Detail -> NameListFragment)
getListView().performItemClick(null, 0, getListView().getAdapter().getItemId(0));
But it didn't work. When I did #Override onStart() method in fragment and I moved my code to onStart(). After that it works properly for me.
If you are working on a unit test case.
Try to use getInstrumentation().waitForIdleSync(), to wait the list be loaded, and extend the ActivityInstrumentationTestCase2
See this answer.
I just meet this freak problem today , and I try me best to deal with it.
My condition is , when I first init the layout , I need make some item checked.
But when I use gridView.getChildAt(position) , always return null. I met this problem before , caused by Not finishing drawing layout . So I send a post message . handler.postDelayed( .. , ..) , It works. Thanks who motion this Exception.
This work for me
If you would get weird result when using getView, this is because the list item you want does not exist within visible parts. Use below:
private View getViewFromAdapterByPosition(int position, ListView listView)
{
View view;
int firstVisiblePos = listView.getFirstVisiblePosition();
int lastVisiblePos = listView.getLastVisiblePosition();
if (position < firstVisiblePos || position > lastVisiblePos) {
view = listView.getAdapter().getView(position, null, listView);
} else {
view = listView.getChildAt(position - firstVisiblePos);
}
return view;
}
And then,
listView.performItemClick(getViewFromAdapterByPosition(index, listView), index, 0);
Try this one:
public static boolean performClicKOnLisViewFromIndex(ListView listView, int index){
if(listView != null){
if(listView.getAdapter()!= null && listView.getAdapter().getCount() >0 && listView.getAdapter().getCount() > index ){
listView.performItemClick(
listView.getAdapter().getView(index, null, null),
index, listView.getItemIdAtPosition(index));
return true;
}
}
return false;
}
myListView.getChildAt(0) returns null because used this very soon.
use a delay for it.
or use below code:
private class MyAdapter extends BaseAdapter
{
private final Context context;
private HashMap<Integer, View> views;
public MyAdapter(Context context)
{
this.context = context;
views = new HashMap<>();
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
if(convertView == null)
{
if(views.get(position) == null)
{
final LayoutInflater layoutInflater = LayoutInflater.from(context);
convertView = layoutInflater.inflate(R.layout.my_grid, null, false);
views.put(position, convertView);
}
else
convertView = views.get(position);
}
TextView tv = convertView.findViewById(R.id.langView);
tv.setText(languageList.get(position));
return convertView;
}
}
and
adapter = new MyAdapter(getActivity());
myListView.setAdapter(adapter);
Runnable r = new Runnable()
{
#Override
public void run()
{
myListView.performItemClick(adapter.getView(position, null, myListView), position, 0);
}
};
myListView.postDelayed(r, 100);
just should use performItemClick() and it's okay.
listView.performItemClick(listView.getAdapter().getView(listView.getSelectedItemId, null, null), listView.getSelectedItemId, listView.getAdapter().getItemId(listView.getSelectedItemId));
In my case, none of the options solved my problem, so I made an adaptation in my CursorAdapter class.
I defined a global variable in the scope, so I just call the class changing this value and check the cursor position by passing the position value
mProductsAdapter.currentPosition = requiredPosition
in my ProductsAdapter builder
var currentPosition = 0
in bindView I do the check
if (cursor.position == currentPosition) {
// perform action
}
The performClick is probably called before listview was filled, put breakpoint in getView and on performItemClick and check wich is called first
getListView().performItemClick(null, 0, 0) did the trick for me (for position 0).
Dropping Some Experience.
using listview1.performItemClick, will also trigger your listview1.OnItemClickListener if you are using the listener with same listview in your code.
Hope It helps
If you would get weird result when using getView, this is because the list item you want does not exist within visible parts. Use below:
private View getViewFromAdapterByPosition(int position, ListView listView)
{
View view;
int firstVisiblePos = listView.getFirstVisiblePosition();
int lastVisiblePos = listView.getLastVisiblePosition();
if (position < firstVisiblePos || position > lastVisiblePos) {
view = listView.getAdapter().getView(position, null, listView);
} else {
view = listView.getChildAt(position - firstVisiblePos);
}
return view;
}
And then,
listView.performItemClick(getViewFromAdapterByPosition(index, listView), index, 0);
This works for me:
listview.getSelectedView().performClick();
This worked for me:
listView.getAdapter().getView(1, null, null).performClick();
This is from Begining Android Games. It creates a simple list of items which you can click to open a new activity. Each list item of course, would have to also be added to the AndroidManifest.xml as a separate activity with a .ListItem# name.
public class MainActivity extends ListActivity {
String tests[] = { "ListItem1",
"ListItem2",
"ListItem3",
"ListItem4"};
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, tests));
}
#Override
protected void onListItemClick(ListView list, View view, int position, long id) {
super.onListItemClick(list, view, position, id);
String testName = tests[position];
try {
Class<?> classInstance = Class.forName("your.package.name." + testName);
Intent intent = new Intent(this, classInstance);
startActivity(intent);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}