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

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

新規登録して質問してみよう
ただいま回答率
85.49%
SQLite

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

Android

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

Android Studio

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

Kotlin

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

Q&A

解決済

1回答

6430閲覧

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

Komugi_Haiga

総合スコア12

SQLite

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

Android

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

Android Studio

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

Kotlin

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

0グッド

0クリップ

投稿2018/07/25 07:33

編集2018/07/25 14:58

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発行 } }

気になる質問をクリップする

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

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 08:46

編集2018/07/26 09:40
razuma

総合スコア1313

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Komugi_Haiga

2018/07/25 10: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) の記述を追加するということでしょうか。
Komugi_Haiga

2018/07/25 13: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というエラーが出てしまうのですが、これはどうしたらよいでしょうか。
razuma

2018/07/25 13:18

setOnItemClickListenerの中でidsがローカル変数で見えない状態だと思うので グローバル変数にしてあげると大丈夫だと思います。 この質問とは離れた内容になってくるのとちょっとすぐに説明をすらすら書けないので 詳しい説明はまたの機会に・・・。 下記のようにしてみるといかがでしょうか? class MainActivity : AppCompatActivity() { private val ids = mutableListOf<ListData>() override fun onCreate(savedInstanceState: Bundle?) { //略
razuma

2018/07/25 13:58

失礼しました。idsはString配列なので以下ですね。混乱させてしまい申し訳ないです。 class MainActivity : AppCompatActivity() { private val ids = mutableListOf<String>() override fun onCreate(savedInstanceState: Bundle?) { //略
razuma

2018/07/25 14:49

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

2018/07/25 15:00

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

2018/07/26 08:18

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

2018/07/26 08:29

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

2018/07/26 09: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) というのが載っていました。
Komugi_Haiga

2018/07/26 10:12

>とりあえず一旦、AndroidManifest.xmlのandroid:allowBackup="false"にしてアプリを削除して入れ直してみて動くかどうか試してみてください。(アプリ削除かallowBackupか、どっちかだけで良いかもしれない?) falseに変えたらできました! インサートも、ListViewからの選択削除も、全削除も全て問題なく動作しました。 SQLiteの扱い方、お作法については今後も勉強を重ねたいと思います。 長い時間に渡って何度も回答いただきまして、本当にありがとうございました。
razuma

2018/07/27 02: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も中身は見れます) エラー時のログ解析が自分で行えるようになると開発もスムーズになると思います。 (こう言うエラーログ解析の仕方はなかなかきちんと教わる機会も少ないですし、個別にケースが違ってきたりするので難しいですよね)
Komugi_Haiga

2018/07/27 14:35

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

2018/07/28 17:35

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.49%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問