menu
書いてる野郎
orebike@gmail.com
2010-02-25 Android SDK1.6
ものをズラズラ並べる時に使うViewでほかの〜〜〜Layout系と違うところはデータ構造をAdapterという形で内包できるということ。
あとStyleにdividerという区切り線が指定できるというなんだかなな機能も使える。
別にLinearLayoutでも無理すれば同じようなものを実装できる
とりあえずlayoutのXMLにListViewを作っておく
<ListView android:layout_width="fill_parent" android:layout_height="300dip" android:id="@+id/hoge_list_view"/>
ListViewにはスクロール機能もあって高さを縛っておくとあふれた場合に自動的にスクロールするような動きができる。
ListViewの特徴として1行ずつの制御ができるというのがある。なので1行分layoutを分離して記述することが効率的だな。このlayoutはListViewが張り付いているlayoutとは別物、さらに行ごとにも別物として管理される。実体の設計図というよりもコピー用の雛形のような位置づけになる。
なのでidとかはこのlayout内でかぶってなければ別に問題は出ない。
ということでこんな感じに hoge_row.xml を作る
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="horizontal"> <ImageView android:id="@+id/hoge_image" android:padding="3dip" android:layout_width="80dip" android:layout_height="80dip" /> <TextView android:id="@+id/hoge_text" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <Button android:id="@+id/hoge_button" android:text="@string/hoge_label" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
このListViewというものはAdapterというものを介して表示を変化させる設計になっている。他の〜Layoutのように直接オブジェクトの下にぶら下げたりしない
なのでこのAdapterをまず実装する
AdapterはArrayAdapterクラスを継承して使う。ここで型を指定しているが、この型(今だとHogeクラス)はリスト1行分のデータを生成するために必要なデータを詰めるクラスだ。
オリジナルのクラスでもいいし、文字列しか出さないならStringでもOK
public class HogeAdapter extends ArrayAdapter<Hoge>{ private List<Hoge> items; private LayoutInflater inflater; //コンストラクタ //特に深い意味は無い気がする・・ public HogeAdapter(Context context, int resourceId, List<Hoge> items) { super(context, resourceId, items); this.items = items; this.inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); } //1行分の処理 //とにかく何をするにしてもこのgetViewというメソッドが1行操作するごとに呼ばれるので //追加処理とかもここに押し込む @Override public View getView(int position, View convertView, ViewGroup parent){ //操作対象のViewを見る //完全に新規に作る場合はnullがわたってくる //それにしてもViewを引数にとっているのにgetViewとは・・・なんだか変な話だ View v = convertView; if(v == null){ //1行分layoutからViewの塊を生成 v = inflater.inflate(R.layout.hoge_row, null); } //itemsからデータ //vから画面にくっついているViewを取り出して値をマッピングする Hoge hoge = (Hoge)items.get(position); final Hoge fHoge = hoge; TextView hogeText = (TextView)v.findViewById(R.id.hoge_text); hogeText.setText(hoge.getPiyo()); ImageView hogeImage = (ImageView)v.findViewById(R.id.hoge_image); hogeImage.setImageBitmap(hoge.getPiyoBitmap()); Button hogeButton = (Button) v.findViewById(R.id.hoge_button); hogeButton.setOnClickListener(new OnClickListener() { public void onClick(View v) { //final宣言したローカル変数でlistenr生成時の値でパラメータを固定化する //クロージャモドキ実装 doHoge(fHoge); } }); return v; } }
List<Hoge> dataList = new ArrayList<Hoge>(); dataList.add(new Hoge()); dataList.add(new Hoge()); dataList.add(new Hoge()); HogeAdapter hogeAdapter = new HogeAdapter(this, R.layout.hoge_row, dataList); ListView hogeListView = (ListView)this.findViewById(R.id.hoge_list_view); hogeListView.setAdapter(hogeAdapter);
このようにしてさっき作ったHogeAdapterを作ってそこにデータのListを渡してやり、それをsetAdapterでListViewに登録することで完了する。
setAdapterの内部ではデータごとにgetViewが呼び出されて先ほどのパラメータのマッピングが行われる仕掛けになっている。
以後、このAdapterを介してデータを追加したり消したりする。
getViewはListViewの内容が(スクロールするなどして)可視範囲に入ってきたときに都度呼び出さされる。あんまり正確に挙動のチェックをしているわけではないか、内部では表示部に使われている1行1行のオブジェクトを再利用しているように見える。
なのでViewにデータを貼り付けてコントロールすると、1画面分のデータでループしてしまうことになる。
でも基本Viewインスタンスの雛部分は共有できるので、1画面分のオブジェクトだけnewすればあとはそれを使いまわせば若干のパフォーマンスアップになる。
つまり↑の
inflater.inflate(R.layout.hoge_row, null);
を画面に表示できる行数分しか呼び出さないということ。 そして中身は毎回詰め込みなおすFlyweightパターンの動きに沿うように作る。
結局今回作ったhoge_row.xmlは貼り付けられる画面に非常に依存性が高い+各種データの参照が欲しい場合が多いのでその画面のActivityクラスのインナークラスとして実装しておいたほうが何かと都合がよい
参考→内部クラス