【问题标题】:Can database entries added from items be deleted with button?可以使用按钮删除从项目中添加的数据库条目吗?
【发布时间】:2017-08-18 06:51:04
【问题描述】:

我正在尝试使用“Android 编程:大书呆子牧场指南”自学 android 开发,其中一个练习(如果您熟悉这本书,则从第 14 章开始)涉及创建一个工具栏,其中包含一个添加新项目的项目单击该项目时进入数据库的条目。一个挑战问题是删除条目,但我想从条目中删除条目。抱歉,如果我没有很好地解释这一点。

第一次打开应用程序时,基本上是一个空的显示屏,右上角有一个带有两个项目的工具栏。 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 操作,但是如果我试图从由条目中的项目创建的数据库中删除条目,这会去哪里?

我想添加的其他内容是通过滑动以显示删除按钮从原始页面中删除条目,就像在 Messenger 应用中一样。

对不起,如果我没有正确地提出我的问题。这对我来说是一个全新的世界,与这本书一起阅读很有帮助,但我发现很容易认为你理解了一些东西,然后自己就完全迷失了。 另外,我查看了 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);
            }
                                         }
        );

【问题讨论】:

    标签: java android android-sqlite


    【解决方案1】:

    备选答案

    以下是一个非常基本但有效的示例的代码。但是,通过合并 ListView 并允许通过 LongClicking ListView 中的项目进行删除,它更进一步。

    但是,这不使用片段。

    有 3 段代码,MainActivity (MainActivity.java)、SQLiteOpenHelper 子类 CrimeDBHelper (CrimeDBHelper.java) 和 MainActivity 的布局,activity_main.xml :-

    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>
    

    CrimeDBHelper.java

    大部分是相似的,除了附加方法 getCrimeList(),它返回一个 Cursor,其中包含犯罪表中的所有数据(用于填充 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);
        }
    }
    

    MainActivity.java

    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 em>)。

    您可能还注意到 SimpleCursorAdapter sca; 这将用于 ListView基本上它将光标中的数据放在 ListView 中 em>)。

    您应该熟悉以下大部分代码。总而言之:-

    • super.onCreate 被调用。
    • 活动设置为使用 activity_main.xml 布局。
    • 加载布局后,将获得与视图关联的 ID。
    • 获得了从数据库中获取当前犯罪的光标(可能没有,这不是问题)。

    • 添加了用于添加犯罪的按钮侦听器。请注意,这使用了添加行的返回 _id 两次(实际上是 3 次,因为它会相应地更改删除按钮文本)。

      • lastcrimeidaddCrime() 方法的返回设置。
      • mDltCrime.setTag(lastcrimeid);将删除按钮的标签设置为添加行的_id

      • 另请注意,还有两行,即crimelist = dbhlpr.getCrimeList();sca.swapCursor(crimelist);

        • 第一个用现在数据库中的内容替换游标(即包括已添加的行),第二个告诉 ListView 使用新游标,因此导致 ListView 显示现在数据库中的内容(这在删除一行时再次使用)。
      • 然后添加删除按钮的按钮侦听器。这可以通过两种方式发挥作用。 lastcrimeid 可以被使用,或者按钮的标签也可以被用于保存要删除的行的_id。代码把前者注释掉了,所以使用后一种方法(即检索按钮标签中的值)。

        • 请注意,后一种方法的缺点是值可以为空,这会导致空指针异常,因此if (view.getTag != null)
      • 如上刷新ListView

      • 接下来设置 SimpleCursorAdapter,它需要 5 个参数:-

        • 要使用的布局 (android.R.layout.simple_list_item_1) 是标准布局。
        • 要以光标形式使用的数据。笔记!必须存在名为 _id 的列(出于这个原因,始终使用 _id INTEGER PRIMARY KEY 通常是个好主意。)请注意,我们得到了一个光标 crimelist 通过getCrimeList 方法。
        • 要从中检索数据的游标中的列。
        • 布局中将放置检索到的数据的视图。
        • 一个我不记得其用途的值。但是 0 很好用。不编码这第 5 个参数可能会导致消息被弃用。
        • (请注意,我通常使用自定义 CursorAdapter,因为它们更加灵活,因此很少使用 Simples)。
    • 然后告诉 ListView 按照mCrimeList.setAdapter(sca); 使用适配器。

    • 然后将onItemLongClickListener添加到ListView,这将删除被长按的犯罪(long l是_id值,因此CursorAdapter需要_id强> 因此为什么dbhlpr.deleteCrime(l);)。

      • ListView 再次刷新。
    • 最后,当Activity仍在使用时,使用了光标onDestory方法用于关闭Cursor(完成后应该始终关闭Cursors)。

    这就是它的外观(不漂亮但实用),添加了三个犯罪(删除按钮将删除 世纪犯罪 犯罪)。长按任何列出的罪行将删除该罪行。除非数据被更改,否则单击添加将添加另一个世纪犯罪条目。

    【讨论】:

    • 刚刚意识到这个答案也是你发布的。非常感谢你的帮助。我将在一个全新的项目中构建它并尝试了解正在发生的事情,以便我可以在我的项目中实现它。我并不反对使用 listview,而且似乎它甚至可能更适合我正在做的事情。我只是在使用片段跟随我的书。我最终可能会以这种格式完全重做所有事情!
    【解决方案2】:

    我认为mDatabase.delete(CrimeTable.NAME, null, new String[] {uuidString}); 是不正确的。

    第二个参数应该是选择标准或where子句(减去WHERE关键字)
    例如yourtablecolumn = yourvalue

    第三个参数可以和第二个参数一起使用。第二个可以有一个占位符?或多个占位符用于更复杂的选择标准)。因此,在您的示例中,它将是:-

    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);
    

    Crime 对象的构造函数是否从数据库中获取所有数据,这可能是 mCrime 基本上没有任何真正可用的数据(无法通过提供的信息来判断,因此可能包含完整的 Crime 类代码) .

    此外,crimeDelete 期望并获得一个犯罪对象,您将其命名为犯罪 ID,这很可能不是犯罪表中条目的 id,而是转换为字符串的犯罪对象。

    你很有可能

    a) 需要在Crime mCrime = new Crime();之后设置Crime对象的相应值

    然后

    b) 从 'deleteCrime' 方法中的 crimeid 获取适当的 crimeid 值。我建议不要将犯罪称为 crimeid,而是使用更具描述性的名称。

    假设您的犯罪类有一个成员 mID,它根据犯罪表中保存的内容保存唯一标识符,那么类似于以下内容:-

    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 KEYcolumnname INTEGER PRIMARY KEY AUTOINCREMENT(不建议使用后者,因为它很可能不需要并且有开销)。

    在包含 Crime.java 和 DBHelper 之后的添加

    Crime mCrime = new crime();

    将构造一个名为 mCrime 的对象,该对象具有随机生成的 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);
    }
    

    您会注意到它返回一个整数,这是受影响的行数。 0 = 未删除任何内容,您希望它为 1。因此您现在可以检查是否删除了任何内容。

    所以你可以这样做:-

    CrimeLab.get(getActivity()).deleteCrime(myCrimeID);
    

    注意!如果您确实更改了表的结构,则需要删除应用程序的数据,因为 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
    

    以上内容还提供了一些关于使用光标从数据库中提取/访问数据的见解。

    【讨论】:

    • 我尝试将“null”更改为所有要删除的列值,因此我调用了 mDatabase.delete 五次,每列一次,使用不同的列。结果是一样的,按钮还是没有效果。感谢您的建议
    • 假设正在调用/调用 crimeDelete,那么 uuidString 与某个值不匹配。因此,首先确认crimeDelete 实际上正在被调用,然后,如果是这样,则uuisString 具有合适的值。在mDatabase.delete(...... 添加断点,然后在调试模式下运行,您可以同时执行这两项操作。
    • 我将此代码添加到我的主类文件中: mDeleteButton = (Button)v.findViewById(R.id.delete_button); mDeleteButton.setOnClickListener(new View.OnClickListener(){ public void onClick(View v){ CrimeLab.get(getActivity()).deleteCrime(mCrime); } } );现在我确定该进程正在被调用,但它导致我的应用程序崩溃而不是删除条目。有什么想法吗?再次感谢
    • @KyleG 查看更新的答案,还有其他问题可能表明 uuidString 值错误的原因。但是,我不得不做出很多猜测。
    • 感谢您的帮助。我添加了crime.java,但我需要一些时间来完成并测试其他所有内容。我应该直接退出数据库类吗?
    猜你喜欢
    • 2020-01-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-21
    • 1970-01-01
    相关资源
    最近更新 更多