我正在尝试使用“Android编程:大书呆子牧场指南”教授自己的Android开发,其中一个练习(如果您熟悉本书,请参阅第14章)涉及创建一个带有添加新项目的工具栏单击项目时数据库的条目。挑战问题是删除条目,但我想删除条目中的WITHIN条目。对不起,如果我没有做好解释这个。
基本上第一次打开应用程序时,大多数都是空的显示屏,工具栏右上角有两个项目。 Here is a screenshot if it helps。这两个项目添加或计算条目数,然后显示在列表中。在上图中,有一个条目。单击+符号时,会添加一个新条目,它会将您带到layout以获取允许您添加所有详细信息的条目。我想添加一个删除按钮,允许您在查看详细信息时删除条目。
这是我尝试过的代码,但没有用到: 在CrimeLab.java中
public void addCrime(Crime c){
ContentValues values = getContentValues(c);
mDatabase.insert(CrimeTable.NAME, null, values);
}
public void deleteCrime(Crime crimeId){
String uuidString = crimeId.toString();
mDatabase.delete(CrimeTable.NAME, null, new String[]
{uuidString});
}
我基本上试图撤消创建数据库条目时所做的操作。 在CrimeListFragment.java中:
@Override
public boolean onOptionsItemSelected(MenuItem item){
switch (item.getItemId()){
case R.id.menu_item_new_crime:
Crime crime = new Crime();
CrimeLab.get(getActivity()).addCrime(crime);
Intent intent =
CrimePagerActivity.newIntent(getActivity(), crime.getId());
startActivity(intent);
return true;
case R.id.menu_item_show_subtitle:
mSubtitleVisible = !mSubtitleVisible;
getActivity().invalidateOptionsMenu();
updateSubtitle();
return true;
case R.id.delete_button:
Crime mCrime = new Crime();
CrimeLab.get(getActivity()).deleteCrime(mCrime);
default:
return super.onOptionsItemSelected(item);
}
}
我想我需要创建一个onClick操作,但是如果我试图从条目中的项目创建的数据库中删除条目,那么这会是什么呢?
我想添加的其他内容是通过滑动显示删除按钮来删除原始页面中的条目,就像在信使应用程序中一样。
很抱歉,如果我没有正确提出我的问题。这对我来说是一个全新的世界,随着这本书的出现很有帮助,但我发现很容易让你觉得你理解了一些东西然后完全迷失了。 此外,我检查了stackoverflow和其他各种论坛,但没有找到这个问题的确切答案。
根据请求,这是Crime.java的代码:
public class Crime {
private String mTitle;
private Date mDate;
private boolean mSolved;
private String mSuspect;
public Date getDate() {
return mDate;
}
private UUID mId;
public void setDate(Date date) {
mDate = date;
}
public void setSolved(boolean solved) {
mSolved = solved;
}
public String getSuspect(){
return mSuspect;
}
public void setSuspect (String suspect){
mSuspect = suspect;
}
public String getPhotoFilename(){
return "IMG_" + getId().toString() + ".jpg";
}
public boolean isSolved() {
return mSolved;
}
public Crime(){
//generate unique identifier
this(UUID.randomUUID());
}
public Crime(UUID id){
mId = id;
mDate = new Date();
}
public UUID getId() {
return mId;
}
public String getTitle() {
return mTitle;
}
public void setTitle(String title) {
mTitle = title;
}
}
这是控制我的数据库条目的.java类:
public class CrimeBaseHelper extends SQLiteOpenHelper{
private static final int VERSION = 1;
private static final String DATABASE_NAME = "crimeBase.db";
public CrimeBaseHelper(Context context){
super(context, DATABASE_NAME, null, VERSION);
}
@Override
public void onCreate(SQLiteDatabase db){
db.execSQL("create table " + CrimeTable.NAME + "(" +
" _id integer primary key autoincrement, " +
CrimeTable.Cols.UUID + ", " +
CrimeTable.Cols.TITLE + ", " +
CrimeTable.Cols.DATE + ", " +
CrimeTable.Cols.SOLVED + ", " +
CrimeTable.Cols.SUSPECT +
")"
);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion){
}
}
这就是我弄乱.onClickListener
的地方mDeleteButton = (Button)v.findViewById(R.id.delete_button);
mDeleteButton.setOnClickListener(new View.OnClickListener(){
public void onClick(View v){
Crime mCrime = new crime();
mCrime.getId()
CrimeLab.get(getActivity()).deleteCrime(mCrime);
}
}
);
答案 0 :(得分:1)
我认为mDatabase.delete(CrimeTable.NAME, null, new String[]
{uuidString});
不正确。
第二个参数应该是选择标准或 where子句(少于WHERE关键字)
例如yourtablecolumn = yourvalue
。
第3个参数可以与第2个一起使用。第二个可以有一个占位符?(或许多占位符用于更复杂的选择标准)。所以在你的例子中,它将是: -
String whereclause = "mycolumn=?";
mDatabase.delete(CrimeTable.NAME,whereclause, new String[]{uuidString});
显然 mycolumn
会被实际的列名替换。
您可能会发现这个有用的SQLiteDatabase - delete
另外,你有: -
case R.id.delete_button:
Crime mCrime = new Crime();
CrimeLab.get(getActivity()).deleteCrime(mCrime);
犯罪对象的构造函数,从数据库获取所有数据,可能是mCrime基本上没有任何实际可用数据(无法告知所提供的信息,因此可能包含完整的Crime类代码)
此外,CrimeDelete,期望并获得一个Crime对象,你将其命名为crimeid,这很可能不是Crimetable中条目的id,而是将Crimeobject转换为字符串。
你很可能
a)需要在Crime mCrime = new Crime();
然后
b)在“删除犯罪”中从犯罪分子获取适当的犯罪率值。方法。我建议不要使用犯罪嫌疑人,而是使用更具描述性的内容。
假设你的Crime类有一个成员mID,它根据CrimeTable中的内容保存唯一标识符,那么就是: -
case R.id.delete_button
Crime mCrime = new crime();
mCrime.setID(ID_obtained_from_displayed_crime);
CrimeLab.get(getActivity()).deleteCrime(mCrime);
public void deleteCrime(Crime crime){
mDatabase.delete(CrimeTable.NAME, "IDCOLUMN=?", new String[]
{crime.getID()});
}
这假设犯罪类中的方法setID()
和String getID()
,并且IDCOLUMN
是用于unqiue标识符的列(例如,存在一个),例如它将被定义为columnname INTEGER PRIMARY KEY
或columnname INTEGER PRIMARY KEY AUTOINCREMENT
(ps不建议后者,因为它很可能不需要并且有开销)。
Crime mCrime = new crime();
将构造一个名为mCrime的对象,该对象具有随机生成的UUID,该UUID很可能不是存储在数据库中的UUID,更不可能是要删除的UUID。
正如您已经_id integer primary key autoincrement,
(PS AUTOINCREMENT最好根据SQLite Autoincrement 删除,但不是问题)那么UUID几乎没有任何需要,因为您将依赖于什么?在数据库中, _id
列是理想的。
当你插入它时,返回插入行的 id ,这样你就可以轻松地使用: -
public long addCrime(Crime c){
ContentValues values = getContentValues(c);
return mDatabase.insert(CrimeTable.NAME, null, values);
}
所以你可以: -
在片段/活动代码long MyCrimeID;
更改CrimeLab.get(getActivity()).addCrime(crime);
myCrimeID = CrimeLab.get(getActivity()).addCrime(crime);
请注意! id将始终为1或更大,0表示未插入(不应该发生,因为根据您的表定义,只有_id列必须是唯一的。)
我建议将crimeDelete
更改为例如public int deleteCrime(long id){
String whereclause = "-id=?";
String[] whereargs = {Long.toString(id)};
return mDatabase.delete(CrimeTable.NAME, whereclause, whereargs);
}
。它可能是: -
CrimeLab.get(getActivity()).deleteCrime(myCrimeID);
您会注意到它返回一个整数,这是受影响的行数。 0 =没有删除,你希望它是1.所以你现在可以检查是否有任何内容被删除。
所以你可以这样做: -
onCreate
注意!如果您确实更改了表的结构,则需要删除应用程序的数据,因为只有在数据库文件不存在时才会自动调用 public void showAll(String table) {
Cursor csr = mDatabase.query(table,null,null,null,null,null,null);
String rowinfo;
while (csr.moveToNext()) {
rowinfo = "Row " + Integer.toString(csr.getPosition()) + " has:-";
for (int i=0; i < csr.getColumnCount(); i++) {
rowinfo = rowinfo + "\n\t Column Name=" +
csr.getColumnName(i) +
" with a value of " +
csr.getString(i);
}
Log.d("SHOWALL",rowinfo);
}
}
。
这假设是一个非常基本的场景,您只能删除刚刚添加的内容。
CrimeLab.get(getActivity()).showALL(CrimeTable.NAME);
使用
在适当的位置调用此方法08-17 09:55:46.073 2310-2310/mjt.cardoniser D/SHOWALL: Row 0 has:-
Column Name=_id with a value of 2
Column Name=username with a value of NotMikeatAll
Column Name=userhash with a value of DZQGvy0a9l0M/kmpmDlR8Q==
Column Name=usersalt with a value of YaqqUZVgOO6OOG4cnAyQndipcEw6YWNAV5DvJKl8gRA=
Column Name=userflags with a value of 0
然后,这将在以下行中添加行: -
playlist = QMediaPlaylist()
url = QUrl.fromLocalFile("./sound2.mp3")
playlist.addMedia(QMediaContent(url))
playlist.setPlaybackMode(QMediaPlaylist.Loop)
player = QMediaPlayer()
player.setPlaylist(playlist)
player.play()
上述内容还提供了一些使用 Cursor 来提取/访问数据库中数据的方法。
答案 1 :(得分:1)
以下是一个非常基本但有效的例子的代码。但是,通过合并 ListView 并允许通过LongClicking ListView 中的项目进行删除,它会更进一步。
但是,这并没有使用片段。
有3段代码, MainActivity (MainActivity.java
),SQLiteOpenHelper子类 CrimeDBHelper (CrimeDBHelper.java
)和MainActivity的布局,activity_main.xml
: -
这非常简单。请注意,它最后包含 ListView 。
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="The Crime Thing"
android:layout_gravity="center"
android:textStyle="bold"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Crime Title"
/>
<EditText
android:id="@+id/crimetitle"
android:layout_width="0dp"
android:layout_weight="3"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Crime Date"
/>
<EditText
android:id="@+id/crimedate"
android:layout_width="0dp"
android:layout_weight="3"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Suspect"
/>
<EditText
android:id="@+id/crimesuspect"
android:layout_width="0dp"
android:layout_weight="3"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Crime Solved?"
/>
<CheckBox
android:id="@+id/crimesolved"
android:layout_width="0dp"
android:layout_weight="3"
android:layout_height="wrap_content" />
</LinearLayout>
<Button
android:id="@+id/addcrime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ADD CRIME"/>
<Button
android:id="@+id/dltcrime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="DLT CRIME (ID=?)"/>
<ListView
android:id="@+id/crimelist"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</ListView>
大多数是相似的,除了附加方法 getCrimeList()
之外,它返回一个Cursor,其中包含来自crime表的所有数据(用于填充ListView)。
public class CrimeDBHelper extends SQLiteOpenHelper {
public static final String DBNAME = "crimesdb";
public static final int DBVERSION = 1;
public static final String CRIMESTABLE = "crimes";
public static final String CRIMEID_COL = "_id";
public static final String CRIMETITLE_COL = "crimetitle";
public static final String CRIMEDATE_COL = "crimedate";
public static final String CRIMESUSPECT_COL = "crimesuspect";
public static final String CRIMESOLVED_COL = "crimesolved";
public static final String TABLECRTSQL =
"CREATE TABLE " + CRIMESTABLE + "(" +
CRIMEID_COL + " INTEGER PRIMARY KEY," +
CRIMETITLE_COL + " TEXT," +
CRIMEDATE_COL + " TEXT, " +
CRIMESUSPECT_COL + " TEXT, " +
CRIMESOLVED_COL + " INTEGER" +
");";
public CrimeDBHelper(Context context) {
super(context, DBNAME, null, DBVERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(TABLECRTSQL);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldversion, int newversion) {
}
public long addCrime(String crimetitle, String crimedate, String crimesuspect, int crimesolved) {
SQLiteDatabase db = getWritableDatabase();
ContentValues cv = new ContentValues();
cv.put(CRIMETITLE_COL,crimetitle);
cv.put(CRIMEDATE_COL,crimedate);
cv.put(CRIMESUSPECT_COL,crimesuspect);
cv.put(CRIMESOLVED_COL,crimesolved);
return db.insert(CRIMESTABLE,null,cv);
}
public int deleteCrime(long crimeid) {
SQLiteDatabase db = getWritableDatabase();
String whereclause = CRIMEID_COL + "=?";
String[] whereargs = {Long.toString(crimeid)};
return db.delete(CRIMESTABLE,whereclause,whereargs);
}
public Cursor getCrimeList() {
SQLiteDatabase db = getWritableDatabase();
return db.query(CRIMESTABLE,null,null,null,null,null,null,null);
}
}
public class MainActivity extends AppCompatActivity {
EditText mCrimeTitle;
EditText mCrimeDate;
EditText mCrimeSuspect;
CheckBox mCrimeSolved;
Button mAddCrime;
Button mDltCrime;
ListView mCrimeList;
CrimeDBHelper dbhlpr = new CrimeDBHelper(this);
Cursor crimelist;
SimpleCursorAdapter sca;
long lastcrimeid;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mCrimeTitle = (EditText) findViewById(R.id.crimetitle);
mCrimeDate = (EditText) findViewById(R.id.crimedate);
mCrimeSuspect = (EditText) findViewById(R.id.crimesuspect);
mCrimeSolved = (CheckBox) findViewById(R.id.crimesolved);
mCrimeList = (ListView) findViewById(R.id.crimelist);
mAddCrime = (Button) findViewById(R.id.addcrime);
mDltCrime = (Button) findViewById(R.id.dltcrime);
crimelist = dbhlpr.getCrimeList();
// Setup Button to Add a crime
mAddCrime.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
int solved = 0;
if (mCrimeSolved.isChecked()) {
solved = 1;
}
lastcrimeid = dbhlpr.addCrime(
mCrimeTitle.getText().toString(),
mCrimeDate.getText().toString(),
mCrimeSuspect.getText().toString(),
solved
);
mDltCrime.setText("DLT CRIME (ID=" + Long.toString(lastcrimeid) + ")");
mDltCrime.setTag(lastcrimeid);
crimelist = dbhlpr.getCrimeList();
sca.swapCursor(crimelist);
}
});
// Setup button to delete the latest Crime added
mDltCrime.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//dbhlpr.deleteCrime(lastcrimeid); can do it this way
if (view.getTag() != null) {
dbhlpr.deleteCrime((long)view.getTag());
crimelist = dbhlpr.getCrimeList();
sca.swapCursor(crimelist);
}
}
});
sca = new SimpleCursorAdapter(this,
android.R.layout.simple_list_item_1,
crimelist,
new String[]{CrimeDBHelper.CRIMETITLE_COL},
new int[]{android.R.id.text1},
0
);
mCrimeList.setAdapter(sca);
mCrimeList.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) {
dbhlpr.deleteCrime(l);
crimelist = dbhlpr.getCrimeList();
sca.swapCursor(crimelist);
return true;
}
});
}
protected void onDestroy() {
super.onDestroy();
if (crimelist != null) {
crimelist.close();
}
}
}
首先要注意的是行long lastcrimeid;
,这是在类级别声明的,因此在整个过程中都非常有用(你遇到的问题 long databaseID
强> 的)。
您可能还会注意到 SimpleCursorAdapter sca;
这将用于 ListView
(基本上它会将光标中的数据放入ListView )。
您应该熟悉以下大部分代码。在summray: -
获取光标获取数据库中的当前犯罪(可能没有,这不是问题)。
添加了添加犯罪的按钮侦听器。请注意,这会使用已添加行的返回 _id 两次(实际上是3次,因为它会相应地更改删除按钮文本)。
lastcrimeid
是通过返回addCrime()
方法设置的。 mDltCrime.setTag(lastcrimeid);
将删除按钮的标记设置为添加的行的 _id
。
另请注意,还存在另外两行,即crimelist = dbhlpr.getCrimeList();
和sca.swapCursor(crimelist);
。
然后添加删除按钮的按钮侦听器。这可以通过两种方式工作。可以使用 lastcrimeid
,或者可以使用按钮按钮来保存要删除的行的 _id 。代码将前者注释掉,因此使用后一种方法(即检索按钮标记中的值)。
if (view.getTag != null)
。如上所述,用于刷新 ListView 。
接下来设置SimpleCursorAdapter,它需要5个参数: -
_id INTEGER PRIMARY KEY
。)注意我们得到一个Cursor {{1} 通过crimelist
方法。然后告诉ListView按照getCrimeList
使用适配器。
然后将mCrimeList.setAdapter(sca);
添加到ListView,这将删除长时间点击的犯罪(长l是 _id 值,因此CursorAdapter需要< strong> _id ,因此为onItemLongClickListener
}。
最后,当活动仍在使用时使用游标dbhlpr.deleteCrime(l);
方法用于关闭游标(游标应该在完成时始终关闭)。
这是它的外观(不漂亮但功能齐全),添加了三个犯罪(删除按钮将删除世纪之罪犯罪)。长期点击任何列出的犯罪将删除该犯罪。单击“添加”将为“Century of the Century”添加另一个条目,除非数据已更改。