Notepad Tutorial のメモ
和訳はしません。要点のみ。別途フォローなエントリ入れます。
Ex.1
- 簡易なノート構築
- ノート追加ができるのみで編集不可
- 以下をナニ
- ListActivities の基礎とメニューオプションの生成とハンドル
- SQLite DB にノートを store するための方法
- SimpleCursorAdapter 使って ListView とカーソルを bind する方法
- 以下を含む画面レイアウトの基礎
- list view のレイアウトの方法
- activity へどうやって item を追加するか
- activity がメニュー選択をどうハンドルするか
Step 1
用意されたプロトタイプ使ってプロジェクトを作成
1. File -> New -> Android Project 選択
2. Create project from existing source チェック
3. Browse クリックで NotepadCodeLab の適切なナニを選択
4. 自動でプロパティはチェックされてる状態のはず
ビルドターゲットは lowest version 推奨
5. finish クリックで終了
AndroidManifest.xml でエラーが出る場合の対処もある
Step 2
NotesDBAdapter クラスについて
- このクラスは SQLite DB へのアクセスを隠蔽するナニ
- 先頭で定数の定義あり
- DB のフィールドを参照するために使う
- DB は data という名前
- notes というテーブルがある
- notes テーブルのフィールドは _id, title, body
- _id は検索、更新時に key として使う
- 他はデータが格納されるテキストフィールド
- SQLiteOpenHelper については調べる
- http://developer.android.com/reference/android/database/sqlite/SQLiteOpenHelper.html
- SQLiteOpenHelper でググッたらてっぺん
- コンストラクタは Android OS とこのクラスがやりとりするための
Context を引数として受け取る
-
- オブジェクトの属性に格納するのみ
- open メソドは DatabaseHelper のインスタンス作る
- DatabaseHelper は SQLiteOpenHelper のローカル実装 (内部クラス
- close メソドは DB 接続に関するリソースを解放して DB クローズ
- createNote メソドは新しい note の title と body な文字列で
DB にノートを作る
-
- 新たなノートの作成に成功したらその _id を戻す
- deleteNote メソドは特定の note の ID を受け取り、DB から delete する
- fetchAllNotes メソドは note 全部のカーソルを戻す
- query メソドの引数について
- 第一引数はテーブル名
- 第二引数は戻して欲しい列リスト (文字列の配列
- 残りのフィールドは順に
- selection
- selectionArgs
- groupBy
- having
- orderBy
- これらは全部 null
- 全部欲しくてグループで集約されてなくて良くて並びかえも不要
- 詳細は下記
- http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html
- android sqlitedatabase でググる (リンクあり
- カーソルに関するフォロー必要?
- collection
- fetchNote メソドは fetchAllNotes メソドに似ている
- がしかし、rowId な単一のノートを取得する
- 先のとちょっと違う query メソドを使っている
- 第一引数は単一の検索結果を期待している旨
- 第四引数は検索キーの指定
- 戻りは一つの列が格納されてるカーソル
- 最後に updateNotes メソドは rowId と title と body を受け取って更新
- ContentValues インスタンスを使って更新
- http://developer.android.com/reference/android/content/ContentValues.html
- リンクあり
- query メソドの引数について
Step 3
res/layout/notepad_list.xml を開いて中身チェキ
Step 4
書いてある通りに修正
- ListView と TextView タグにあるid 文字列の @ なシンボルは
- XML パーサのパース対象
- id 文字列の残りを拡張して ID リソースとして使う
- ListView と TextView は二つあるけど一つしか表示されん
- ListView は notes が存在する場合に使われる
- TextView は表示する notes が存在しない場合に使われる
- "No Notes Yet!" って文字列は res/values/strings.xml にて定義
- list および empty な ID は Android プラットフォームによって提供される
- ので id の prefix に android: を付ける必要あり
- empty な View は ListAdapter が ListView なデータを持たない時に自動でナニ
Step 5
ListView の中の notes のリストを作るためにそれぞれの行のための
View も定義する必要がある
- rss/layout/notes_row.xml を新規作成
- 例示されてるナニをコピペ
- それぞれの note のタイトル行のために使われる View
- 一つのテキストフィールドのみを持つ
- ここでは text1 という名前の新しい id を追加
- @ の後の + は自動増分なナニを示す
- ファイル保存
- R.java を開いて notes_row とか text1 が追加されてるのを確認
- 自動生成される点
Step 6
次に、Notepadv1 クラスのソースを開きます。
以降のステップでは
- list adpter にして
- notes を表示させ
- 新規作成も可能に
していきます
- Notepadv1 は ListActivity という名前の Activity のサブクラスを継承
- リスト操作の例が書いてあるが略
- Notepadv1 の既存のソースを見なさい
- 未使用の mNoteNumber という属性がある
- 番号付きの note のタイトルを作るために使われる
- 未使用の mNoteNumber という属性がある
- 三つの override なメソドが定義されている
- onCreate
- onCreateOptionsMenu
- onOptionsItemSelected
- 以下の機能を盛り込む必要あり
- onCreate は activity がスタートした時に呼びだされる
- Activity のための main メソドにちょっとだけ似ている件
- 実行開始した時にリソースと状態を設定するのに使う
- onCreateOptionsMenu は Activity に menu を入れるのに使う
- ユーザが menu ボタンをタップした時に出てくる
- 選択可能なリストオプションを持つ
- onOptionsItemSelected は menu から生成されたイベントをハンドルするのにナニ
- onCreate は activity がスタートした時に呼びだされる
Step 7
Notepadv1 を Activity ではなく ListActivity を継承するよう変更
- ListActivity を import させるには ctrl+shift+o (win/linux)
- Mac の場合には cmd+shift+o でナニ
Step 8
onCreate メソドの中身を埋める
XML で作った notepad_list レイアウトを使って Activity にタイトルをセットして
notes なデータにアクセスする NotesDbAdapter をセットアップして
有効な note タイトルをリストにナニ
- super クラスの onCreate に savedInstanceState を渡す
- savedInstanceState とか画面の状態遷移についてフォロー必要
- setContentView メソドを呼び出して R.layout.notepad_list を渡す
- クラス定義の先頭に private なクラス属性を追加
- 変数名は mDbHelper で NotesDbAdapter の参照
- mDbHelper 属性に NotesDbAdapter なインスタンスを生成してナニ
- open メソド呼び出し
- データベースを open するために mDbAdapter インスタンスのメソド呼び出し
- 最後に fillData メソドを呼び出す
- データ取得して ListView にナニ (ヘルパ使う
- このメソドはまだ定義してない
Step 9
onCreateOptionsMenu メソドの中身を埋める
menu ボタンのタッチで表示される Add Item ボタンを作る
- res/values/strings.xml に新規に文字列を追加
- menu_insert という名前
- 保存して Notepadv1 に戻る
- menu position な定数をクラス定義のアタマにナニ
- onCreateOptionsMenu メソドにおいて super クラスのメソドを呼ぶ
- 戻り値を変数 result に格納
- 最後にこれを戻す
- menu.add で item 追加
- add に渡す引数は
- このメニューのグループ識別子 (このケイスでは無し
- 上で定義済みのユニークな ID
- 順番 (ゼロだとスルー
- この item で使うリソース文字列
- add に渡す引数は
Step 10
onOptionsItemSelect メソドの中身を埋める
- 追加した Add Note な menu item をハンドルするナニ
- menu item が選択された時、item.getId は INSERT_ID を戻す
- これを検知した場合、以下な手続きを行なう
- メソド末端で super.onOptionsItemSelected を呼び出す
- 戻り値を返却
- 先に自分が検知したいイベントの処理を行なうこと
- item.getItemId() の戻りで switch する
- INSERT_ID なケイスで createNote() メソドを呼び出して true を戻す
- メソド末端で super.onOptionsItemSelected を呼び出す
Step 11
createNote メソドの追加
- このアプリの最初のバージョンではcreateNote メソドはさほど便利ではない
- Note1, Note2 みたいなカウンタな名前のノートしか追加できない
- ノートの中身も編集できません
- そのうちなんとかなるはず
- "Note" という文字列で名前を生成 (noteName
- mNoteNumber を使ってナニ
- mDbHelper.createNote メソドを呼び出し noteName と空文字列を渡す
- fillData メソドを呼び出して notes なリストに入れる
- このメソドは今から作る
Step 12
fillData メソドを定義する
- このメソドは SimpleCursorAdapter を使う
- カーソルとレイアウトにあるフィールドをつなぐ
- フィールドはリストの行要素として定義されて
- DB からのエントリとしてリストに簡易に表示できる
- カーソルからもどる title フィールドと TextView の text1 のマッピングのために
- 二つの配列を定義
- 最初のは文字列の配列でマップ元の DB の列となる
- 次に int の配列でレイアウトのリソース ID
- 二つの配列を定義
- ここで何をしたかというと
- mDbHelper.fetchAllNotes から戻ったカーソルで
- Activity のメソド startManagingCursor を使う
- 次にテーブルの列名な文字列の配列を作る
- 関連する int なリソースの配列もナニ
- 次に SimpleCursorAdapter をナニ
- SimpleCursorAdapter は Context 必要 (なので this 渡している
- 他にデータを配置する notes_row とかカーソルとか生成した配列を渡す
- mDbHelper.fetchAllNotes から戻ったカーソルで
- 今後にむけて from な列と to なリソース間の mapping 二つの配列のそれぞれの
順序でしている事を覚えておくこと
-
- bind 対象が増えたときに色々ナニ
- 二つの列を一つの行に表示したいときなど
- コンパイルエラーが出るはずなので import を {ctrl, cmd}-shift-o で調整
Ex.2
- この課題では二番目の Activity を notepad アプリに追加する。
- ユーザは note の作成と編集ができる
- ユーザは context menu から note の削除もできる
- 新しい Activity は正しいユーザからの入力による note の生成な責任は無いと見る
- intent により戻される Bundle にソレを packing (??)
- この課題は以下をナニ
- 新たな Activity を生成して Android Manifest にそれを追加
- startActivityForResult を使って非同期に他の Activity を呼び出す
- Bundle objects な Activity 間でのデータのやりとり
- さらにアレなレイアウトの使い方
- context menu の作り方
Step 1
- 新規プロジェクトを NotepadCodeLab から作って名前を Notepadv2 に
- 最初の課題と同様の方法
- なんらかのエラーが出たら AndroidTools->FixProjectProperties でナニ
- Notepadv2 プロジェクトを開いて以下
- res/values/strings.xml を開いてみる
- いくつかの新たな文字列がナニ
- Notepadv2 クラスも開いてみること
- いくつかの新たな定数が mNotesCursor あたりに定義されている
- カーソル制御云々でナニ
- いくつかの新たな定数が mNotesCursor あたりに定義されている
- fillData メソドもいくつかのコメントやらフィールドがナニ
- 二つの新たなオーバーライドなメソド定義なペアもある
- onCreateContextMenu, onContextItemSelected
- onListItemClick, onActivityResult
- これらは以下にて別途中身を埋めます
- res/values/strings.xml を開いてみる
Step 2
- 最初に個々の notes の削除をユーザに許可する context menu を作成
- Notepadv2 クラスを開く
- ListView のそれぞれの list item 順に context menu に登録
- registerForContextMenu を呼び、ListView を渡す
- onCreate の末端に例示されたナニを追加
- notes アプリの Activity は ListActivity を継承してるので
- getListView はこの中の ListView を戻す
- onCreateContextMenu メソドを埋める
- この callback は option menu な callback と似ている
- menu.add 追加で OK
- onClickContextMenu はメニューに追加された他の情報も渡す形で callback される
- オブジェクトが選択された事に関する追加情報とか?
- がしかし、ここではスルー
- context menu から一つしか選択しないし
- 次のステップでは menu item 選択なハンドルをナニ
- context menu は "long click" で出てくる模様
Step 3
で、context menu な ListView を登録して
- menu item も定義した
- それが選択された時に callback をハンドルする必要あり
- わしらは選択された item の ID を識別してそれを消さんとナニ
- onContextItemSelected の中身を埋める
- ここで getMenuInfo な AdapterCOntextMenuInfo で探してます
- この object の id フィールドで ListView の item の位置がわかる
- これを deleteNote に渡したら NotesDbAdapter が消してくれます
- ちゃんちゃん
Step 4
- NoteEdit クラスで新たな note を生成するインテントを生成
- Intent の着火には startActivityForResult メソドを使う
- この書き方は記述されたクラスをターゲットに Intent を呼び出す形式
- ここでの例は NoteEdit クラス
- Intent が route request を Android にナニする時には
- this って Context を使わないと云々 (??
- startActivityForResult メソドは Intent をナニ
- 生成する Activity が終わった時に呼び出されるメソドがソレ
- 終了な callback は onActivityResult という名前
- これは別途実装
- 他の方法としては startActivity メソドを使う方法あり
- がしかし、これは_打ちっぱなし_なナニ
- startActivity で呼び出された Activity からは終了情報を戻すことはできん
- NoteEdit は現時点でまだないが、別途実装
Step 5
onListItemClick を埋める
- onListItemClick は override した callback
- リストから item を選択した時に呼ばれる
- 4 つのパラメータが渡される
- ListView オブジェクトはその呼び出し元
- View は クリックされた ListView の中のナニ
- position はクリックされたリスト内の位置
- mRowId はクリックされた ID
- このインスタンスの中では最初の二つのパラメータは無視できる
- and we ignore the mRowId as well (??)
- ユーザが選択した位置にのみ着目すれば良い
- 行からデータを取り出すのにこれ (position) を使う
- NoteEdit Activity に bundle する
- callback の実装において note の編集に NoteEdit を使って Intent を生成
- Intent の拡張 Bundle としてデータを追加
- 呼び出される Activity に渡される
- title と body なテキストを渡すのにこれを使う
- putExtra は intent 呼び出しに渡される拡張 Bundle に item 追加なナニ
- 編集したい note の id, title, body を渡すのに Bundle を使う
- カーソルから note の詳細を取り出す
- 選択されたリストから moveToPosition メソドを使って取り出し位置をナニ
- getColumnIndexOrThrow もアレ
- extras が追加されたインテントと共に NoteEdit な intent を呼び出す
- intent とリクエストコードを渡して startActivityForResult をナニ
- リクエストコードは onActivityResult にて requestCode で取得
- mNotesCursor をローカル変数に代入してるのは最適化な模様
- Dalvik VM からアクセスされる可能性があるとの事
- このあたり、微妙だな。。
- なので一度のみアクセスしてローカルにナニ
- 可能であればこうした最適化を心掛ける必要あり
- Dalvik VM からアクセスされる可能性があるとの事
Step 6
- 上記の createNote と onListItemClick メソドは非同期な Intent 呼び出し
- これは onActivityResult メソドを埋めて callback をハンドルする必要あり
- onActivityResult Activity が値を戻す場合に override されるメソド
- startActivityForResult でナニされてる必要がある
- この callback のパラメータは
- requestCode は Intent 呼び出し時にナニされた request code
- ここでは ACTIVITY_CREATE とか ACTIVITY_EDIT
- resultCode 呼び出しの結果で全て OK であれば zero だけど
- non-zero だったら何かが失敗したことを示す
- 自分でエラーなナニを記述できるもよう
- intent 結果を戻す Acitivity により生成された intent
- intent extra から戻された値を使える
- startActivityForResult と onActivityResult のセットは
- 非同期 RPC とか共有サービスとか他から呼び出された Activity で推奨される形式
- このメソドでは ACTIVITY_CREATE とか ACTIVITY_EDIT の両方をハンドル
- create なケイスでは title と body を extras から取り出して note にナニ
- edit なケイスでは mRowId を取り出して update
- fillData で (ry
Step 7
- note_edit.xml を開いて中身チェキ。NodeEditor の UI なコード
- 高度な UI はまだ。
- コード入力時にバグが入るのを防ぐためにファイルそのまま提供してる
- 新たなパラメータ android:layout_weight
- layout_weight は LinearLayout で使われる
- "importance" なナニとして assign
- デフォでは layout_weight は zero
- 表示に十分なスペース
- zero より大なら parent View の残りのスペースがナニ
- サンプルでは 2 つの label と 2 つの text field
- label は表示に十分なスペースのみ
- 一行に2つの weight:1 があったら均等割り
- 1 と 2 だと 1/3 と 2/3 になる模様
- 複数レイアウトを入れ子にする方法が云々
- ApiDemos のレイアウトも見てみること推奨
Step 8
- NoteEdit クラスは android.app.Activity を継承
- Activity をイチから初めて作ります
- onCreate メソドはデフォで override な形
- Activity が onCreate を override しないのはあり得んよね
- Package Explore からパケジ右クリックで New->Class
- NoteEdit と名前をナニ
- Superclass は android.app.Activity で finish
- NoteEdit クラスを右クリックで Source->Override/Implement Methods
- onCreate 選択してナニ
- OK をクリック
Step 9
- NoteEdit の onCreate を埋める
- 新しい Activity のタイトルを "Edit Note" にします
- note_edit.xml を使って content view にセットされる?
- タイトルってどこでセットされるんだっけ?
- AndroidManifest.xml
- タイトルってどこでセットされるんだっけ?
- title と body のテキスト編集 view とか confirm ボタンをハンドルを補足して
- note の title と body の set とか get ができて
- ユーザによる confirm ボタンなイベントをナニ
- Intent 呼び出し時に attach された Bundle と一緒の Activity を受けとって
- それらの値を unbundle できる
- 受けとったソレを編集できるようにナニ
- mRowId でどのソレを編集してるのかも分かってます (意味微妙
- onCreate の中でレイアウトを設定
- 必要とするコンポートネントをナニ
- R クラスで定義されてる ID で見つけて
- View の型でキャストする必要あり
- mTitleText と mBodyText はクラスの属性
- R クラスで定義されてる ID で見つけて
- クラスの先頭にて Long mRowId 属性定義 (private)
- カレントの ID 格納
- 続いて onCreate の中で titile body と mRowId の初期化
- Intent にある Bundle から値を取得 (extras が存在すれば)
- Intent 呼び出しでセットされた Bundle から title を body をナニ
- null を text field にセットしない工夫をしている
- Intent にある Bundle から値を取得 (extras が存在すれば)
- onClickListener をボタンのために作る (内部クラス?
- UI 実装をワケワカにする Listener
- なるべく簡易にナニ
- ユーザが confirm ボタンをタッチした時、onClick メソドが呼ばれて
- 編集した値を Intent 呼び出し元に戻す
- これを無名 inner クラスでナニ
- 見たことなかったら微妙だよね
- リスナーの作り方とボタンにそれを関連付けるナニ
- 例示されてるのは空のリスナ
Step 10
ラストに onClickListerner の onClick を埋める
- ユーザが confirm ボタンをタッチしたときに動作するコード
- title と body なテキストをテキストフィールドから捕捉して
-
- それらを Bundle にナニして
- NoteEdit Activity の呼び出し元に戻す
- 新規作成なら mRowId も Bundle に詰めて戻す
-
- Bundle 生成して Notepadv2 で定義されているキーとともに値を入れる
- 新たな Intent に戻す情報 (Bundle) 格納して Activity を終わる
- Intent は Bundle で値を戻す簡易なもの
- setResult は結果なコードをセットして Intent 呼び出し元にパスするナニ
- このケースでは全部の仕事が終わったら RESULT_OK を result code にナニ
- finish 呼び出しは Activity の終了な signal としてナニ
- 呼び出し元に戻る
- onCreate の定義の全部を例示
Step 11
最後に、新しく作った Activity を manifest で定義せねば
- AndroidManifest.xml ダブルクリック
- Application タブをクリック
- Application Nodes セクションにて Add をクリック
- ダイアログが出てくる模様
- "Create a new element at the top level, in Application" チェキ
- "(A)Activity" が選択されるのを確認たら OK クリック
CRUD な機能は実装
次は
- 編集中の back button のタップでエラーが起きる
- これを改修
- 編集内容の lost もなんとかする模様
Ex.3
この演習では lifecycle event callback とアプリケーションの状態をナニする
- Life-cycle event とどのようにそれらを application で使うか
- アプリケーションの状態の維持のテクニック
Step 1
- Notepadv3 を eclipse にインポートせよ。
- エラーが出たらいつものように対応
- 始点は Notepadv2 の終端
- この時点でいくつかの問題がある
- 編集中の back ボタンのタップによる crash
- 編集中に何か起きたら編集内容はロストしてしまう
- これをナニするため、NoteEdit クラスに機能を移す
- 新規作成とか編集とか?
- note 編集の lifecycle を取り込む
- NodeEdit のコードの Bundle から title、body をパースしてるナニを除去
- 代わりに DBHelper クラスを使う
- DB の note にダイレクトにアクセスする
- NoteEdit に渡す必要があるのは mRowId のみ
- 代わりに DBHelper クラスを使う
- UI から編集された title とか body のテキストをセットしている
- Bundle から渡される属性も除去
Step 2
- NotesDbAdapter な属性を NoteEdit の先頭にナニ (private
- NotesDbAdapter のインスタンスを onCreate でナニ
- supre.onCreate の下ね
- new して open する
Step 3
- NoteEdit にて mRowId のための savedInstanceState をチェックな必要あり
- Bundle から note 編集中なステイトがあった場合、それを復帰せねば
- savedInstanceState の null チェキに注意
- savedInstaceState からナニされなかったら Bundle から mRowId をナニ
Step 4
- 次に populateFields メソドがあれば mRowId なフィールドをナニする必要あり
- この命令は confirmButton.setOnClickListener() の前に入る
- このメソドはもうすぐ定義な模様
Step 5
- onClick から Bundle な値のナニと Bundle の生成を除去する件
- Activity が呼び出し元からの extra な情報の return を必要としない件
- あと、Intent からの戻りも必要ではない件
- なので、setResult も短いナニで良い件
- なんか違うぞ
- life-cycle メソドを使って新規/修正の DB への反映を注意深くしましょう
- onCreate 全体をナニ
- どこの onCreate?
- onCreate 全体をナニ
Step 6
- pupulateFields メソドを定義せよ
- このメソドは NotesDBAdapter.fetchNote メソドで編集する note を見つけるために使う
- この中では Activity クラスの startManagingCursor を呼びだす
- これはカーソルの life-cycle の世話を提供する Android の便利なメソド
- Activity の life-cycle が申告したリソースの release と re-create をナニ
- なのでわしらはそれら全部を心配する必要ナシ
- てコトは休眠とかそのあたりも保存してくれるんだ
- なので、わしらはカーソルから title とか body を lookup できる
- で、それらを View に配置できる、と
Step 7
- NoteEdit クラスでいくつかのメソドを override します
- onSaveInstanceState, onPause, onResume
- onSaveInstanceState は Activity が停止してて再開前に死ぬ、ってトキにナニ
- 具体的には他のアプリが起動とか省電力モード突入時とか
- これは Activity が再開した時に同じ状態に再設定する必要がある事を意味する
- onCreate の counterpart で実際に onSaveInstanceState な Bundle は
- onCreate い渡されるものと同じもの
- onPause とか onResume も無料 (?) のメソド
- onPause は Activity 終了時に常に呼びだされる。
- DB に戻すために現在の note を保存
- onPause の間に解放できるリリースをリリースしとくのは
- active でない状態の時にリソースを節約するために良い方法
- onResume は populateFIelds メソドを呼ぶ
- DB に書きだされた note を読んでフィールドに配置するために
- で、populateFields メソドの後ろに以下のナニを追加
Step 8
- DB に data を掃き出す saveState メソドを定義
- createNote の戻りを捕捉して ID が valid なら mRowId 保存
- 復帰後に create するより update した方がナニ
- 他の life-cycle イベント云々なからみもある?
Step 9
- Notepadv3 クラスの onActivityResult メソドから
- 前のハンドルを行なうコードを除去
- NoteEdit の life-cycle で起きる note 検索とか更新の全て
- onActivityResult で必要とされるのは画面更新のみ
- 他の働きは不要
- 他のクラスが仕事してくれてるので
- これだけで画面の更新は OK
Step 10
- onListItemClick メソドから title とか body をナニしてる行を削除
- 削除する行が出てて最後にこうなる、ってのが出てます
- クラスから mNotesCursor 属性も除去
- fillData メソドではローカル変数にします
- 接頭辞 m は属性な接頭辞
- なのでローカル変数な名前は notesCursor になる
次のステップ
Eclipse なデバッガで life-cycle イベントをナニする模様