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.
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 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);
}
having a problem with trying to access data in my database when I click an item in my listview.
Here is my method in my database handler:
public Match getMatchFromId(int matchId)
{
SQLiteDatabase db = this.getReadableDatabase();
Match m = null;
Cursor cursor;
cursor = db.rawQuery("select * from " + MATCH_TABLE + " where " +
MATCH_ID_COL + "='" + matchId + "'" , null);
while(cursor.moveToNext())
{
m = new Match();
m.setHome(cursor.getString(cursor.getColumnIndex(MATCH_HOME_TEAM_COL)));
m.setAway(cursor.getString(cursor.getColumnIndex(MATCH_AWAY_TEAM_COL)));
m.setHomeScore(cursor.getString(cursor.getColumnIndex(MATCH_HOMESCORE_COL)));
m.setAwayScore(cursor.getString(cursor.getColumnIndex(MATCH_AWAYSCORE_COL)));
m.setDate(cursor.getString(cursor.getColumnIndex(MATCH_DATE_COL)));
m.setTime(cursor.getString(cursor.getColumnIndex(MATCH_TIME_COL)));
m.setRedCard(cursor.getString(cursor.getColumnIndex(MATCH_REDCARD_COL)));
m.setBookings(cursor.getString(cursor.getColumnIndex(MATCH_BOOKINGS_COL)));
m.setTypeOfMatch(cursor.getString(cursor.getColumnIndex(MATCH_TYPEOFMATCH_COL)));
// m.setGroundId(cursor.getInt(cursor.getColumnIndex()));
}
cursor.close();
return m;
}
I'm just having a problem with how to access the data via a listview. I want to be able to click the item, and bring it to another activity to edit it.
public class ViewMatchesActivity extends AppCompatActivity {
DBHandler myDB;
ArrayList<Match> matches = new ArrayList<Match>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_matches);
myDB = new DBHandler(this);
final ListView listView = (ListView) findViewById(R.id.listview);
matches = myDB.getAllMatches();
MyAdapter myAdapter = new MyAdapter(ViewMatchesActivity.this, R.layout.list2col, matches);
listView.setAdapter(myAdapter);
Any help would be nice as I'm very new to Android Studio.
You need to add a Listener to Listen to the respective event (likely Item Click). Then extract the id (or whatever can be used to uniquely identify the row in the table id or rowid (normally the same thing) is frequently used), place that data into an Intent Extra, after creating an Intent and then invoke the other activity using the Intent.
You could code something along the lines of :-
listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//>>>> position is the position of the clicked item, 0 for the first item.
//>>>> position will also be the respective offset into matches
//>>>> position SHOULD NOT BE USED to try to perform arithmetic
//>>>> calculation of rowid/id of the row in the table
//>>>> So get rowid via matches.get(position).
Intent intent = new Intent(getApplicationContext(),your_other_activity.class);
intent.putExtra("KEYFORDATA",row_id_to_so_selected_item_can_be_edited);
StartActivity(intent);
}
});
Note! Obviously the above will not work as is, it will need to be tailored to suit.
I have a list view and a database I want to display the data from the database in another activity so I made a OnItemClickListener for my list view.
Now I get the position of the ClickListener but because I have made my adapter to display data so the latest input from the user is on top. I nead to reverse the position of the onClick.
At the moment I get:
1
2
3
4
but I need:
4
3
2
1
because of the database id.
If for example the user clicks position 3 on the list I want the database to return the row 3.
ListView:
listView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
// TODO Auto-generated method stub
Intent myIntent = new Intent(getActivity(), JornalListViewClick.class);
myIntent.putExtra("intVariableName",position);
startActivity(myIntent);
}
});
The getRows of the database:
public Cursor getAllRowre(){
String where=null;
Cursor cursor=db.query(true, DATABASE_TABLE, ALL_KEY, where, null, null, null,ID_KEY + " DESC", null);
if(cursor!=null){
cursor.moveToFirst();
}
return cursor;
}
The activity where I want to display the data:
Intent mIntent = getIntent();
int intValue = mIntent.getIntExtra("intVariableName", 0);
intValue++;
text=(TextView) findViewById(R.id.textViewUserInputFromListClicked);
Cursor cursor=dbJ.getRowUserInput(intValue+"");
if(cursor.moveToFirst()){
do{
String mes=cursor.getString(0);
text.setText(mes);
}while(cursor.moveToNext());
}
An alternative way:
Use a POJO to store every row retrieved
Implement Comparator and use Collections.sort(List, Comparator) to sort it before putting it into adapter
public class MyData{
private String field;
// getter and setter
}
After retrieve the String from database, you can instantiate MyData class and set the string (or more fields) into the instance. Put all results in a Collection. E.g.:
Vector<MyData> listOfResults=new Vector<MyData>();
if(cursor.moveToFirst()){
do{
String mes=cursor.getString(0);
MyData instance=new MyData();
listOfResults.add(instance);
}while(cursor.moveToNext());
}
return listOfResults;
After retrieving data from database, you want to sort it, right? Try this:
Collections.sort(listOfResults,new Comparator<MyData>(){
public int compareTo(MyData a,MyData b){
return a.getField().compareTo(b.getField());
}
});
Inside the Activity containing the ListView, create a private class implementing ListAdapter. E.g.:
private class MyListAdapter implements ListAdapter{
private Vector<MyData> data;
public MyListAdapter(Vector<MyData> list){
data=list;
}
/*** other methods you need to implement ***/
}
Instantiate MyListAdapter by supplying the Vector you got from database access method.
Then call setAdapter(ListAdapter) of the ListView inside the Activity after the data is ready.
I'm trying to implement a CursorAdapter in my Android App. I read different tutorials and tried out different things but it won't work!
I found one question here very similar to mine but I didn't get the idea out of it.
Here's the thing:
I got a Database with multiple tables and foreign keys and so on. I wrote a (tested and working) Database including an
extension of the SQLiteHelper class to work properly. The DB-Class containts a lot of methods to get various operations
on that DB like: insert, update, delete, alter and some special needs....
My Problem is the following:
I have written a method which returns all Data containing (all rows) in the Database which i want to present
in a ListView. The returning object is a wrapped ArrayList> . I got the thing working all fine
with an ListViewAdapter but then i came to my problem which now almost drives me crazy:
The User shall click a random item in that specific list (which contains all rows from the DB) and then i want to
update that specific row in the DB with some new data the user put into a EditText box. Pretty simple task actually but I think I just don't understand the usage of the CursorAdapter.
My questions now:
What does my DB-Method have to return (or look alike) that the CursorAdapter can handle it....
How do I have to implement the Adapter that it just fulfill this one task (retrieving the correct rowID of the DB entry in the list)
Here is my method retrieving the data from the DB
public ArrayList<ArrayList<Object>> getAll()
{
ArrayList<ArrayList<Object>> allRows = new ArrayList<ArrayList<Object>>();
//Cursorobjekt haelt die Daten einer Zeile und dient dazu in diesen zu iterieren
Cursor myCursor1,myCursor2,myCursor3;
myCursor1 = db.query
( "Faecher",
new String[] { "id" , FACH_NAME, FACH_ART },
null, null, null, null, null
);
// Den Pointer an die erste Stelle ruecken
myCursor1.moveToFirst();
myCursor2 = db.query
(
"Ort",
new String[]{"id" , ORT_RAUM , ORT_GEBAEUDE},
null,null,null,null,null,null
);
myCursor2.moveToFirst();
myCursor3 = db.query
(
"Profs",
new String[]{"id", PROFS_NAME, PROFS_SNAME, FKEY_GENDER_ID},
null,null,null,null,null,null
);
myCursor3.moveToFirst();
for(int i=0; i < myCursor1.getCount(); i++)
{
ArrayList<Object> row1 = new ArrayList<Object>();
row1.add(myCursor1.getLong(0));
row1.add(myCursor1.getString(1));
row1.add(myCursor1.getString(2));
row1.add(myCursor2.getLong(0));
row1.add(myCursor2.getString(1));
row1.add(myCursor2.getString(2));
row1.add(myCursor3.getLong(0));
row1.add(myCursor3.getString(1));
row1.add(myCursor3.getString(2));
row1.add(myCursor3.getLong(3));
allRows.add(row1);
myCursor1.moveToNext();
myCursor2.moveToNext();
myCursor3.moveToNext();
}
myCursor1.close();
myCursor2.close();
myCursor3.close();
return allRows;
}
The Adapter is empty right now because my code was real crap and now looks like this hull:
public class SubjectListAdapter extends CursorAdapter
{
private LayoutInflater myInflater;
public SubjectListAdapter(Context context, Cursor c)
{
super(context, c);
}
#Override
public View newView(Context context, Cursor myCursor, ViewGroup parent)
{
return null;
}
#Override
public void bindView(View parent, Context context, Cursor myCursor)
{
}
}
I hope someone can help me out with my problem or give me a hint where I have to go to get this working.
If I understand your question correctly, it seems like your primary issue is determining which element has been clicked on within the adapter. You may need to switch from bindview and newview to just using getview. If you do, you can just add an onClicklistener to the view before getView returns it. This way, each row in the list has its own listener and can update the correct row in the db. Any particular information that the onClickListener needs to run the correct database update method (ie the rowId) can also be passed into the listener with a Tag on the view. Hope that helps.
You have to create one big query from DB and get one Cursor. You can use even ordinary sql selection like
String selectQuery = "SELECT id, " FACH_NAME", "+ FACH_NAME ", " + FACH_ART .... +" FROM Faecher INNER JOIN ....";
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery(selectQuery, null);
to get requared cursor. Than create custom CursorAdapter with getted cursor
public class ExampleCursorAdapter extends CursorAdapter {
public ExampleCursorAdapter(Context context, Cursor c) {
super(context, c);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
TextView something1 = (TextView)view.findViewById(R.id.something1);
something1.setText(cusror.getString(0));
TextView something2 = (TextView)view.findViewById(R.id.something2);
something1.setText(cusror.getString(1));
.......
viev.addOnClickListener(new OnClickListener{
public void onClick(...){
}
});
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
LayoutInflater inflater = LayoutInflater.from(context);
View v = inflater.inflate(R.layout.item, parent, false);
return v;
}
}
Method bindView(....) will be called for each raw of Cursor but it will be setted to the different position, you souldnt change it inside this method. Than just bind this adapter to your ListView by setAdapter(....) method.