[自分用メモ] ListView、ArrayList、ArrayAdapterの関係性がいまいち分かりにくかったのでサンプルコードで整理した

Androidアプリで、たとえばTwitterのタイムラインのようなものを画面上に出す際に使うであろう「リストビュー」を使う機会があったのでサンプルコードを参照しながらプログラムを書いていたが、ListView、ArrayList、ArrayAdapterの関係性がいまいち分かりにくく、どうやってコードを書けばいいのか分からなくなってきたので、独立したサンプルコードをスクラッチで書いて頭の中を整理してみた。

作るものは至って単純で、以下の画面のような「リストビュー」によるリスト表示。上下スクロールで別のリストが登場するというものだ。

and01and02

関係性を整理したものの、自信はない。特にArrayAdapterに関しては詳しく解説されたページや書籍に出会ったことがないので、あくまでもサンプルコードから得た概念だけで認識している有様。なんたる情けなさ……。

and03

緑の枠線全体がListViewで、これは1つだけインスタンスを生成して使用する。このサンプルではメンバ変数でクラス内全体で使用している。
青い枠線、赤い枠線がそれぞれTextViewで構成された表示部分で、これはリストの行数分だけインスタンスを生成している必要がある。逆を言うとリスト表示で必要になった場合に呼び出されるコールバック関数などで随時生成するなどで対応することになるだろう。


そしてこの2つを横並びに載せるためのコンテナUIとしてLinearLyaoutのインスタンスを生成する。これがオレンジの枠線の部分だ。
水色の枠線で青と赤をまとめているが、これはMyDataという1行分のデータ搭載用のクラスで、このサンプル例では100行分のデータを用意しているので、0~99まである。したがって、LinearLayoutもその行数分だけ用意することになる。
水色の枠はArrayListとして100個のMyDataクラスのインスタンスを抱えた配列で、これをMyAdapterクラスのインスタンスに載せる。
で、なぜArrayAdapterを継承したMyAdapterというクラスを定義して、そのインスタンスに100行分のデータを載せる必要があるかというと、ListViewがリストを表示する場合に、このMyAdapterクラスでオーバライドされたgetView()メソッドが呼び出されるからだと認識している。つまり、ListView自体はデータの表示方法を知らないし、データそのものもListViewにどのように表示させるかの手段は持ち合わせていない。これを接続させる役割がまさに「アダプター」の役割で、ListViewはgetView()を呼び出すことで(ListView側から見たら)ブラックボックスとしてのメソッドから返されるViewインスタンスを表示する処理をする。逆にデータ側はgetView()メソッドに、どのようなUIにデータを載せるかを記述するコードを書いておき、Viewインスタンスをreturnしてやることで勝手にListViewがリストに表示してくれる。この橋渡し的役割と言うか中間ポイントに相当するのがArrayAdapterクラスを継承したMyAdapterクラスということになるのだと思っている。

[Sponsored Link]


それをふまえた上で、ソースコード。

[java]
package com.example.myadapter;

import java.util.ArrayList;

import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;

public class MyAdapterActivity extends Activity {

private ListView lv;
private MyAdapter myadapter;
private ArrayList<MyData> mylist;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);

mylist = new ArrayList<MyData>();
for (int i = 0; i < 100; i++) {
mylist.add(new MyData("文字列"+i, i));
}
myadapter = new MyAdapter(this, mylist);

lv = new ListView(this);
lv.setAdapter(myadapter);
setContentView(lv);
}

private class MyAdapter extends ArrayAdapter<MyData> {

public MyAdapter(Context context, ArrayList<MyData> objects) {
super(context, 0, objects);
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
MyData md = this.getItem(position);
Context context = MyAdapterActivity.this;

if (convertView == null) {
LinearLayout ll = new LinearLayout(context);
ll.setPadding(5, 5, 5, 5);
convertView = ll;

TextView tv1 = new TextView(context);
tv1.setTextColor(Color.BLUE);
tv1.setTag("str");
tv1.setWidth(200);
ll.addView(tv1);

TextView tv2 = new TextView(context);
tv2.setTextColor(Color.RED);
tv2.setTag("num");
ll.addView(tv2);
}

TextView tv;
tv = (TextView)convertView.findViewWithTag("str");
tv.setText(md.str);
tv = (TextView)convertView.findViewWithTag("num");
tv.setText("" + md.num);

return convertView;
}
}

private class MyData {
private String str = null;
private int num = 0;

public MyData(String str, int num) {
this.str = str;
this.num = num;
}
}
}
[/java]

29行目で、まずはArrayList<mydata>クラスのインスタンスmylistを生成して、水色枠100個分を抱えるリストを用意する。
30~32行目で、for文を使って100件分の個々のデータと、それらを保存するためにのMyDataクラスのインスタンスを生成している。生成と同時にArrayListへの登録をArrayList.add()メソッドを使って行っている(31行目)。
33行目で、ArrayAdapterクラスを継承したMyAdapterクラスのインスタンスmyadapterを生成する。これこそがまさにアクティビティとデータを結びつけるための接点となる。Activityオブジェクト(this)とArrayAdapterクラスのオブジェクト(mylist)を指定する。これをListViewオブジェクトに登録することで、ListViewがどのActivityに対してどのデータをどのように表示すればいいかという「場所」と「手段」を取得できる。myadapterもメンバ変数である。

35行目はListViewクラスのインスタンスlvを生成している。lvはメンバ変数である。
そして36行目でListView.setAdapter()メソッドを使って、lvにmyadapterをセットする。つまり、33行目で設定した「場所」「手段」を持つアダプターをListViewに渡すということになる。
ListViewはその後リスト表示をする際に、このmyadapterインスタンスのgetView()メソッド(46~75行目)を呼び出すことでリスト表示のためのオブジェクトを取得し描画することになる。
37行目はActivityにListViewをContentViewとして設定している。

40~76行目はMyAdapterクラスを定義している。
42~44行目はコンストラクタで、33行目のように呼び出された場合に単純に親クラスに引数を渡して処理させている。
46~75行目が、ListViewから呼び出されるgetView()メソッドである。親クラスのメソッドをオーバライドする必要がある。そしてこのgetView()メソッドでViewインスタンスを用意しメソッドの返り値としてListViewに渡す(74行目)。

getView()メソッドは呼ばれる際に3つの引数を受け取る。順番に、位置、ビューオブジェクト、ビューグループオブジェクトとなっている。位置はリストの位置、ビューオブジェクトは対象となるリストのビューのオブジェクト。最後は不明……。
51行目のif文で、引数のViewがnullかどうかを判断している。つまり表示するViewがそもそも存在しない場合は新たに作るし、すでにあるならそのViewを使うという判断である。
52~65行目がViewがない場合の新しいLinearLayoutの生成と、その上に載せるテキスト表示用のTextViewの生成である。52、53行目でLinearLayoutの生成と設定、54行目でnullであったconvertViewにLinearLayoutのインスタンスをコピーする。つまりViewをLinearLayoutとする。
56~65行目はそれぞれTextViewを生成してLinearLayoutに載せるという作業である。

68~72行目でTextViewにそれぞれのデータを設定する作業をしている。findViewWithTag()メソッドでタグ名からLinearLayoutインスタンス上に載せたTextViewインスタンスを取得して(69、71行目)、48行目でgetItem()メソッドを使って取得したインスタンスを使ってそれぞれ保存したデータを取り出しTextViewに設定している。

78~86行目はMyDataクラスの定義である。これは特に何の継承もしておらず、メンバ変数とコンストラクタ以外にメソッドも何もない、ほとんどデータ構造だけのようなものである。

最後に31行目について。
以下のように指定しているadd()メソッドを、

[java firstline=”31″]
mylist.add(new MyData("文字列"+i, i));
[/java]

次のように挿入位置を指定することもできる。以下の例では0の位置、つまり先頭に挿入するようにしている。これにより、たとえばTwitterのタイムラインのような最新のリストを上位に差し挟んでいくということが可能になる。

[java firstline=”31″]
mylist.add(0, new MyData("文字列"+i, i));
[/java]

スッキリわかるJava入門スッキリわかるJava入門 実践編業務用 レンジ用和風竜田揚 1kg ロイヤルシェフ(ニチレイ)【冷凍】湘南貿易 デュック・ド・モンターニュ ノンアルコールワイン 750mlJava言語プログラミングレッスン 第3版(上)増補改訂版Java言語で学ぶデザインパターン入門

コメントを残す

メールアドレスが公開されることはありません。

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください