I want to delete a row in my Database via a ListView. I already managed to display the data in a listview. I am working with a OnItemLongClickListener which then opens up a Dialog to delete the clicked item. I can't get the actual delete method to work.
The OnCLickListener method:
public boolean onItemLongClick(AdapterView<?> adapterView, View view, final int position, long id) {
build = new AlertDialog.Builder(ZeigeFaecherListe.this);
build.setTitle("Fach löschen?" + faecherListe.get(position));
build.setMessage("Willst du das Fach wirklich löschen?");
build.setNegativeButton("Abbrechen", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.cancel();
}
});
build.setPositiveButton("Löschen", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialogInterface, int i) {
myDb.loescheFach(position);
Toast.makeText(getApplicationContext(), faecherListe.get(position) + " gelöscht.", Toast.LENGTH_LONG).show();
myDb.zeigeFaecher();
dialogInterface.cancel();
}
});
AlertDialog alert = build.create();
alert.show();
return true;
}
});
The actual delete method:
public int loescheFach(int position){
SQLiteDatabase db = this.getWritableDatabase();
return db.delete("Fach_table","FACHID=?", new String[]{String.valueOf(position)});
}
You are confusing your list position with id, they might not match.
Change your myDb.loescheFach(position); to myDb.loescheFach(id);
You're passing the position of ArrayList to SQLite to delete statement. You should pass FACHID (in your case) as an argument to delete statement.
As your posted code is incomplete I assume it as below.
....
....
build.setPositiveButton("Löschen", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialogInterface, int i) {
// myDb.loescheFach(position);
// first get an id from ArrayList using position faecherListe.get(position)
// then pass that id to your method
myDb.loescheFach(id);
Toast.makeText(getApplicationContext(), faecherListe.get(position) + " gelöscht.", Toast.LENGTH_LONG).show();
myDb.zeigeFaecher();
dialogInterface.cancel();
}
});
....
....
First position will not equate to the rowid/id of the row in the table. Assuming that you have a id column defined as your_id_column INTEGER PRIMARY KEY (or with the AUTOINCREMENT keyword, that makes no real difference) and that you don't specify a value when inserting a row then the first id will likely be 1, then 2 then 3 ... etc.
However, position (3rd parameter passed to onItemClick/onItemLongClick) for the first item in the List will be 0, then 1 then 2 etc. So you will never actually delete correct row (clicking the first item will delete nothing, clicking the second item will delete the first) and when rows are deleted it gets even more out of sync.
The 4th parameter (long id) passed will be the rowid/id IF AND ONLY IF a CursorAdapter is used (i.e. a Cursor is the source data for the ListView). In which case the cursor MUST have a column named _id. However, it looks as though faecherListe is the source and as you use the get method that it is an ArrayList/List. As such the id passed will be the same value position except it is a long.
Now if faecherListe were an ArrayList<object_that_reflects_the_columns_of_the_table_including_id> then assuming a getter of getId you could use long retrieved_id = faecherListe.get(position).getId(). However, the comments :-
I guess you have ArrayList of String, that's why are getting String
value from ArrayList – Shashanth 10 hours ago
Ye
You simply have ArrayList and thus no means of ascertaining the id unless whatever data is held by the String is guaranteed to be unique.
Example
This is an example using object_that_reflects_the_columns_of_the_table_including_id in this case a Feacher class that just has a name and an id
Feacher.java
This is an entirely new class that allows you to have an ArrayList with all the relevant data/fields/columns (especially the id column).
i.e. you can an have ArrayList
public class Feacher {
private String name;
private long id;
public Feacher(String feacher_name) {
this(feacher_name,-1);
}
public Feacher(String feacher_name, long feacher_id) {
this.name = feacher_name;
this.id = feacher_id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String toString() {
return name + " ID(" + String.valueOf(id) + ")";
}
}
DBHelper
This is the database helper (sub class of SQLiteOpenHelper) :-
public class DBHelper extends SQLiteOpenHelper {
public static final String DBNAME = "feacher.db";
public static final int DBVERSION = 1;
public static final String TB_FEACHER = "feacher";
public static final String COL_FEACHER_ID = BaseColumns._ID;
public static final String COL_FEACHER_NAME = "feacher_name";
SQLiteDatabase mDB;
public DBHelper(Context context) {
super(context, DBNAME, null, DBVERSION);
mDB = this.getWritableDatabase();
}
#Override
public void onCreate(SQLiteDatabase db) {
String crtsql = "CREATE TABLE IF NOT EXISTS " + TB_FEACHER +
"(" +
COL_FEACHER_ID + " INTEGER PRIMARY KEY," +
COL_FEACHER_NAME + " TEXT" +
")";
db.execSQL(crtsql);
}
#Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
}
public long addFeacher(String feacher_name) {
ContentValues cv = new ContentValues();
cv.put(COL_FEACHER_NAME,feacher_name);
return mDB.insert(TB_FEACHER,null,cv);
}
public ArrayList<Feacher> getAllAsFeacherArrayList() {
ArrayList<Feacher> rv = new ArrayList<>();
Cursor csr = mDB.query(
TB_FEACHER,
null,
null,
null,
null,
null,
null
);
while (csr.moveToNext()) {
rv.add(new Feacher(
csr.getString(csr.getColumnIndex(COL_FEACHER_NAME)),
csr.getLong(csr.getColumnIndex(COL_FEACHER_ID))
)
);
}
return rv;
}
public int deleteFeacher(long id) {
return mDB.delete(
TB_FEACHER,
COL_FEACHER_ID + "=?"
,new String[]{String.valueOf(id)}
);
}
}
the deleteFeacher method to delete a row according to it's id,
the addFeacher method allowing some test data to be added.
the getAllAsFeacherArrayList returns all data as an ArrayList (note uses the constructor that sets the id)
MainActivity.java
The activity that has the ListView.
public class MainActivity extends AppCompatActivity {
ListView mListView;
ArrayAdapter<Feacher> mAdapter;
DBHelper mDBHlpr;
ArrayList<Feacher> mFeacherList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView = this.findViewById(R.id.listview);
mDBHlpr = new DBHelper(this);
addDataForTesting();
displayAdapter();
}
private void displayAdapter() {
if (mFeacherList == null) {
mFeacherList = mDBHlpr.getAllAsFeacherArrayList();
} else {
mFeacherList.clear();
for (Feacher f: mDBHlpr.getAllAsFeacherArrayList()) {
mFeacherList.add(f);
mAdapter.notifyDataSetChanged();
}
}
if (mAdapter == null) {
mAdapter = new ArrayAdapter<Feacher>(this,android.R.layout.simple_list_item_1,mFeacherList);
mListView.setAdapter(mAdapter);
mListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) {
mDBHlpr.deleteFeacher(mAdapter.getItem(i).getId());
displayAdapter();
return true;
}
});
}
}
private void addDataForTesting() {
mDBHlpr.addFeacher("Feacher 001");
mDBHlpr.addFeacher("Feacher 002");
mDBHlpr.addFeacher("Feacher 003");
}
}
Note for simplicity and also to avoid inadvertent deletion onItemLongClick has been used. The all important bit being mDBHlpr.deleteFeacher(mAdapter.getItem(i).getId()); which gets the id from the item (object, the Feacher) that was clicked according to the position.
activity_main.xml
A very basic layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
<ListView
android:id="#+id/listview"
android:layout_margin="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FFAAAAFF"
>
</ListView>
</LinearLayout>
Result
here's a screen shot after 3rd run so 9 rows have been added and 2 rows have been deleted (note id's 2 and 5 are missing). The list will refresh after a deletion so the row disappears almost immediately the long-click is completed.
Related
I have a list view that gets populated from a database. On the same activity I also have an EditTExt and an Add Category Button above the listview.
EditText | Add Category
Category Name1 | Edit Button | Delete Button
Category Name2 | Edit Button | Delete Button
Category Name3 | Edit Button | Delete Button
If I Add Category, the listview updates.
If I click on Delete, Opens Up Dialog (Are You Sure?), when you click yes it deletes it from the database and then updates the listview with the row deleted.
If I click the edit button (which is in the custom adapter for the listview layout), it opens up the dialog and shows an edittext with the category name and you can change it and when you click on the save button it saves it to the database but DOES NOT update the listview.
So I solved the issue instead of trying to repoll the database to update the listview again, I grabbed the position and the edittext for the category of that row and updated it that way.
Being only 1 item can be edited at a time I figured no big deal until I ran into another issue by using this work around. And that is:
If you click on Edit Button For Category Name1 and in the dialog you rename it to Meaningful Category Name. It updates the database, it is reflected in the listview right away, but when you delete the item before doing anything else when I send the message to a different listview to let me know that it was deleted, it reflects the original category name and not the edited one.
Now the work around for this is that when I want to delete it, instead of relying on the category name from the listview, get the category name from the database before it's deleted, then delete it and then send the message with the category name from the database.
What I would really like instead of using the 2 workarounds is the following flow:
Activity Loads With Custom Listview -> Click on Edit -> Dialog Edit Opens From Custom Adapter -> Click on Save Button -> Database is updated. -> Listview is updated / refreshed.
I have it all working with the exception of Listview is updated / refreshed.
I have searched high and low and can find all kinds of things that you can do to refresh listview, but none of them work and none of them are for Database update from within a dialog in the custom adapter.
I can post code, but I thought this is pretty much straight forward. If not I will gladly post code.
Perhaps consider using a CursorAdapter which eliminates the need for an intermediate Array and refreshing is a breeze.
Here's an example, that uses the SimpleCursorAdapter, that does all but edit (however you may well want a Custom Cursor Adapter). However the refresh aspect is no different bar the specific adapter type.
The layout activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
/>
<EditText
android:id="#+id/categoryname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
</EditText>
<Button
android:id="#+id/addcategorybutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ADD CATEGORY"
>
</Button>
<ListView
android:id="#+id/categorylist"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
</ListView>
</LinearLayout>
i.e. EditText, Add Button and ListView
The Database Helper DBHelper
class DBHelper extends SQLiteOpenHelper {
public static final String DBNAME = "category.db";
public static final int DBVERSION = 1;
public static final String TB_CATEGORY = "category";
public static final String COl_ID_CATEGORY = BaseColumns._ID;
public static final String COl_CATEGORYNAME_CATEGORY = "category_name";
private SQLiteDatabase db;
private static volatile DBHelper instance;
private DBHelper(#Nullable Context context) {
super(context, DBNAME, null, DBVERSION);
db = this.getWritableDatabase();
}
public static DBHelper getInstance(Context context) {
if (instance == null) {
instance = new DBHelper(context);
}
return instance;
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE IF NOT EXISTS " + TB_CATEGORY + "(" +
COl_ID_CATEGORY + " INTEGER PRIMARY KEY" +
"," + COl_CATEGORYNAME_CATEGORY + " TEXT UNIQUE " +
")");
}
#Override
public void onUpgrade(SQLiteDatabase db, int i, int i1) {
}
public long insertCategory(String categoryName) {
ContentValues cv = new ContentValues();
cv.put(COl_CATEGORYNAME_CATEGORY,categoryName);
return db.insert(TB_CATEGORY,null,cv);
}
public int deleteCategory(long id) {
return db.delete(TB_CATEGORY,COl_ID_CATEGORY + "=?", new String[]{String.valueOf(id)});
}
public int deleteCategory(String categoryName) {
return db.delete(TB_CATEGORY,COl_CATEGORYNAME_CATEGORY + "=?",new String[]{categoryName});
}
public int updateCategory(Long id, String categoryName) {
ContentValues cv = new ContentValues();
cv.put(COl_CATEGORYNAME_CATEGORY,categoryName);
return db.update(TB_CATEGORY,cv,COl_ID_CATEGORY+"=?", new String[]{String.valueOf(id)});
}
public int updateCategory(String oldCatgeoryName, String newCategoryName) {
ContentValues cv = new ContentValues();
cv.put(COl_CATEGORYNAME_CATEGORY,newCategoryName);
return db.update(TB_CATEGORY,cv,COl_CATEGORYNAME_CATEGORY + "=?",new String[]{oldCatgeoryName});
}
public Cursor getCategories() {
return db.query(TB_CATEGORY,null,null,null,null,null,COl_CATEGORYNAME_CATEGORY + " ASC");
}
}
NOTE Cursor adapters expect(must have) a column named _id (hence BaseColumns._ID) and that column should be a unique integer that identiofies the row. Most often an alias of the rowid column aka column is defined using _id INTEGER PRIMARY KEY (with or without AUTOINCREMENT (without is more efficient, with is rarely required so best not used)).
As you can see simple two column table _id and categoryname. methods to add, update, insert, delete and extract all rows (as a Cursor) have been added.
The Activity MainActivity :-
public class MainActivity extends AppCompatActivity {
EditText categoryName;
Button addCategory;
ListView categoryList;
SimpleCursorAdapter sca;
DBHelper db;
Cursor csr;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
categoryName = this.findViewById(R.id.categoryname);
addCategory = this.findViewById(R.id.addcategorybutton);
categoryList = this.findViewById(R.id.categorylist);
db = DBHelper.getInstance(this);
setupAddCategoryButton();
setOrRefreshCategoryListView();
}
public void setOrRefreshCategoryListView() {
csr = db.getCategories();
if (sca == null) {
sca = new SimpleCursorAdapter(
this,
android.R.layout.simple_list_item_1,
csr, //<<<<<<<< The Cursor
new String[]{DBHelper.COl_CATEGORYNAME_CATEGORY}, //<<<<<<<< The list of columns to be displayed
new int[]{android.R.id.text1}, //<<<<<<<< The corresponding view id's in the layout that is to display the data from the column
0
);
categoryList.setAdapter(sca);
categoryList.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) {
DBHelper dbx = DBHelper.getInstance(view.getContext());
if (dbx.deleteCategory(l) > 0) {
setOrRefreshCategoryListView();
}
return true;
}
});
} else {
sca.swapCursor(csr);
}
}
public void setupAddCategoryButton() {
addCategory.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (categoryName.getText().toString().length() > 0) {
if (db.insertCategory(categoryName.getText().toString()) > 0) {
setOrRefreshCategoryListView();
}
categoryName.setText("");
}
}
});
}
}
See the setOrRefreshCategoryListView, all that is needed to refresh (and initially setup the ListView) is done in here. You just have to call the method.
When the above is run. Categories can be added by entering text and then clicking the ADD CATEGORY button. Categories can be deleted by long clicking a Category.
Result
After adding a few categories (1-3) and just before adding a 4th :-
Click on Add :-
Long Click Category 2 :-
Additional re comment
I was just hiping that there was a way to refresh the listview from within the custom adapter itself after the update from the alertdialog.
perhaps consider the following which shows both refresh from within the adapter (i.e. hot DLT button and item is deleted and refreshed)
and also from the invoking activity via the EDIT button (doesn't edit just toasts).
First the layout used for the CustomAdapter
subclass of CursorAdapter which I would still recommend over ArrayAdapter (i.e. CursorAdapter exists for a reason).
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<TextView
android:id="#+id/categoryname"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="6">
</TextView>
<Button
android:id="#+id/categoryedit"
android:layout_width="0dp"
android:layout_weight="2"
android:layout_height="match_parent"
android:text="EDIT"
android:onClick="onClickEditCategoryButton"
android:layout_marginRight="2dp"
>
</Button>
<Button
android:id="#+id/categorydelete"
android:layout_width="0dp"
android:layout_weight="2"
android:layout_height="match_parent"
android:text="DLT"
>
</Button>
</LinearLayout>
NOTE the EDIT button has onCLick specified so onClicklistener code is coded on your behalf.
DBHelper is unchanged from above.
MainActivity is now :-
public class MainActivity extends AppCompatActivity {
EditText categoryName;
Button addCategory;
ListView categoryList;
CustomAdapter ca;
DBHelper db;
Cursor csr;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
categoryName = this.findViewById(R.id.categoryname);
addCategory = this.findViewById(R.id.addcategorybutton);
categoryList = this.findViewById(R.id.categorylist);
db = DBHelper.getInstance(this);
setupAddCategoryButton();
setOrRefreshCustomCategoryListView();
}
public void setOrRefreshCustomCategoryListView() {
csr = db.getCategories();
if (ca == null) {
ca = new CustomAdapter(this,csr);
categoryList.setAdapter(ca);
} else {
ca.swapCursor(csr);
}
}
public void onClickEditCategoryButton(View v) {
switch (v.getId()) {
case R.id.categoryedit: {
Toast.makeText(this, "You Clicked Edit for Category with ID of " + v.getTag(), Toast.LENGTH_SHORT).show();
setOrRefreshCustomCategoryListView();
}
break;
}
}
public void setupAddCategoryButton() {
addCategory.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (categoryName.getText().toString().length() > 0) {
if (db.insertCategory(categoryName.getText().toString()) > 0) {
setOrRefreshCustomCategoryListView();
}
categoryName.setText("");
}
}
});
}
}
Last the CustomAdapter :-
class CustomAdapter extends CursorAdapter {
CustomAdapter(Context context, Cursor csr) {
super(context,csr, android.widget.CursorAdapter.IGNORE_ITEM_VIEW_TYPE);
}
#Override
public View newView(Context context, Cursor csr, ViewGroup parent) {
return LayoutInflater.from(context).inflate(R.layout.categorylist,parent,false);
}
#Override
public void bindView(View view, Context context, Cursor csr) {
TextView categoryName = view.findViewById(R.id.categoryname);
categoryName.setText(csr.getString(csr.getColumnIndex(DBHelper.COl_CATEGORYNAME_CATEGORY)));
// Set Button tags with id of current row
// NOTE button onClick listener set in layout
String id = csr.getString(csr.getColumnIndex(DBHelper.COl_ID_CATEGORY));
view.findViewById(R.id.categoryedit).setTag(id);
view.findViewById(R.id.categorydelete).setTag(id);
view.findViewById(R.id.categorydelete).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(context,"You Clicked Delete",Toast.LENGTH_SHORT).show();
DBHelper.getInstance(context).deleteCategory(Long.parseLong(view.getTag().toString()));
refreshListView(context);
}
});
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
return super.getView(position, convertView, parent);
}
public void refreshListView(Context context) {
this.swapCursor(DBHelper.getInstance(context).getCategories());
}
}
result
Looks like this now (clicking Edit toasts (via activity), clicking DLT deletes (via adapter))
I am using an sqllite database to store two columns which are phonename and phonenumber. I am using an arrayList to iterate through the data and display the phonename in a listview which is working, but I also need to iterate through the phonenumber column under the same listview as well. I only need the phonename to be showing in the listview.
This is for when the user has selected the item in the listview, it shows the selected phonename and phonenumber, which at the moment it is only currently showing the phonename and showing blank for phonenumber for obvious reasons.
DataDBAdapter
public long insert(String phonename, String phonenumber)
{
ContentValues cv = new ContentValues();
cv.put(COl_MYTABLE_PHONENAME,phonename);
cv.put(COL_MYTABLE_PHONENUMBER,phonenumber);
return mDB.insert(TBL_MYTABLE,null,cv);
}
//---------------------------------------------------------------------------
// Iterating through the database
//---------------------------------------------------------------------------
public ArrayList<String> getAllRowsAsList()
{
Cursor csr = mDB.query(TBL_MYTABLE,null,null,null,null,null,null);
ArrayList<String> rv = new ArrayList<>();
while (csr.moveToNext())
{
rv.add(csr.getString(csr.getColumnIndex(COl_MYTABLE_PHONENAME)));
}
return rv;
}
SelectModemFragment
private void manageListView(Context context)
{
thelist = dbHelper.getAllRowsAsList(); // Extract the list, just the phone names
// Only setup the adapter and the ListView if the adapter hasn't been setup
if(arrayAdapter == null)
{
// Instantiate the adapter
arrayAdapter = new ArrayAdapter<>(context,android.R.layout.simple_list_item_1,thelist); //<<<<<<<<<< list included
display_contacts1.setAdapter(arrayAdapter); //<<<<<<<<<< Tie the adpater to the ListView
// Set the ListViews OnItemClick Listener
display_contacts1.setOnItemClickListener(new AdapterView.OnItemClickListener()
{
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
{
String namedisplay = arrayAdapter.getItem(position); //<<<<<<<<<< this gets the phone name
namedisplay = arrayAdapter.getItem(position);
Toast.makeText(view.getContext(), namedisplay + " Selected for Communication", Toast.LENGTH_SHORT).show();
Toast.makeText(view.getContext(), phoneNo, Toast.LENGTH_SHORT).show();
}
});
}
Issue
using ArrayAdapter only allows a a single item to be passed, thus unless you resort to complicated/messy/inefficient methods ArrayAdapter is only really suitable for a single value.
Fix
You could use an ArrayList where your_object has members for all the required values. i.e phonenumber and phonename. Noting that unless you use a Custom Adapter that you should override the the toString method to extract the data that you want to be displayed, as that is what a standard ArrayAdapter uses.
Alternative (use a CursorAdapter)
An alternative would be to use a Cursor Adapter (e.g. SimpleCursorAdapter), you can then return the Cursor and use it directly. However, a CursorAdapter REQUIRES a column specifically name _id (BaseColumns._ID can be used).
One of the clear advantages of a Cursor adapter is the the 4th paremmter passed to the onItemClick/onItemLongClick is the id of the row (if used correctly) allowing a single value to then get/update/delete/pass the respective selected row.
As such I'd recommend a Cursor Adapter for a ListView and hence the more comprehensive answer.
You may think I don;t have such a column. However, you can use the normally hidden rowid column and dynamically create a column named _id.
You could have a method, in the database helper (DataDBAdapter) such as :-
public Cursor getAllRowsAsCursor()
{
String[] columns = new String[]{"rowid AS " + BaseColumns._ID,"*"}
return = mDB.query(TBL_MYTABLE,null,null,null,null,null,null)
}
The ManageList method could then be :-
private void manageListView(Context context) {
myCursor = dbhelper.getAllRowsAsCursor();
// Only setup the adapter and the ListView if the adapter hasn't been setup
if(arrayAdapter == null)
{
// Instantiate the adapter
arrayAdapter = new SimpleCursorAdapter(context,android.R.layout.simple_list_item_1,myCursor,new String[]{DataAdapter.COl_MYTABLE_PHONENAME},newint[]{android.R.id.text1},0);
display_contacts1.setAdapter(arrayAdapter); //<<<<<<<<<< Tie the adpater to the ListView
// Set the ListViews OnItemClick Listener
display_contacts1.setOnItemClickListener(new AdapterView.OnItemClickListener()
{
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
{
String namedisplay = arrayAdapter.getItem(position); //<<<<<<<<<< this gets the phone name
String phonenumber = myCursor,getString(myCursor.getColumnIndex(DataAdapter.COL_MYTABLE_PHONENUMBER);
Toast.makeText(view.getContext(), namedisplay + " Selected for Communication", Toast.LENGTH_SHORT).show();
Toast.makeText(view.getContext(), phonenumber, Toast.LENGTH_SHORT).show();
}
});
} else {
arrayAdapter.swapCursor(myCursor);
}
Notes
MyCursor would be declared as a class variable e.g. Cursor MyCursor;
Instaed of
ArrayAdapter<String> arrayAdapter; you would have
SimpleCursorAdapter arrayAdapter;
The above is in-principle code and has not been tested, so there may be errors and/or omissions.
Working Example
The following is the code based upon the code from the previous question asked (which this appears to follow on from). It has two ListViews the old and a new one that uses a SimpleCursorAdapter. Clicking an item display phone number and also id. Lon Clicking an Item deletes that item (refreshing both ListViews).
DataDBAdapter.java has two new methods (so add these) :-
//<<<<<<<<<< ADDED
public Cursor getAllRowsAsCursor() {
return mDB.query(TBL_MYTABLE,null,null,null,null,null,null);
}
public int delete(long id) {
String whereclause = COL_MYTABLE_ID + "=?";
String[] whereargs = new String[]{String.valueOf(id)};
return mDB.delete(TBL_MYTABLE,whereclause,whereargs);
}
SelectModemFragment.java is now :-
public class SelectModemFragment extends Fragment {
private SelectModemViewModel mViewModel;
ListView display_contacts1;
ArrayAdapter<String> arrayAdapter;
ArrayList<String> thelist;
DataDBAdapter dbhelper;
//<<<<<<<<<< ADDED
ListView display_contacts2;
SimpleCursorAdapter sca;
Cursor MyCursor;
public static SelectModemFragment newInstance() {
return new SelectModemFragment();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.select_modem_fragment, container, false);
display_contacts1 = view.findViewById(R.id.lv001); //<<<<<<<<<< top listview ArrayAdapter<String>
display_contacts2 = view.findViewById(R.id.lv002);
dbhelper = new DataDBAdapter(view.getContext());
AddSomeData();
manageListView(view.getContext());
manageListView2();
return view;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mViewModel = ViewModelProviders.of(this).get(SelectModemViewModel.class);
// TODO: Use the ViewModel
}
//Sets up the ListView if not already setup
private void manageListView(Context context) {
thelist = dbhelper.getAllRowsAsList(); //<<<<<<<<<< extract the list (just the phone names) from the database
// Only setup the adapter and the ListView if the adapter hasn't been setup
if (arrayAdapter == null) {
// Instantiate the adapter
arrayAdapter = new ArrayAdapter<>(context,android.R.layout.simple_list_item_1,thelist); //<<<<<<<<<< list included
display_contacts1.setAdapter(arrayAdapter); //<<<<<<<<<< Tie the adpater to the ListView
// Set the ListViews OnItemClick Listener
display_contacts1.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
String name = arrayAdapter.getItem(position); //<<<<<<<<<< this gets the phone name
Toast.makeText(view.getContext(),"You clicked the phone named " + name,Toast.LENGTH_SHORT).show();
}
});
} else {
//<<<<<<<<<< MODIFIED to cope with changes (needs to rebuild the array within the adpater)
arrayAdapter.clear();
for (String s: thelist) {
arrayAdapter.add(s);
}
arrayAdapter.notifyDataSetChanged();
}
}
//<<<<<<<<<< ADDED FOR CursorAdapter
private void manageListView2() {
MyCursor = dbhelper.getAllRowsAsCursor();
if (sca == null) {
sca = new SimpleCursorAdapter(
getContext(),
android.R.layout.simple_list_item_1,
MyCursor,
new String[]{DataDBAdapter.COl_MYTABLE_PHONENAME},
new int[]{android.R.id.text1},
0
);
display_contacts2.setAdapter(sca);
display_contacts2.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(view.getContext(),
"You Clicked the phone name " +
MyCursor.getString(MyCursor.getColumnIndex(DataDBAdapter.COl_MYTABLE_PHONENAME)) +
". The phonenumber is " +
MyCursor.getString(MyCursor.getColumnIndex(DataDBAdapter.COL_MYTABLE_PHONENUMBER)) +
". The ID (as passed) is " + String.valueOf(id) +
". The ID (from Cursor) is " + String.valueOf(MyCursor.getLong(MyCursor.getColumnIndex(DataDBAdapter.COL_MYTABLE_ID)))
,
Toast.LENGTH_SHORT).show();
}
});
//<<<<<<<<<< EXTRA delete row on long click
display_contacts2.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
dbhelper.delete(id);
manageListView2();
manageListView(getContext());
return true;
}
});
} else {
sca.swapCursor(MyCursor);
}
}
// Add some testing data (only if none already exists)
private void AddSomeData() {
if (DatabaseUtils.queryNumEntries(dbhelper.getWritableDatabase(),DataDBAdapter.TBL_MYTABLE) < 1) {
dbhelper.insert("Phone 1", "0000000000");
dbhelper.insert("Phone 2", "1111111111");
}
}
#Override
public void onResume() {
super.onResume();
manageListView2();
manageListView(getContext());
}
#Override
public void onDetach() {
super.onDetach();
MyCursor.close();
}
}
On deleting the item from the Listview the item gets deleted at that time, but on coming back to the activity the item reappears.
This is my Main2Activity code:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
position = intent.getIntExtra("position", 0);
adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, listItem);
listView.setAdapter(adapter);
viewData1();
listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> parent, View view, final int pos, long id) {
final int itemToDelete = pos;
new AlertDialog.Builder(Main2Activity.this)
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle("Are you sure?")
.setMessage("Do you want to delete this location?")
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
listItem.remove(itemToDelete);
databaseHelper1.deleteLocation(itemToDelete, position);
adapter.notifyDataSetChanged();
}
}
)
.setNegativeButton("No", null)
.show();
return true;
}
});
}
private void viewData1() {
Cursor cursor = databaseHelper1.viewData1(position);
if (cursor.getCount() == 0) {
Toast.makeText(this, "No data to show", Toast.LENGTH_SHORT).show();
} else {
while (cursor.moveToNext()) {
Log.i("message", "Data got");
listItem.add(cursor.getString(1));
}
adapter.notifyDataSetChanged();
}
}
DatabaseHelper:
public void deleteLocation(int itemToDelete,int position)
{
SQLiteDatabase sqLiteDatabase = this.getWritableDatabase();
String itemDelete = Integer.toString(itemToDelete);
Log.i("itemdel",itemDelete);
if(position ==0)
{
String Id = (ID1);
Log.i("Id",Id);
String query = " Delete from "+DB_TABLE1 + " where "+ Id + " = " + itemDelete;
sqLiteDatabase.execSQL(query);
sqLiteDatabase.delete(DB_TABLE1,ID1 + " = "+ itemDelete, null);
sqLiteDatabase.compileStatement(query);
Log.i("del"," executed")
}
}
public Cursor viewData1(int position)
{
SQLiteDatabase sqLiteDatabase = this.getReadableDatabase();
Cursor cursor = null;
if (position ==0)
{
String query = "Select * from " + DB_TABLE1;
cursor = sqLiteDatabase.rawQuery(query, null);
}
return cursor;
}
What happens is:
Before Deleting:
After Deleting garden:
On restarting activity:
How do I commit the delete to the database? Thanks.
Your issue is that you are assuming that position (3rd parameter passed to the onItemLongClick method) directly relates to the id of the row.
You cannot rely on a correlation between position and id.
The first position in the list will be at position 0. The lowest ID allocated (unless forced) will be 1. However adding 1 is not a solution as even though it may initially work. As soon as you delete anything other than the last item in the list then an id is omitted from the list of id's and you may not delete a row or you may delete a different row.
The most sensible/easiest fix is to utilise a CursorAdapter i.e. SimpleCursorAdapter in which case the 4th parameter to onItemClick and onItemLongClick (long l) will be the actual id. However, to utilise a CursorAdapter you MUST have the id column named as _id (hence why there is the constant BaseColumns._ID).
You could always rename the column when extracting it using AS e.g. SELECT rowid AS _id, * FROM the_table; (which will select all existing columns AND the id column).
Here's a link to a more comprehensive answer with options for other adapter Deleting item from ListView and Database with OnItemClickListener
This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 5 years ago.
I'm building an app where I've to create multiple tables on a SQLite database.
The best example I can give is of one store that I'll have to manage, so I'm creating one table for each category (wines, beers, milk, etc...) of products where I have to manage the brands that I'm selling but, if I quit from selling one of this categories I would like to be able to drop this category table.
But I tried many examples from the web and nothing works. I'm always getting the same error.
Because I've many items on my tables I built a custom list view and a custom adapter and both are working perfectly, I can delete or add new items without problems, build new tables, but the same approach doesn't work to drop my built tables.
I intentionally let my database opened until my activity is destroyed so I don't have to open it trough my adapter.
Maybe someone can help me how to drop a table with the name passed from a String, using one button or in this case, would be really helpful using my adapter button, because my idea is to add buttons on my list view.
Just to make clear, I can drop the tables if I create one editText to type my tables names and use a button to run this code:
dataBase.execSQL("DROP TABLE IF EXISTS '" + tableName + "'");
or if I run this code during the execution of the method onCreate and I give a name from a table that really exists.
dataBase.execSQL("DROP TABLE IF EXISTS 'test 1'");
So my question is not how to handle NullPointerException but how to manipulate my SQLite database from my custom adapter.
Thank you!
this is my adapter code:
public class MyCustomAdapter extends BaseAdapter implements ListAdapter {
private ArrayList<String> list = new ArrayList<String>();
private Context context;
private SQLiteDatabase dataBase;
private String tableName;
public MyCustomAdapter(ArrayList<String> list, Context context) {
this.list = list;
this.context = context;
}
#Override
public int getCount() {
return list.size();
}
#Override
public Object getItem(int pos) {
return list.get(pos);
}
#Override
public long getItemId(int pos) {
//return list.get(pos).getId();
//just return 0 if your list items do not have an Id variable.
return 0;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
View view = convertView;
if (view == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.list_item_clickable, null);
}
//Handle TextView and display string from your list
TextView listItemText = (TextView)view.findViewById(R.id.list_item_textview);
listItemText.setText(list.get(position));
//Handle buttons and add onClickListeners
Button deleteBtn = (Button)view.findViewById(R.id.delete_btn);
deleteBtn.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
//criar alertDialog
AlertDialog.Builder dialog = new AlertDialog.Builder(context);
dialog.setTitle("Delete!");
dialog.setMessage("Do you wish to delete this ToDo list?");
dialog.setCancelable(false);
dialog.setIcon(android.R.drawable.ic_delete);
dialog.setNegativeButton("No", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
Toast.makeText(context, "No!", Toast.LENGTH_SHORT).show();
}
});
dialog.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
final String tableName= list.get( position ).toString();
try {
dataBase.execSQL("DROP TABLE IF EXISTS '" + tableName + "'");
//dataBase.execSQL("DROP TABLE IF EXISTS 'test 1'");
list.remove(position); //or some other task
Toast.makeText(context, "Tabela " + tableName + " removida com sucesso!", Toast.LENGTH_SHORT).show();
notifyDataSetChanged();
} catch (Exception e) {
e.printStackTrace();
}
}
});
dialog.create();
dialog.show();
}
});
listItemText.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//Toast.makeText(context, "Item position is: " + list.get(position), Toast.LENGTH_SHORT).show();
Intent intent = new Intent(context, MainActivity.class);
String tabelaNome = list.get( position ).toString();
intent.putExtra("tableName", tableName);
context.startActivity(intent);
}
});
return view;
}
Error message
Note the difference between your two SQL statements:
dataBase.execSQL("DROP TABLE IF EXISTS '" + tableName + "'");
dataBase.execSQL("DROP TABLE IF EXISTS teste 1");
The one you say works does not have quotes around the table name.
I have a listview that contains some data which I got from the web. Now I can make changes in the list item and once I make changes to the item, I am storing the updated value in the db. When i login in next time to the app, I am downloading the content from net and showing it in the listview with the changes that I have done last time. So my approach here is, I am querying the db for each item in the getview method of the list adapter to check for changes. Is it a good practice to do a db query for each item's getview method of the adapter? If not could you please suggest me some alternative. Thanks.
Never, really, never do that.
If you put your data download code in the getView method of the adapter it will make a network call for each row of the list.
Even worst, it will call it anytime that row appears on the screen, not only one time for row.
You should get all your data first, then use the adapter only to draw it.
You can at anytime call the db to check for changes and, if needed, notify the adapter to redraw the list to show the changes.
Hope this helps.
In Android development, any time you want to show a vertical list of items you will want to use a ListView which is populated using an Adapter to a data source. When we want the data for the list to be sourced directly from a SQLite database query we can use a CursorAdapter.
The CursorAdapter fits in between a Cursor (data source from SQLite query) and the ListView (visual representation) and configures two aspects:
Which layout template to inflate for an item
Which fields of the cursor to bind to views in the template
Creating the View Template
When we want to display a series of items into a list using a custom representation of the items, we need to use our own custom XML layout template for each item. We can simply create an XML layout template in res/layout/item_todo.xml representing a particular cursor row:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<TextView
android:id="#+id/tvBody"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Study cursors"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView
android:id="#+id/tvPriority"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:text="3"
android:textAppearance="?android:attr/textAppearanceMedium" />
</LinearLayout>
Defining the Adapter
public class ViewAdapter extends BaseAdapter {
LayoutInflater mInflater;
public ViewAdapter() {
mInflater = LayoutInflater.from(context);
}
#Override
public int getCount() {
return favoriteList.size();
}
#Override
public Object getItem(int position) {
return null;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(R.layout.listitem,null);
}
final TextView nameText = (TextView) convertView.findViewById(R.id.nameText);
nameText.setText("Name : "+favoriteList.get(position).getName());
final TextView ageText = (TextView) convertView.findViewById(R.id.ageText);
ageText.setText("Age : "+favoriteList.get(position).getAge());
final Button edit = (Button) convertView.findViewById(R.id.edit);
edit.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
final Dialog dialog = new Dialog(context);
dialog.setContentView(R.layout.row);
dialog.setTitle("Add Data to Database");
final EditText name = (EditText) dialog.findViewById(R.id.name);
final EditText age = (EditText) dialog.findViewById(R.id.age);
Button Add = (Button) dialog.findViewById(R.id.Add);
Add.setText("Add");
Add.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if(name.getText().toString() != null && name.getText().toString().length() >0 ){
if(age.getText().toString() != null && age.getText().toString().length() >0 ){
db.updateRow(favoriteList.get(position).getId(), name.getText().toString(), age.getText().toString());
favoriteList = db.getFavList();
listView.setAdapter(new ViewAdapter());
dialog.dismiss();
}else{
Toast.makeText(getApplicationContext(), "Please Enter the Age", Toast.LENGTH_LONG).show();
}
}else{
Toast.makeText(getApplicationContext(), "Please Enter the Name", Toast.LENGTH_LONG).show();
}
}
});
dialog.show();
}
});
final Button delete = (Button) convertView.findViewById(R.id.delete);
delete.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
db.removeFav(favoriteList.get(position).getId());
notifyDataSetChanged();
favoriteList = db.getFavList();
listView.setAdapter(new ViewAdapter());
}
});
return convertView;
}
}
Create database
DatabaseHandler.java
public class DatabaseHandler extends SQLiteOpenHelper {
//Database Version
private static final int DATABASE_VERSION = 1;
//Database Name
private static final String DATABASE_NAME = "Test";
//Table Name
private static final String TABLE_TEST = "TestTable";
//Column Name
private static final String KEY_ID = "id";
private static final String KEY_NAME = "name";
private static final String KEY_AGE = "age";
public DatabaseHandler(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
//Create Table
#Override
public void onCreate(SQLiteDatabase db) {
String CREATE_CONTACTS_TABLE = "CREATE TABLE " + TABLE_TEST + "("
+ KEY_ID + " INTEGER PRIMARY KEY," + KEY_NAME + " TEXT,"
+ KEY_AGE + " TEXT" + ")";
db.execSQL(CREATE_CONTACTS_TABLE);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_TEST);
onCreate(db);
}
//Insert Value
public void adddata(Context context,String movieId,String songId) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(KEY_NAME, movieId);
values.put(KEY_AGE, songId);
db.insert(TABLE_TEST, null, values);
db.close();
}
//Get Row Count
public int getCount() {
String countQuery = "SELECT * FROM " + TABLE_TEST;
int count = 0;
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery(countQuery, null);
if(cursor != null && !cursor.isClosed()){
count = cursor.getCount();
cursor.close();
}
return count;
}
//Delete Query
public void removeFav(int id) {
String countQuery = "DELETE FROM " + TABLE_TEST + " where " + KEY_ID + "= " + id ;
SQLiteDatabase db = this.getReadableDatabase();
db.execSQL(countQuery);
}
//Get FavList
public List<FavoriteList> getFavList(){
String selectQuery = "SELECT * FROM " + TABLE_TEST;
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery(selectQuery, null);
List<FavoriteList> FavList = new ArrayList<FavoriteList>();
if (cursor.moveToFirst()) {
do {
FavoriteList list = new FavoriteList();
list.setId(Integer.parseInt(cursor.getString(0)));
list.setName(cursor.getString(1));
list.setAge(cursor.getString(2));
FavList.add(list);
} while (cursor.moveToNext());
}
return FavList;
}
}
Enojoys.... :)
It is better to use cursor adapter to bind the list view.You can use Loader to get the list updated even if there is a change in the data base.
onLoadFinished (Loader loader, D data) of the Loader call back would be monitor for changes to the data, and report them to you through new calls. You should not monitor the data yourself.