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

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

ただいまの
回答率

90.48%

  • Java

    14144questions

    Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

  • Android

    6635questions

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

SQLiteを使用した保存/取り出しについて

解決済

回答 2

投稿

  • 評価
  • クリップ 1
  • VIEW 135

Neev

score 1

 前提・実現したいこと

Androidの勉強として、SQLiteを使用した簡単なメッセージアプリを作成しています。
ただ、メッセージの保存/取り出しに関して、下記の通り意図しない動作が発生しています。

 発生している問題・エラーメッセージ

下記のような順序でメッセージを保存したとき

1. AAAA
2. BBBB
3. CCCC

次回にDBから取り出してListViewで一覧化すると下記のようになっています。

1. CCCC
2. CCCC
3. CCCC

SQLiteを使用したアプリは初めて作成するので、使用方法や呼び出し方などに誤りがあるのではないかと色々なサイトも調べましたが、似たような事象を発見できず行き詰っています。

 SQLiteOpenHelper

public class DBHelper extends SQLiteOpenHelper {
    public DBHelper(Context context) {
        super(context, Consts.DB_NAME, null, Consts.DB_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("create table " + Consts.TABLE_NAME_MESSAGE + " ("
                + Consts.COLUMN_NAME_MESSAGE_ID + " integer primary key autoincrement, "
                + Consts.COLUMN_NAME_ROOM_ID + " integer not null, "
                + Consts.COLUMN_NAME_MESSAGE + " text not null, "
                + Consts.COLUMN_NAME_SPEAKER_TYPE + " text not null, "
                + Consts.COLUMN_NAME_DATE + " text not null);");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("drop table if exists " + Consts.TABLE_NAME_MESSAGE);
        onCreate(db);
    }
}

 DBUtils 

public class DBUtils {
    /**
     * [概 要] メッセージ取得メソッド。<br>
     * [詳 細] メッセージを取得するメソッド。<br>
     * [備 考] <br>
     */
    public static List<MessageBean> queryMessageRecodes(Context context){
        DBHelper dbHelper = new DBHelper(context);
        SQLiteDatabase db = dbHelper.getReadableDatabase();

        // SQL文作成
        StringBuilder sql = new StringBuilder();
        sql.append("SELECT ");
        sql.append(Consts.COLUMN_NAME_MESSAGE + ", ");
        sql.append(Consts.COLUMN_NAME_SPEAKER_TYPE + ", ");
        sql.append(Consts.COLUMN_NAME_DATE + " ");
        sql.append("FROM " + Consts.TABLE_NAME_MESSAGE + ";");
        Log.d(Consts.LOG_TAG, "queryMessageRecodes SQL:" + String.valueOf(sql));

        // 取得したレコードをリストに詰める
        List<MessageBean> messageList = new ArrayList<>();
        MessageBean messageBean = new MessageBean();
        Cursor cursor = db.rawQuery(sql.toString(), null);
        while (cursor.moveToNext()) {
            messageBean.setMessage(cursor.getString(0));
            messageBean.setSpeakerType(cursor.getString(1));
            messageBean.setDate(cursor.getString(2));

            messageList.add(messageBean);
        }
        cursor.close();
        db.close();

        return messageList;
    }

    /**
     * [概 要] メッセージ保存メソッド。<br>
     * [詳 細] メッセージを保存するメソッド。<br>
     * [備 考] <br>
     */
    public static void insertMessageRecode(Context context, MessageBean messageBean){
        DBHelper dbHelper = new DBHelper(context);
        SQLiteDatabase db = dbHelper.getWritableDatabase();

        // メッセージをDBに格納
        ContentValues contentValues = new ContentValues();
        contentValues.put(Consts.COLUMN_NAME_ROOM_ID, 0);
        contentValues.put(Consts.COLUMN_NAME_MESSAGE, messageBean.getMessage());
        contentValues.put(Consts.COLUMN_NAME_SPEAKER_TYPE, messageBean.getSpeakerType());
        contentValues.put(Consts.COLUMN_NAME_DATE, messageBean.getDate());

        db.insert(Consts.TABLE_NAME_MESSAGE, "", contentValues);
        db.close();
    }

    /**
     * [概 要] 全メッセージ削除メソッド。<br>
     * [詳 細] 全てのメッセージを削除するメソッド。<br>
     * [備 考] <br>
     */
    public static void deleteAllMessageRecodes(Context context){
        DBHelper dbHelper = new DBHelper(context);
        SQLiteDatabase db = dbHelper.getWritableDatabase();

        // 全メッセージを削除
        db.delete(Consts.TABLE_NAME_MESSAGE, null, null);
    }
}

 Fragment

public class ChatFragment extends Fragment {
    View mRootView;
    ListView mMessageListView;
    List<MessageBean> mMessageList = new ArrayList<>();
    ChatAdapter mChatAdapter;

    String mMessageType = Consts.MESSAGE_TYPE_I;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
        mRootView = inflater.inflate(R.layout.fragment_chat, container, false);
        mMessageListView = mRootView.findViewById(R.id.message_list);

        // メッセージをリストに追加
        mMessageList = DBUtils.queryMessageRecodes(getContext());
        mChatAdapter = new ChatAdapter(getContext(), mMessageList);
        mMessageListView.setAdapter(mChatAdapter);

        // 初期表示時にキーボードを出さない
        getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);

        // ボタンのクリックイベントを設定
        setClickEvent();

        return mRootView;
    }

    /**
     * [概 要] クリックイベント設定メソッド。<br>
     * [詳 細] フラグメント上の各ボタンのクリックイベントを設定するメソッド。<br>
     * [備 考] <br>
     */
    public void setClickEvent(){
        // 発言者変更ボタン
        ImageButton changeButton = mRootView.findViewById(R.id.change_button);
        changeButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ImageButton changeButton = mRootView.findViewById(R.id.change_button);

                if(mMessageType.equals(Consts.MESSAGE_TYPE_I)){
                    // メッセージタイプを切り替え
                    mMessageType = Consts.MESSAGE_TYPE_YOU;
                    changeButton.setImageResource(R.mipmap.ic_launcher_round);
                }
                else{
                    // メッセージタイプを切り替え
                    mMessageType = Consts.MESSAGE_TYPE_I;
                    changeButton.setImageResource(R.mipmap.ic_launcher_round);
                }
            }
        });

        // 送信ボタン
        ImageButton sendButton = mRootView.findViewById(R.id.send_button);
        sendButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                EditText editMessage = mRootView.findViewById(R.id.edit_message);
                String sendMessage = editMessage.getText().toString();

                if(sendMessage.length() <= 0){
                    // 何も入力されていなければエラー
                    editMessage.setError(getString(R.string.send_message_empty));
                    return;
                }

                // メッセージをリストに追加
                MessageBean messageBean = new MessageBean();
                messageBean.setMessage(sendMessage);
                messageBean.setSpeakerType(mMessageType);
                messageBean.setDate("");
                mMessageList.add(messageBean);

                // データベースにデータを格納
                DBUtils.insertMessageRecode(getContext(), messageBean);

                // 最新メッセージを表示
                mMessageListView.setSelection(mMessageListView.getCount() -1);

                // メッセージを空に戻す
                editMessage.setText("");

                // キーボードを隠す
                InputMethodManager inputMethodManager = (InputMethodManager)getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
                inputMethodManager.hideSoftInputFromWindow(v.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
            }
        });
    }
}
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

checkベストアンサー

+2

SQLite3は関係無いはずで、DBUtilクラスのメソッド中に問題があるようです。こうではないかと。

//ここではなく        MessageBean messageBean = new MessageBean();
        Cursor cursor = db.rawQuery(sql.toString(), null);
        while (cursor.moveToNext()) {
      // ここでインスタンス生成
          MessageBean messageBean = new MessageBean();
            messageBean.setMessage(cursor.getString(0));
            messageBean.setSpeakerType(cursor.getString(1));
            messageBean.setDate(cursor.getString(2));

            messageList.add(messageBean);
        }

最初のご提示のコードですと、MessageBeanクラスのインスタンスはwhileループ外で1回だけ生成しています。その参照をmessageListに何回もaddしていることになるので、結局最後のcursorの値を参照することになってます。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/04/26 11:58

    早急なご回答ありがとうございます。
    参照型なので、都度 new しないと最後のデータを表示してしまうという事ですね。

    SQLiteの方ばかり調査していて、ListViewに正しくデータが格納されているかの確認が疎かになっていました…。

    キャンセル

  • 2018/04/26 12:03

    コメントありがとうございます。
    > 参照型なので、都度 new しないと最後のデータを表示してしまうという事ですね。
    はい、仰るとおりです。

    キャンセル

+1

MessageBeanを一度しか作成していないので、messageListに同じオブジェクトが検索件数分追加されているようです。
MessageBeanの作成をwhileループの中に移動すればよいと思います。

        // 取得したレコードをリストに詰める
        List<MessageBean> messageList = new ArrayList<>();
        // MessageBean messageBean = new MessageBean();  // 削除
        Cursor cursor = db.rawQuery(sql.toString(), null);
        while (cursor.moveToNext()) {
            MessageBean messageBean = new MessageBean(); // 追加
            messageBean.setMessage(cursor.getString(0));
            messageBean.setSpeakerType(cursor.getString(1));
            messageBean.setDate(cursor.getString(2));

            messageList.add(messageBean);
        }

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/04/26 11:59

    早急なご回答ありがとうございます。
    SQLiteの方ばかり調査していて、ListView側の確認が疎かになっていました…。

    キャンセル

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

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

関連した質問

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

  • Java

    14144questions

    Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

  • Android

    6635questions

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