Stručně řečeno, musíte být schopni rozlišit řádek pro smazání podle dat dostupných pro ListView. Pokud je hodnota načtena z kurzoru, jako 2. sloupec (tj. řetězec extrahovaný pomocí res.getString(1))
, a hodnota bude jedinečná , můžete jej načíst a použít ke smazání.
Při použití ListAdapter
však existuje několik problémů asi nebude dostačující. Existují další adaptéry, jako je ArrayAdapter, které nabízejí více funkcí, a co je důležité, notifyDatasetChanged
metoda (která obnoví související ListView).
Je zbytečné vytvářet nový adaptér pro každou iteraci kurzoru. Adaptér by tedy měl být vytvořen mimo smyčku a pouze jednou.
Navrhoval bych, že smazání po kliknutí na položku bude příliš náchylné k náhodnému kliknutí, smazání na položce LongClick by bylo mnohem méně náchylné k náhodnému smazání.
Pokud přesunete proměnné jako proměnné třídy, nemusíte je deklarovat jako konečné.
Takže na základě výše uvedeného byste mohli mít :-
Metoda adaptéru pole
public class ZeigeFaecherListe extends AppCompatActivity {
DatabaseHelper myDb;
Cursor res;
ListView listViewFaecher;
ArrayAdapter<String> fachListAdapter;
ArrayList<String> faecherListe;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.zeige_faecher);
listViewFaecher = (ListView) this.findViewById(R.id.listview);
myDb = new DatabaseHelper(this);
addSomeData(); //<<<<<<<<<< ADDED for testing
faecherListe = new ArrayList<>();
res = myDb.zeigeFaecher();
while (res.moveToNext()) {
faecherListe.add(res.getString(1));
}
//<<<< NOTE outside of the loop as this only needs to be done once
fachListAdapter = new ArrayAdapter<String>(
this,
android.R.layout.simple_list_item_1,
faecherListe
);
listViewFaecher.setAdapter(fachListAdapter);
//<<<<< NOTE used LONG CLICK listener (less likely to accidentally delete)
listViewFaecher.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
myDb.deleteRow((String)fachListAdapter.getItem(position));
faecherListe.remove(position);
fachListAdapter.notifyDataSetChanged();
return true; //<<<< Indicate that this longclick has been used
}
});
}
private void addSomeData() {
for (int i=1; i <= 10; i++) {
myDb.addRow("Row " + String.valueOf(i));
}
}
}
Spolu s výše uvedeným deletRow
metoda je :-
public int deleteRow(String col2) {
SQLiteDatabase db = this.getWritableDatabase();
return db.delete(TB001,COL_TB001_DATA + "=?",new String[]{col2});
}
- kde
- TB001 je konstantní řetězec, který je nastaven na název tabulky.
- COL_TB001_DATA je název sloupce 2. sloupce.
UPOZORNĚNÍ Výše uvedené řešení bude správně fungovat pouze v případě, že 2. sloupec obsahuje jedinečná data, jinak by bylo odstraněno více řádků.
Je zde i předpoklad, že mazání funguje, mohlo by být lepší mít :-
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
if (myDb.deleteRow((String)fachListAdapter.getItem(position))<0) {
faecherListe.remove(position);
}
fachListAdapter.notifyDataSetChanged();
return true; //<<<< Indicate that this longclick has been used
}
Metoda kurzorového adaptéru
Existují však další adaptéry vhodné pro kurzory, které by mohly odstranit potřebu mezilehlého pole. Můžete použít CursorAdapter
. Pro CursorAdapter
název sloupce _id
je povinný a tento sloupec by měl být dlouhý a také nesprávně identifikovat řádek. Záměr a odtud název je, že jde o alias rowid se používá (proto také CONSTANT BaseColumns._ID
existuje).
Alias rowid je vytvořeno definováním ?? INTEGER PRIMARY KEY
kde?? je název sloupce. V ideálním případě by tedy měla být tabulka definována včetně definice sloupce s _id INTEGER PRIMARY KEY
např. CREATE mytable (_id INTEGER PRIMARY KEY, myothercolumn TEXT)
(můžete postupovat podle INTEGER PRIMARY KEY
s klíčovým slovem AUTOINCREMENT, nicméně obecně byste to neudělali, protože má režii SQLite Autoincrement)
Pokud vaše tabulka takový sloupec nemá, můžete při dotazování na data vždy vytvořit sloupec v kurzoru pomocí rowid AS _id
např. pokud se SQL rovná SELECT * FROM mytable
pak můžete použít SELECT *, rowid AS _id FROM mytable
.
V tomto příkladu SimpleCursorAdapter
bude použit, kód může být :-
public class ZeigeFaecherListe extends AppCompatActivity {
DatabaseHelper myDb;
Cursor res;
ListView listViewFaecher;
SimpleCursorAdapter fachSimpleCursorAdapter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.zeige_faecher);
listViewFaecher = (ListView) this.findViewById(R.id.listview);
myDb = new DatabaseHelper(this);
addSomeData(); //<<<<<<<<<< ADDED for testing
faecherListe = new ArrayList<>();
res = myDb.zeigeFaecher();
fachSimpleCursorAdapter = new SimpleCursorAdapter(this,
android.R.layout.simple_list_item_1, //<<<< The layout
res, //<<<< The Cursor
new String[]{"_data"}, //<<<< The column names from which to get the data
new int[]{android.R.id.text1} //<<<< The ids of the views in which the data is placed
);
listViewFaecher.setAdapter(fachSimpleCursorAdapter);
listViewFaecher.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
// id is the value of the respective _id column
//<<<< Normally you would have the delete method in the Databasehelper >>>>
myDb.getWritableDatabase().delete("mytable","_id=?",new String[]{String.valueOf(id)});
fachSimpleCursorAdapter.swapCursor(myDb.zeigeFaecher()); // Tell the adapter about the new cursor
return true;
}
});
}
}
POZNÁMKA jako _id
sloupec bude vždy jedinečný, tato metoda odstraní pouze konkrétní řádek, nikoli více řádků, pokud zobrazené hodnoty nejsou jedinečné.