[Android Studio] メモ帳を作ろう!(その4)

どうも、tatsuです!

今回はメモ帳アプリ制作その4ということでやっていこうと思います。
わからない箇所は他の記事を参考にしてみてください。

※この記事で使用しているAndroid Studioのバージョンは2.3.3です。

作成手順

メモ帳アプリを作る上で必要な手順を以下にまとめました。
本記事では⑤と⑥を紹介します。
①②③④は以下の記事を参照してください。

[Android Studio] メモ帳を作ろう!(その3)

2017.09.03

[Android Studio] メモ帳を作ろう!(その2)

2017.09.02

[Android Studio] メモ帳を作ろう!(その1)

2017.09.01
  1. 新しくプロジェクトを作る
  2. 画面レイアウトを作る
  3. 画面に動きをつける
  4. データベースに保存する
  5. テスト&手直し
  6. 完成物披露

⑤テスト&手直し

それでは作成したメモ帳がきちんと動作するかをテストしたいと思います。
簡単ですが、以下のテストを行います。

  1. リスト画面で新規作成ボタンクリック⇒メモ作成画面に遷移すること
  2. メモ作成画面で登録ボタンをクリック⇒リスト画面に遷移して、登録した内容が一覧に表示されること
  3. メモ作成画面で戻るボタンをクリック⇒リスト画面に遷移して、入力した内容は破棄されること
  4. リスト画面で一覧から項目をクリック⇒メモ作成画面に遷移して、以前登録した内容が表示されること
  5. リスト画面で一覧から項目をクリック⇒メモ作成画面で編集して登録ボタンをクリック⇒リスト画面に遷移して内容が更新されていること

テスト1

リスト画面で新規作成ボタンクリック⇒メモ作成画面に遷移することを確認します。
1. アプリを立ち上げ、新規作成ボタンをクリックします。

2. メモ作成画面に遷移することを確認します。

テスト2

メモ作成画面で登録ボタンをクリック⇒リスト画面に遷移して、登録した内容が一覧に表示されることを確認します。
1. アプリを立ち上げ、新規作成ボタンをクリックします。

2. メモ作成画面に遷移することを確認します。

3. メモを記入して登録ボタンをクリックします。

4. リスト画面に遷移して、登録した内容が一覧に表示されることを確認します。

テスト3

メモ作成画面で戻るボタンをクリック⇒リスト画面に遷移して、入力した内容は破棄されることを確認します。
1. メモ作成画面で戻るボタンをクリックします。

2. リスト画面に遷移して、入力した内容は破棄されることを確認します。

テスト4

リスト画面で一覧から項目をクリック⇒メモ作成画面に遷移して、以前登録した内容が表示されることを確認します。
1. リスト画面で一覧から項目をクリックします。

2. メモ作成画面に遷移して、以前登録した内容が表示されることを確認します。

テスト5

リスト画面で一覧から項目をクリック⇒メモ作成画面で編集して登録ボタンをクリック⇒リスト画面に遷移して内容が更新されていることを確認します。
1. リスト画面で一覧から項目をクリックします。

2. メモ作成画面で編集して登録ボタンをクリックします。

3. リスト画面に遷移して内容が更新されていることを確認します。

手直し

みなさんお気づきでしょうか・・・
そう!このメモ帳アプリ、メモを削除できません!!笑

ということで項目長押しで削除する処理を追加します。
ListActivity.javaのリスト項目クリック処理の下に以下の処理を追加してください。
これで項目長押しで削除されるようになりました!

        // リスト項目を長押しクリックした時の処理
        listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener(){
            /**
             * @param parent ListView
             * @param view 選択した項目
             * @param position 選択した項目の添え字
             * @param id 選択した項目のID
             */
            public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
                // 選択されたビューを取得 TwoLineListItemを取得した後、text2の値を取得する
                TwoLineListItem two = (TwoLineListItem)view;
                TextView idTextView = (TextView)two.getText2();
                String idStr = (String) idTextView.getText();

                // 長押しした項目をデータベースから削除
                SQLiteDatabase db = helper.getWritableDatabase();
                try {
                    db.execSQL("DELETE FROM MEMO_TABLE WHERE uuid = '"+ idStr +"'");
                } finally {
                    db.close();
                }
                // 長押しした項目を画面から削除
                memoList.remove(position);
                simpleAdapter.notifyDataSetChanged();

                // trueにすることで通常のクリックイベントを発生させない
                return true;
            }
        });

⑥完成物披露

最後に完成したメモ帳アプリのソースコードを全て紹介します。
今回リファクタリングを全くしていないのでソースが汚いと思います。時間がある方は知識の再確認の意味でリファクタリングもしてみてください。笑

ListActivity.java

public class ListActivity extends AppCompatActivity {

    // MemoOpenHelperクラスを定義
    MemoOpenHelper helper = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_list);

        // データベースから値を取得する
        if(helper == null){
            helper = new MemoOpenHelper(ListActivity.this);
        }
        // メモリストデータを格納する変数
        final ArrayList<HashMap<String, String>> memoList = new ArrayList<>();
        // データベースを取得する
        SQLiteDatabase db = helper.getWritableDatabase();
        try {
            // rawQueryというSELECT専用メソッドを使用してデータを取得する
            Cursor c = db.rawQuery("select uuid, body from MEMO_TABLE order by id", null);
            // Cursorの先頭行があるかどうか確認
            boolean next = c.moveToFirst();

            // 取得した全ての行を取得
            while (next) {
                HashMap<String,String> data = new HashMap<>();
                // 取得したカラムの順番(0から始まる)と型を指定してデータを取得する
                String uuid = c.getString(0);
                String body = c.getString(1);
                if(body.length() > 10){
                    // リストに表示するのは10文字まで
                    body = body.substring(0, 11) + "...";
                }
                // 引数には、(名前,実際の値)という組合せで指定します 名前はSimpleAdapterの引数で使用します
                data.put("body",body);
                data.put("id",uuid);
                memoList.add(data);
                // 次の行が存在するか確認
                next = c.moveToNext();
            }
        } finally {
            // finallyは、tryの中で例外が発生した時でも必ず実行される
            // dbを開いたら確実にclose
            db.close();
        }

        // Adapter生成
        final SimpleAdapter simpleAdapter = new SimpleAdapter(this,
                memoList, // 使用するデータ
                android.R.layout.simple_list_item_2, // 使用するレイアウト
                new String[]{"body","id"}, // どの項目を
                new int[]{android.R.id.text1, android.R.id.text2} // どのidの項目に入れるか
        );

        // idがmemoListのListViewを取得
        ListView listView = (ListView) findViewById(R.id.memoList);
        listView.setAdapter(simpleAdapter);

        // リスト項目をクリックした時の処理
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener(){
            /**
             * @param parent ListView
             * @param view 選択した項目
             * @param position 選択した項目の添え字
             * @param id 選択した項目のID
             */
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                // インテント作成  第二引数にはパッケージ名からの指定で、遷移先クラスを指定
                Intent intent = new Intent(ListActivity.this, com.android.tatsu.simplememo.CreateMemoActivity.class);

                // 選択されたビューを取得 TwoLineListItemを取得した後、text2の値を取得する
                TwoLineListItem two = (TwoLineListItem)view;
//                TextView idTextView = (TextView)two.findViewById(android.R.id.text2);
                TextView idTextView = (TextView)two.getText2();
                String idStr = (String) idTextView.getText();
                // 値を引き渡す (識別名, 値)の順番で指定します
                intent.putExtra("id", idStr);
                // Activity起動
                startActivity(intent);
            }
        });

        // リスト項目を長押しクリックした時の処理
        listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener(){
            /**
             * @param parent ListView
             * @param view 選択した項目
             * @param position 選択した項目の添え字
             * @param id 選択した項目のID
             */
            public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
                // 選択されたビューを取得 TwoLineListItemを取得した後、text2の値を取得する
                TwoLineListItem two = (TwoLineListItem)view;
                TextView idTextView = (TextView)two.getText2();
                String idStr = (String) idTextView.getText();

                // 長押しした項目をデータベースから削除
                SQLiteDatabase db = helper.getWritableDatabase();
                try {
                    db.execSQL("DELETE FROM MEMO_TABLE WHERE uuid = '"+ idStr +"'");
                } finally {
                    db.close();
                }
                // 長押しした項目を画面から削除
                memoList.remove(position);
                simpleAdapter.notifyDataSetChanged();

                // trueにすることで通常のクリックイベントを発生させない
                return true;
            }
        });

        /**
         * 新規作成するボタン処理
         */
        // idがnewButtonのボタンを取得
        Button newButton = (Button) findViewById(R.id.newButton);
        // clickイベント追加
        newButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                // CreateMemoActivityへ遷移
                Intent intent = new Intent(ListActivity.this, com.android.tatsu.simplememo.CreateMemoActivity.class);
                intent.putExtra("id", "");
                startActivity(intent);
            }
        });
    }
}

activity_list.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.android.tatsu.simplememo.ListActivity">

    <ListView
        android:id="@+id/memoList"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="9"/>

    <Button
        android:id="@+id/newButton"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="新規作成する"
        />

</LinearLayout>

CreateMemoActivity.java

public class CreateMemoActivity extends AppCompatActivity {

    // MemoOpenHelperクラスを定義
    MemoOpenHelper helper = null;
    // 新規フラグ
    boolean newFlag = false;
    // id
    String id = "";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_create_memo);

        // データベースから値を取得する
        if(helper == null){
            helper = new MemoOpenHelper(CreateMemoActivity.this);
        }


        // ListActivityからインテントを取得
        Intent intent = this.getIntent();
        // 値を取得
        id = intent.getStringExtra("id");
        // 画面に表示
        if(id.equals("")){
            // 新規作成の場合
            newFlag = true;
        }else{
            // 編集の場合 データベースから値を取得して表示
            // データベースを取得する
            SQLiteDatabase db = helper.getWritableDatabase();
            try {
                // rawQueryというSELECT専用メソッドを使用してデータを取得する
                Cursor c = db.rawQuery("select body from MEMO_TABLE where uuid = '"+ id +"'", null);
                // Cursorの先頭行があるかどうか確認
                boolean next = c.moveToFirst();
                // 取得した全ての行を取得
                while (next) {
                    // 取得したカラムの順番(0から始まる)と型を指定してデータを取得する
                    String dispBody = c.getString(0);
                    EditText body = (EditText)findViewById(R.id.body);
                    body.setText(dispBody, TextView.BufferType.NORMAL);
                    next = c.moveToNext();
                }
            } finally {
                // finallyは、tryの中で例外が発生した時でも必ず実行される
                // dbを開いたら確実にclose
                db.close();
            }
        }

        /**
         * 登録ボタン処理
         */
        // idがregisterのボタンを取得
        Button registerButton = (Button) findViewById(R.id.register);
        // clickイベント追加
        registerButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                // 入力内容を取得する
                EditText body = (EditText)findViewById(R.id.body);
                String bodyStr = body.getText().toString();

                // データベースに保存する
                SQLiteDatabase db = helper.getWritableDatabase();
                try {
                    if(newFlag){
                        // 新規作成の場合
                        // 新しくuuidを発行する
                        id = UUID.randomUUID().toString();
                        // INSERT
                        db.execSQL("insert into MEMO_TABLE(uuid, body) VALUES('"+ id +"', '"+ bodyStr +"')");
                    }else{
                        // UPDATE
                        db.execSQL("update MEMO_TABLE set body = '"+ bodyStr +"' where uuid = '"+id+"'");
                    }
                } finally {
                    // finallyは、tryの中で例外が発生した時でも必ず実行される
                    // dbを開いたら確実にclose
                    db.close();
                }
                // 保存後に一覧へ戻る
                Intent intent = new Intent(CreateMemoActivity.this, com.android.tatsu.simplememo.ListActivity.class);
                startActivity(intent);
            }
        });


        /**
         * 戻るボタン処理
         */
        // idがbackのボタンを取得
        Button backButton = (Button) findViewById(R.id.back);
        // clickイベント追加
        backButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                // 保存せずに一覧へ戻る
                finish();
            }
        });
    }
}

activity_create_memo.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.android.tatsu.simplememo.CreateMemoActivity">

    <EditText
        android:id="@+id/body"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:inputType="textMultiLine"
        android:lines="50"
        android:layout_weight="9"
        android:gravity="top|left"
        />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_weight="1">

        <Button
            android:id="@+id/register"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="登録する"
            />

        <Button
            android:id="@+id/back"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="戻る"
            />

    </LinearLayout>


</LinearLayout>

MemoOpenHelper.java

public class MemoOpenHelper extends SQLiteOpenHelper {

    // データベース名
    static final private String DBName = "MEMO_DB";
    // データベースのバージョン(2,3と挙げていくとonUpgradeメソッドが実行される)
    static final private int VERSION = 1;

    // コンストラクタ 以下のように呼ぶこと
    public MemoOpenHelper(Context context){
        super(context, DBName, null, VERSION);
    }

    // データベースが作成された時に実行される処理
    // データベースはアプリを開いた時に存在しなかったら作成され、すでに存在していれば何もしない
    @Override
    public void onCreate(SQLiteDatabase db) {
        /**
         * テーブルを作成する
         * execSQLメソッドにCREATET TABLE命令を文字列として渡すことで実行される
         * 引数で指定されているものの意味は以下の通り
         * 引数1 ・・・ id:列名 , INTEGER:数値型 , PRIMATY KEY:テーブル内の行で重複無し , AUTOINCREMENT:1から順番に振っていく
         * 引数2 ・・・ uuid:列名 , TEXT:文字列型
         * 引数3 ・・・ body:列名 , TEXT:文字列型
         */
        db.execSQL("CREATE TABLE MEMO_TABLE (" +
                "id INTEGER PRIMARY KEY AUTOINCREMENT, " +
                "uuid TEXT, " +
                "body TEXT)");

    }

    // データベースをバージョンアップした時に実行される処理
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        /**
         * テーブルを削除する
         */
        db.execSQL("DROP TABLE IF EXISTS MEMO_TABLE");

        // 新しくテーブルを作成する
        onCreate(db);
    }

}

まとめ

いかがでしたか?

今回をもちまして、メモ帳を作ろう!シリーズは完結となります。

本ブログの記事を見て、これからAndroidアプリを作ろうと思っている方のお役にたてれば幸いです!

では!

[Android Studio] メモ帳を作ろう!(リファクタリング)

2018.09.02

[Android Studio] 実機でAndroidアプリを起動する

2018.08.15

4 件のコメント

  • スマホでアプリプログラムに興味穂持ち入門中です
    上から下に流すだけのスクリプト的なやつは、書いたことが有るのですが敷居が高いですね
    因みにidを日時で作成編集表示出来るようにと苦戦しています。。てか。。発想だけの状態ですが(笑)

  • こんにちは先月はteratailのほうで間違った使い方をしてご迷惑をおかけして申し訳けございません。
    ひとつだけ質問があるのですがcreatememoActivityとは別で音声入力機能を搭載したアプリを追加して作っております。そこで質問なのですがcreateActivityのように登録するをクリックするとlistActivityに戻って保存されてるようにするにはどのコードを使えばいいのでしょうか?もしお時間があるときでいいので答えてくださるとありがたいです。

    • こんにちは。
      メモ帳アプリで使われているコードの中からという意味でしたら、CreateMemoActivityの「登録ボタン処理」というコメントがついている部分が該当箇所かと思われます。

  • コメントを残す

    メールアドレスが公開されることはありません。 * が付いている欄は必須項目です