質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.52%

  • Android

    6521questions

    Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

  • Android Studio

    3701questions

    Android Studioは、 Google社によって開発された、 Androidのネイティブアプリケーション開発に特化した統合開発ツールです。

  • SQLite

    622questions

    SQLiteはリレーショナルデータベース管理システムの1つで、サーバーではなくライブラリとして使用されている。

  • Kotlin

    324questions

    Kotlinは、ジェットブレインズ社のアンドリー・ブレスラフ、ドミトリー・ジェメロフが開発した、 静的型付けのオブジェクト指向プログラミング言語です。

【Android Studio】ListView で選択したデータベースを削除したい(SQLite)

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 329

Komugi_Haiga

score 4

SQLiteを利用して、データベースにある情報をListViewに表示するアプリを作っています。

このアプリに、タッチした行のデータベースを削除する機能を追加したいのですが、

delete (String table, String whereClause, String[] whereArgs)
(Android Developers: SQLiteDatabase)

のwhereClauseとwhereArgsに何の情報を入れればできるのかが分かりません。

ご存知の方、ぜひご教授をよろしくお願いいたします。

(以下、コード一部抜粋)

(MainActivity.kt)

package com.komugi.haiga.samplesqlite

import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.support.v7.app.AlertDialog
import android.widget.*


class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        fun show() {
            //データベースに登録されている文字列の一覧を得る
            val texts = queryTexts(this)
            val listView = findViewById<ListView>(R.id.listView)
            listView.adapter = ArrayAdapter<MutableList<String>>(this, R.layout.list_text_row, R.id.textView, texts)
        }


        val button = findViewById<Button>(R.id.button)
        button.setOnClickListener {
            val editText = findViewById<EditText>(R.id.editText)
            insertText(this, editText.text.toString())
            show()
        }

        val buttonDelete = findViewById<Button>(R.id.button_delete)
        buttonDelete.setOnClickListener {
            //全削除する前に確認する
            AlertDialog.Builder(this)
                    .setTitle("全削除")
                    .setMessage("全削除します")
                    .setNegativeButton("Cancel", null)
                    .setPositiveButton("OK") { _, _ ->
                        //データベースを全削除する
                        deleteText(this)
                        show()
                    }.show()
        }

        val adapter = ArrayAdapter<String>(this, R.layout.list_text_row, R.id.textView)
        val selectedList = findViewById<ListView>(R.id.listView)
        selectedList.adapter = adapter
        selectedList.setOnItemClickListener { _, _, position, _ ->
            AlertDialog.Builder(this)
                    .setTitle("削除")
                    .setMessage("削除します")
                    .setNegativeButton("Cancel", null)
                    .setPositiveButton("OK") {_, _ ->
                        deleteItem(this, ids[position])
                        show()
                    }.show()
        }
    }
}
(SampleDatabase.kt)

package com.komugi.haiga.samplesqlite

import android.content.ContentValues
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper


private const val DB_NAME = "SampleDatabase"
private const val DB_VERSION = 1

fun queryTexts(context: Context) : Array<MutableList<String>> {
    //読み込み用のデータベースを開く
    val database = SampleDatabaseOpenHelper(context).readableDatabase
    //データベースから全件検索する
    val cursor = database.query("texts", null, null, null, null, null,"created_at DESC")

    val texts = mutableListOf<String>()
    val ids = mutableListOf<String>()
    cursor.use {
        //カーソルで順次処理していく
        while(cursor.moveToNext()) {
            //保存されているテキストを得る
            val text = cursor.getString(cursor.getColumnIndex("text"))
            val id = cursor.getString(cursor.getColumnIndex("id"))
            texts.add(text)
            ids.add(id)
        }
    }
    val resArray = arrayOf(texts)
    database.close()
    return resArray
}

fun insertText(context: Context, text: String) {
    //書き込み可能なデータベースを開く
    val database = SampleDatabaseOpenHelper(context).writableDatabase

    database.use { db ->
        //挿入するレコード
        val record = ContentValues().apply {
            put("text", text)
        }
        //データベースに挿入する
        db.insert("texts", null, record)
    }
}

//データベース全削除機能
fun deleteText(context: Context) {
    val database = SampleDatabaseOpenHelper(context).writableDatabase
    database.use { db ->
        db.delete("texts", null, null)
    }
}

//データベース選択削除機能
fun deleteItem(context: Context, id: String) {
    val database = SampleDatabaseOpenHelper(context).writableDatabase
    database.use { db ->
        db.delete("texts", "id =" + id, null)
    }
}



class SampleDatabaseOpenHelper(context: Context) : SQLiteOpenHelper(context, DB_NAME, null, DB_VERSION) {
    override fun onCreate(db: SQLiteDatabase?) {
        //テーブルの作成
        db?.execSQL("CREATE TABLE texts ( " +
                " id INTEGER PRIMARY KEY AUTOINCREMENT, "+
                " text TEXT NOT NULL, " +
                " created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP) ")
    }

    override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
        //バージョン更新時のSQL発行
    }
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

whereClauseはWHERE句、whereArgsはWHEREの引数を入れる場所です。
db.delete("texts", "id = ?", null)はSQL文で言うと
DELETE FROM texts WHERE id = ?になります。
?に入るべき引数部分がnullになってしまっているので削除したいidを指定する必要があります。

現在のところおそらくqueryTexts ()でtestカラムのみを取得してきていると思いますが、
idカラムも取得できるようにしてidsに格納しているとすると

selectedList.setOnItemClickListener { _, _, position, _ ->
          AlertDialog.Builder(this)
                    .setTitle("削除")
                    .setMessage("削除します")
                    .setNegativeButton("Cancel", null)
                    .setPositiveButton("OK") {_, _ ->
                        deleteItem(this, ids[position])
                        show()
          }.show()
}

fun deleteItem(context: Context, id: String) {
    val database = SampleDatabaseOpenHelper(context).writableDatabase
    database.use { db ->
        db.delete("texts", "id = " + id, null)
    }
}

単純な1項目だけならWHERE句に追加してしまっても良いと思います。whereArgsに渡すのはString[]なのでそちらに入れるとすれば以下のような感じで。

fun deleteItem(context: Context, id: String) {
    val args = arrayOf(id)

    val database = SampleDatabaseOpenHelper(context).writableDatabase
    database.use { db ->
        db.delete("texts", "id = ?", args)
    }
}


コメントへの回答を追記:

val ids = cursor.getString(cursor.getColumnIndex("id"))
texts.add(ids)


のようにしてしまうとtextsの中にtextとidが混在してしまうので取り出したい値を取り出しにくくなります。
なので考えられる方法としては以下のようにMutableList<String>の配列で返すか

fun queryTexts(context: Context) : Array<MutableList<String>> {
//略
     val ids = mutableListOf<String>()
     val texts = mutableListOf<String>()
     cursor.use {
          while(cursor.moveToNext()) {
               val id = cursor.getString(cursor.getColumnIndex("id"))
               ids.add(id)
               val text = cursor.getString(cursor.getColumnIndex("text"))
               texts.add(text)
          }
     }
     val resArray = arrayOf(ids, texts)
     return resArray


以下のようにクラスを作って返すか。

data class ListData(val id: String, val text: String)

fun queryTexts(context: Context) : MutableList<ListData> {
//略
     val listDatas = mutableListOf<ListData>()

     cursor.use {
          while(cursor.moveToNext()) {
               val id = cursor.getString(cursor.getColumnIndex("id"))
               val text = cursor.getString(cursor.getColumnIndex("text"))
               listDatas.add(ListData(id, text))
          }
     }
     return listDatas
}


にしてidとtextをそれぞれで取り出して使えるようすると良いと思います。

さらに回答を追記:
1.idsがどこにも定義されていないことが問題でした。
2.queryTextsの戻り値にidsがセットされていませんでしたので、[0]にtexts配列、[1]にids配列が入るように修正
3.show()の中でArray<MutableList<String>として受け取り、表示に使うtexts配列は[0]で受け取る、idsは[1]で受け取っておく。
4.idsはnullチェックをする(nullチェックについてはkotolinとしてこの方法が最適かはわからないので別途調べてみてください)

override fun onCreate(savedInstanceState: Bundle?) {
     super.onCreate(savedInstanceState)
     setContentView(R.layout.activity_main)

     var ids:MutableList<String>? = null

     fun show() {
            //データベースに登録されている文字列の一覧を得る
            val array = queryTexts(this) 
            val texts = array[0]
            ids = array[1]
            val listView = findViewById<ListView>(R.id.listView)
            listView.adapter = ArrayAdapter<MutableList<String>>(this, R.layout.list_text_row, R.id.textView, texts)
        }
// 略
     selectedList.setOnItemClickListener { _, _, position, _ ->
// 略
           if (ids != null) {
                deleteItem(this, ids!![position])
            }

fun queryTexts(context: Context) : Array<MutableList<String>> {
    // 略
    val resArray = arrayOf(texts, ids)
    database.close()
    return resArray
}

無事に動くと良いですが・・・。

さらにさらにコメントに回答を追記:
おそらくDBやCursorをきちんと処理できていないために発生するエラーだと思います。
きちんとcursorやdbを扱っている部分の書き方をみなおす(多分お作法があると思うので)
https://qiita.com/YusukeIwaki/items/74b7d52ff8bd7d2849c9
create table sqlの書き方に問題がある場合が多いかもしれないらしいです。
https://forums.bignerdranch.com/t/cant-read-row-0-col-1-from-cursorwindow/11995

とりあえず一旦、AndroidManifest.xmlのandroid:allowBackup="false"にしてアプリを削除して入れ直してみて動くかどうか試してみてください。(アプリ削除かallowBackupか、どっちかだけで良いかもしれない?)

ちょっと根本問題解決かはわかりませんが、これで解決するのであれば一旦はこの質問でのやり取りはこの辺にしておきますか。。。(SQLiteの扱い方についてはまた別途)

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/07/25 19:18

    回答ありがとうございます。
    たしかに、Sampledatabase.ktの方で下記のようにqueryTexts() を記述しています。

    fun queryTexts(context: Context) : List<String> {
    val database = SampleDatabaseOpenHelper(context).readableDatabase
    val cursor = database.query("texts", null, null, null, null, null,"created_at DESC")

    val texts = mutableListOf<String>()
    cursor.use {
    while(cursor.moveToNext()) {
    val text = cursor.getString(cursor.getColumnIndex("text"))
    texts.add(text)
    }
    }
    database.close()
    return texts
    }

    上記の
    val text = cursor.getString(cursor.getColumnIndex("text"))
    texts.add(text)
    がtextカラムの取得ということで合っていますでしょうか。

    また、「idカラムも取得できるようにしてidsに格納」というのは、
    val ids = cursor.getString(cursor.getColumnIndex("id"))
    texts.add(ids)
    の記述を追加するということでしょうか。

    キャンセル

  • 2018/07/25 22:05

    早速追記いただき、ありがとうございます。

    >のようにしてしまうとtextsの中にtextとidが混在してしまうので取り出したい値を取り出しにくくなります。
    なるほど、たしかにそうですね!
    丁寧な説明ありがとうございます。


    また続けて何度も申し訳ないのですが、

    selectedList.setOnItemClickListener { _, _, position, _ ->
    AlertDialog.Builder(this)
    .setTitle("削除")
    .setMessage("削除します")
    .setNegativeButton("Cancel", null)
    .setPositiveButton("OK") {_, _ ->
    deleteItem(this, ids[position])
    show()
    }.show()
    }

    のdeleteItem(this, ids[position])という部分で、idsにunresolved referenceというエラーが出てしまうのですが、これはどうしたらよいでしょうか。

    キャンセル

  • 2018/07/25 22:18

    setOnItemClickListenerの中でidsがローカル変数で見えない状態だと思うので
    グローバル変数にしてあげると大丈夫だと思います。
    この質問とは離れた内容になってくるのとちょっとすぐに説明をすらすら書けないので
    詳しい説明はまたの機会に・・・。
    下記のようにしてみるといかがでしょうか?

    class MainActivity : AppCompatActivity() {
    private val ids = mutableListOf<ListData>()
    override fun onCreate(savedInstanceState: Bundle?) {
    //略

    キャンセル

  • 2018/07/25 22:58

    失礼しました。idsはString配列なので以下ですね。混乱させてしまい申し訳ないです。

    class MainActivity : AppCompatActivity() {
    private val ids = mutableListOf<String>()
    override fun onCreate(savedInstanceState: Bundle?) {
    //略

    キャンセル

  • 2018/07/25 23:49

    度たび申し訳ないです。unresolved referenceなのでおそらく原因が違いますね。
    どのような全体コードになったか記載いただいても良いでしょうか?

    キャンセル

  • 2018/07/26 00:00

    こちらこそ何度もお手数を煩わせてしまい、申し訳ないです。
    質問に全体コードを載せました。
    ご確認お願いいたします。

    キャンセル

  • 2018/07/26 17:18

    何度も丁寧な回答を本当にありがとうございます。
    idsの未定義は解消され、アプリは起動するようになりましたが、新規データを追加しようとすると、強制終了するようになりました...。
    選択削除機能が上手く動くようになったか確認したいところですが、データが追加できなくなったので確かめられない状況です。

    キャンセル

  • 2018/07/26 17:29

    インサートできないとなるとinsertText()のどこかでエラー吐いてる感じですかね?
    エラーログを貼っていただけると解析できますが。

    キャンセル

  • 2018/07/26 18:05

    ログカットには、

    07-26 17:57:00.349 515-515/? E/Parcel: Reading a NULL string not supported here.
    Reading a NULL string not supported here.
    07-26 17:57:05.079 20008-20008/? E/memtrack: Couldn't load memtrack module (No such file or directory)
    07-26 17:57:05.079 20008-20008/? E/android.os.Debug: failed to load memtrack module: -2
    07-26 17:57:05.449 515-515/? E/Parcel: Reading a NULL string not supported here.
    Reading a NULL string not supported here.
    07-26 17:57:06.939 20059-20059/? E/memtrack: Couldn't load memtrack module (No such file or directory)
    07-26 17:57:06.939 20059-20059/? E/android.os.Debug: failed to load memtrack module: -2
    07-26 17:57:07.189 20078-20078/? E/dalvikvm: Could not find class 'android.graphics.drawable.RippleDrawable', referenced from method android.support.v7.widget.AppCompatImageHelper.hasOverlappingRendering
    07-26 17:57:08.009 515-515/? E/Parcel: Reading a NULL string not supported here.
    Reading a NULL string not supported here.
    07-26 17:57:14.049 20078-20078/com.komugi.haiga.samplesqlite E/CursorWindow: Failed to read row 0, column -1 from a CursorWindow which has 5 rows, 3 columns.
    07-26 17:57:14.059 20078-20078/com.komugi.haiga.samplesqlite E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.komugi.haiga.samplesqlite, PID: 20078
    java.lang.IllegalStateException: Couldn't read row 0, col -1 from CursorWindow. Make sure the Cursor is initialized correctly before accessing data from it.
    at android.database.CursorWindow.nativeGetString(Native Method)
    at android.database.CursorWindow.getString(CursorWindow.java:434)
    at android.database.AbstractWindowedCursor.getString(AbstractWindowedCursor.java:51)
    at com.komugi.haiga.samplesqlite.SampleDatabaseKt.queryTexts(SampleDatabase.kt:25)
    at com.komugi.haiga.samplesqlite.MainActivity$onCreate$1.invoke(MainActivity.kt:19)
    at com.komugi.haiga.samplesqlite.MainActivity$onCreate$2.onClick(MainActivity.kt:30)
    at android.view.View.performClick(View.java:4569)
    at android.view.View$PerformClick.run(View.java:18553)
    at android.os.Handler.handleCallback(Handler.java:733)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:212)
    at android.app.ActivityThread.main(ActivityThread.java:5151)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:515)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:877)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:693)
    at dalvik.system.NativeStart.main(Native Method)

    というのが載っていました。

    キャンセル

  • 2018/07/26 19:12

    >とりあえず一旦、AndroidManifest.xmlのandroid:allowBackup="false"にしてアプリを削除して入れ直してみて動くかどうか試してみてください。(アプリ削除かallowBackupか、どっちかだけで良いかもしれない?)

    falseに変えたらできました!
    インサートも、ListViewからの選択削除も、全削除も全て問題なく動作しました。
    SQLiteの扱い方、お作法については今後も勉強を重ねたいと思います。
    長い時間に渡って何度も回答いただきまして、本当にありがとうございました。

    キャンセル

  • 2018/07/27 11:16

    無事、目的の動作をするようなものができたようでよかったです。

    一応、最後のSQLiteでエラーが出ていた部分の解析の仕方だけお伝えしておきます。
    このログの中で注目すべき点はFATAL EXCEPTION: mainから下の部分です。
    Exceptionと言うのは例外と言う意味で
    java.lang.IllegalStateException:と言う例外が起きてますよと言うこと。
    例外の内容はその後の部分
    Couldn't read row 0, col -1 from CursorWindow. Make sure the Cursor is initialized correctly before accessing data from it.
    これはただの英文です。CursorWindowがどこかを読み込めなかった。カーソルが初期化されているか確認してくらいの意味が取れれば良いと思います。(Google翻訳とかでも全然良いです。)

    エラーの内容はわかったとして、じゃあどうしたら良いの?って部分は大体検索すると出てきます。
    今回の場合知りたいのは
    「java.lang.IllegalStateException: Couldn't read row 0, col -1 from CursorWindow. Make sure the Cursor is initialized correctly before accessing data from it.」
    の例外が出た時の対処方法です。

    今回の場合は
    「Make sure the Cursor is initialized correctly before accessing data from it.」
    で検索したときの1ページ目下から2番目くらいに見つけた解決方法がありました。
    検索するのもコツを掴むまではなかなか目的の情報にたどり着くのが少し難しいかもしれません。

    あとはその下の方の
    at com.komugi.haiga.samplesqlite.SampleDatabaseKt.queryTexts(SampleDatabase.kt:25)
    あたりでは実際に例外が発生した箇所、SampleDatabase.ktの25行目のqueryTextsだよと教えてくれています。
    いっぱい書かれているのは階層構造的に書かれていて、実際に発生しているのは
    at android.database.CursorWindow.nativeGetString(Native Method)
    であり、それはどこから呼ばれているかと言うと
    at android.database.CursorWindow.getString(CursorWindow.java:434)
    であり、それが呼ばれているのは・・・と言う風になっています。
    (CursorWindow.javaも中身は見れます)

    エラー時のログ解析が自分で行えるようになると開発もスムーズになると思います。
    (こう言うエラーログ解析の仕方はなかなかきちんと教わる機会も少ないですし、個別にケースが違ってきたりするので難しいですよね)

    キャンセル

  • 2018/07/27 23:35

    ご丁寧にエラーログの読み方まで教えていただきまして、ありがとうございました。
    Java、Kotlinに触れ始めてから2か月ほど経ちましたが、プログラミング初習者な上独学のため、今までログカットの使い方を教わることがありませんでした。razumaさんの説明のおかげで、やっとログカットが重要であることが分かりました。
    今まではコードを書くのに必死でシンタックスエラーしか見ておらず、ログカットをきちんと読めていませんでした。せっかくログ解析について教えていただきましたので、今後は質問する前にまずは自分でログ解析を試みたいと思います。
    delete文の説明から始まりまして、たくさんのことを教えていただきました。
    重ねてお礼申し上げます。
    もしログ解析も試みた上で分からないことがあった際にはまたよろしくお願いいたします。

    キャンセル

  • 2018/07/29 02:35

    わからないことをひとりで考えて悶々とし続けることはつらいと思いますので
    何かあれば気軽に質問あげていただければと思います。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.52%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • Android

    6521questions

    Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

  • Android Studio

    3701questions

    Android Studioは、 Google社によって開発された、 Androidのネイティブアプリケーション開発に特化した統合開発ツールです。

  • SQLite

    622questions

    SQLiteはリレーショナルデータベース管理システムの1つで、サーバーではなくライブラリとして使用されている。

  • Kotlin

    324questions

    Kotlinは、ジェットブレインズ社のアンドリー・ブレスラフ、ドミトリー・ジェメロフが開発した、 静的型付けのオブジェクト指向プログラミング言語です。