ListViewを使うとなると、行のコンポーネントをカスタマイズしたいところ。 iPhoneで言うと、UITableCellView、Flexで言うと(ry
Android Adapter & ListView & ExpandableListView
想像していたよりやっかいでした。 http://hoge/api/person/list にアクセスして人名リストデータをJSONで取得し、ListViewに人名と画像を表示するサンプル。 (サンプル用にオブジェクト名を変えているのでコンパイルエラーしているかも) ※画像はプロジェクトに埋め込み
layout/person_row.xml
src/xxxx/PersonList.java [java] public class PersonList extends ListActivity { DefaultHttpClient httpClient;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initHttp();
loadPersons();
}
static class ViewHolder {
TextView nameTextView;
ImageView imageView1;
}
class Person {
String name;
String no;
public Person() {
}
}
public int personImageId(int no) {
switch (no) {
case 1:
return R.drawable.image1;
case 2:
return R.drawable.image2;
case 3:
return R.drawable.image2;
}
return 0;
}
public class MyAdapter extends ArrayAdapter<Person> {
private LayoutInflater inflater;
private int layoutId;
public MyAdapter(Context context, int layoutId, List<Person> objects) {
super(context, 0, objects);
this.inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
this.layoutId = layoutId;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
holder = new ViewHolder();
convertView = inflater.inflate(layoutId, parent, false);
holder.nameTextView = (TextView) convertView.findViewById(R.id.nameTextView);
holder.imageView1 = (ImageView) convertView.findViewById(R.id.imageView1);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
if(!isEnabled(position)) {
convertView.setBackgroundColor(Color.BLACK);
} else {
Person person = getItem(position);
holder.nameTextView.setText(person.name);
int resourceId1 = personImageId(Integer.parseInt(person.no));
if (resourceId1 > 0) holder.imageView1.setImageResource(resourceId1);
}
return convertView;
}
}
public void initHttp() {
//スキーマ登録
SchemeRegistry schReg = new SchemeRegistry();
schReg.register(
new Scheme(
HttpHost.DEFAULT_SCHEME_NAME,
PlainSocketFactory.getSocketFactory(),
80
)
);
//HTTPパラメータ設定
HttpParams httpParams;
httpParams= new BasicHttpParams();
HttpProtocolParams.setVersion(httpParams, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(httpParams, HTTP.UTF_8);
//HTTPクライアント生成
httpClient = new DefaultHttpClient(
new ThreadSafeClientConnManager(httpParams, schReg),
httpParams
);
}
public void loadPersons() {
Uri.Builder uriBuilder = new Uri.Builder();
uriBuilder.scheme("http");
uriBuilder.authority("hoge");
uriBuilder.path("/api/person/list");
String uri = uriBuilder.toString();
HttpUriRequest httpRequest = new HttpGet(uri);
//HTTP Request送信
HttpResponse response = null;
try{
response = httpClient.execute(httpRequest);
} catch(Exception e) {
return;
}
// レスポンスコードを確認
if(response.getStatusLine().getStatusCode() != HttpStatus.SC_OK){
AlertDialog.Builder diag = new AlertDialog.Builder(PersonList.this);
diag.setTitle("ネットワークエラー");
diag.setMessage("サーバに接続できませんでした");
// ダイアログに表示するボタンの定義
DialogInterface.OnClickListener listner = new DialogInterface.OnClickListener(){
public void onClick(DialogInterface dialog, int which) {
setResult(RESULT_OK);
}
};
diag.setPositiveButton("OK",listner);
diag.create();
diag.show();
return;
}
//JSON取得
StringBuilder json = new StringBuilder();
try{
HttpEntity entity = response.getEntity();
InputStream input = entity.getContent();
InputStreamReader reader = new InputStreamReader(input);
BufferedReader bufReader = new BufferedReader(reader);
String line;
while((line = bufReader.readLine()) != null) {
json.append(line);
}
} catch(IOException e) {
return;
}
items = new ArrayList<Person>();
// JSON解析
try{
JSONObject jsonRoot = new JSONObject(json.toString());
JSONArray jsonAreas = jsonRoot.getJSONArray("person");
int i;
for(i = 0; i < jsonAreas.length(); i++){
JSONObject jsonRslt = jsonAreas.getJSONObject(i);
Person person = new Person();
person.name = jsonRslt.getString("name");
person.image1 = jsonRslt.getString("image1");
items.add(person);
}
MyAdapter adapter = new MyAdapter(this, R.layout.person_row, items);
setListAdapter(adapter);
} catch(JSONException e) {
return;
}
}
[/java]
ポイントやハマったところ。
普通にActivityを使ってもできますが、手続きを多少簡略化できるListActivityを使いました。 Activityの場合、ListViewレイアウトを作ったり、Adapterのセットの仕方が異なってくるかと。
2011/08/11 追記 ListActivity を継承しない場合は、 [java] .... MyAdapter adapter = new MyAdapter(this, R.layout.list_row, values); (ListView) listView = (ListView) findViewById(R.id.listView); listView.setAdapter(adapter); [/java] のように、setAdapter() メソッドで Adapter を直接指定します。 ListActivity は1画面1リスト(だと思う)なので、複数リストを利用したい場合は、このやり方でよいかと。
JSONデータ取得&Listデータ生成後、ArrayAdapterを利用してListViewにバインドしてやります。 [java] MyAdapter adapter = new MyAdapter(this, R.layout.person_row, items); setListAdapter(adapter); [/java] 2番目の引数が、行レイアウトになります。
リストを描画しようとする度に呼ばれます(多分)。 iOSで言うと、TableView:cellForRowAtIndexPath にあたる部分と思ってよいかも。
Androidでは何やらif文で分岐してまどろっこしい書き方だけど、 既に行のコンポーネントが作成されていれば再利用します。
つまりは、ListViewの行のキャッシュ処理
行のキャッシュは、静的クラス(ViewHolder)として定義しています。
positionは行のインデックスにあたり、データであるList
これはどうにかして欲しいかも。 ちょっと、画面を作る気にならないほど面倒くさいです。
と言う訳で、リスト表示に関してはコーディング的にもツール的にAppleの方が軍配が上がるかな?