android開発のリストビューに関するTIPSをまとめました。
ListViewの使い方
- 業務アプリによるListViewの使い方のポイント
- 基本的に表示するだけの一覧表示するだけの機能とする
- 詳細データの操作は別の画面で処理を行う。
- 外部から行のビューを取り出して特定な行の表示を書き換えるような処理は行わない。1行におけるデータの描画は基本的にはアダプタのgetViewで行うようにする。行のビューを取り出して画面の書替えを行う事は、対象行の画面書替えが発生した時に、画面に再作成で対象行の画面書替えが出来る事を考慮する必要がある。複数の行を書き換える(変更)する場合も、画面の再作成で画面書替え状態で画面の復元ができる事を考慮する必要がある。
- 外部から行のビューを取り出して、行のビュー(画面情報)から処理するデータ(例えば、担当者一覧のListViewから行のビューを取り出して、行ビューから画面に張り付いたTextViewオブジェクトから取得する担当者コード)を取り出して、処理内容に使う事はしない。
- 外部から行のビューを取り出して処理するような処理は行わない。基本的に行表示はアダプタのgetViewで完結できるようにする。外部から行のビューを取り出す場合、画面の再作成後には対象行が表示されない状態で行のビューの取り出した場合はNULLになる場合を考慮する。getViewが実行された後の行については、外部から行のビューが取り出せるが、画面の再作成が実行された場合は、当該positionはバンドルで保存されて復活できても、画面の表示がされない場合があるため(例えば、バンドルで復活したpositionはリストの下の方にある場合ばど)行ビューはNULLで返ってしまう。
- 非同期タスクが外部から行のビューを取り出して処理するような事は行わない。例)ListViewの行クリックで非同期タスクが起動し、非同期タスクの終了処理で処理結果を行ビューに表示するような場合。画面の回転等で再作成が行われ、非同期タスクの終了処理中とリストビューの表示処理(getViewのコール)が同じ時、行のビューを取り出してもNULLになる場合がある。
- 画面一覧表示は最初から画面の再作成を考慮して設計する
- 画面の再作成で行選択状態を戻す事は難しい
- アダプタのgetViewがコールされるタイミングに注意する
ListViewの表示処理について
ListViewの一覧表示処理を行う場合、ArrayAdapter等のアダプタを使用する場合が多いと思います。何も表示されていない状態から単純に一覧表示を行う場合は特に問題ないと思いますが、表示内容からタップ動作などで何かしらの処理を行い、処理結果で表示内容を更新する場合は注意が必要です(例えば、タップ行に対して処理を行い、処理完了を示すチェック表示する場合など)。リストビューのgetChildAt(position)メソッドでposition行のサブビューを取り出して、そこれからfindViewByIdメソッドでウェジットオブジェクトを取り出して表示内容を直接的に変更する事も出来ますが、アダプタのデータは変更されていないので、大きなスクロールが発生した場合などによるアダプタのgetViewの再呼出しが発生した場合、当該行は最初の一覧表示後の状態に戻る可能性があります。当該行のgetViewの呼出しタイミングは基本的にシステム側で決定されるもなので、いつコールされても良い状態にしておく必要があります。MVCの考えてに基づき、最初にアダプタのデータを変更させてから、getViewメソッドをシステム側にコールしてもらうような処理にする必要があります。
よって、ListViewの表示処理は、(1)アダプタのデータ設定(追加 or 変更(更新))をしてから、(2)アダプタのgetViewメソッドをシステムからコールしてもらうような処理動作の繰り返しになります。
(1)アダプタのデータ設定(追加 or 変更(更新))は、リストビューのsetAdapter、getAdapter()で紐付けられるアダプタのデータ操作を行う事で対応できます。
(2)アダプタのgetViewメソッドをシステムからコールしてもらうような処理は、リストビューのinvalidateViews()メソッドやアダプタのnotifyDataSetChanged()メソッドをコールする事になります。これらはリストビュー全体の更新になりますので、1行だけを更新したい場合は、以下の処理で対応できます。
1 2 3 4 5 |
// 更新対象のViewを取得 View targetView = ListView.getChildAt(positionA); // getViewで対象のViewを更新 ListView.getAdapter().getView(positionB, targetView, ListView); |
上記の注意点としては、positionAとpositionBの意味合いが異なる事です。getChildAtメソッドのpositionAは、現在、リストビューに画面表示されているフレーム枠の中で上部からのインデクッスです。positionBはアダプタに登録されてるデータに対するインデクッスです。よって、最上部付近を表示している場合はpositionA=positionBの意味合いになりますが、スクロールして中下部を表示するとpositionA≠positionBの意味合いになります。
アダプタのgetViewがコールされるタイミング
リストビューやスピナーの表示処理でアダプタを使う事が多いと思います。リストビューの表示処理はアダプタのgetViewメソッドにコーディングされますが、getViewメソッドがコールされるタイミングはonPauseイベント処理後の画面表示の寸前のタイミングになります。よって、画面起動時の初期表示処理においてonPauseイベント以前にgetViewメソッドがコールされる事を期待するような処理は問題が発生します。また、getViewメソッドは画面に表示している部分の位置でコールされますので、アダプタに登録した全てのデータ数分のgetViewメソッドが一度にコールされる事が期待できません。
ArrayAdapter使用の注意
データ無しのためにArrayAdapterを初期化するためnewする時、第3パラメータのListオブジェクトをnullにすると、システム側の表示処理で落ちてしまいます。Listオブジェクトをインスタンス化してアイテム0のオブジェクトとして渡す必要があります。
1 2 |
ArrayAdapter<String> adapater = new ArrayAdapter<String>(context, 0, new ArrayList<String>()); |