mucchinのAndroid戦記

ListViewのカスタマイズ方法

ListViewをカスタマイズする方法は?

Androidでは、ListViewというViewがあります。
このListViewは、同じ構成のレイアウトを一覧表示していく、というViewです。
最もシンプルな使い方としては、単純にTextViewだけを配置した一覧でしょうかね。
ですが、そんな単純な使い方って、実用的でしょうか?
例えば商品名やユーザ名を一覧表示するだけでは、何も出来ません。
商品名と、その詳細情報を表示する画面へ遷移する為のボタンであるとか、ユーザー名とそのユーザへ音声発信するためのボタンとか、単にテキストだけではなくて、ボタンを一緒にセットにしたい、とか、その他のカスタマイズをしたいと考えている方、多いのではないかと思います。
ListViewの使い方は少々ややこしいですが、私自身がよく使う方法を紹介します。
一応、この方法ですと、結構汎用的に使えるのではないかと思います。


まず、ListViewの行のレイアウトを作ります。
レイアウトの作り方は、画面のレイアウトを作るときと全く同じです。
XMLを追加して、Viewを追加していく、という手順を踏みます。
この方法は以前紹介しているので、下記を参考にしてください。
Android XMLの追加方法
レイアウトXMLのEclipseでの設定方法
ここでは、サンプルとして以下のレイアウトにしてみましょう。
TextViewとButtonを配置して、Buttonは一番右側に表示されるようにする。
やり方は様々かと思いますが、例えば以下のような感じです。


<?xml version=”1.0″ encoding=”utf-8″?>
<RelativeLayout
  xmlns:android=”http://schemas.android.com/apk/res/android”
  android:layout_height=”wrap_content” android:layout_width=”fill_parent”>
<TextView android:layout_width=”wrap_content”
 android:layout_height=”wrap_content”
 android:id=”@+id/nameText”>
</TextView>
<Button android:layout_width=”wrap_content”
 android:layout_height=”wrap_content”
 android:layout_alignParentRight=”true”
 android:id=”@+id/detailButton”
 android:text=”@string/detail”>
</Button>
</RelativeLayout>


今回は「Buttonは一番右側に表示されるようにする」という要件を満たす為に、親のレイアウトはRelativeLayoutを使ってます。
RelativeLayoutのlayout_width属性をtrueにする事で、ListViewの行自体の幅を、親画面の幅一杯とします。
そして、Buttonのlayout_alignParentRightをtrueにする事で、RelativeLayoutの一番右側に配置されるようにする、という事です。
今回のテーマは、ListViewをカスタマイズするという事なので、上記はあくまで一例です。
あなたのお好みに併せて、レイアウトを作ってください。


次はメインとなるListViewを表示する画面の作成です。
まずは画面レイアウトを作りましょう。
今回は、単純に、一番上にヘッダを表示するTextViewと、その下にListViewを表示する、というだけにしましょう。
ヘッダには、タイトルとか、Listの件数とかを表示するのに使いますが、今回は単純に固定文字を表示するだけにしておきます。
ListViewを使う画面を表示するActivityは、通常のActivityではなく、ListActivityを継承させます。必ずしも必要ではないのですが、ListActivityを継承させると好都合な事があります。
それは後述しますが、とりあえず以下のXMLのようにレイアウトを作ります。


<?xml version=”1.0″ encoding=”utf-8″?>
<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
  android:orientation=”vertical”
  android:layout_width=”fill_parent”
  android:layout_height=”fill_parent”
  >
<TextView
  android:layout_width=”fill_parent”
  android:layout_height=”wrap_content”
  android:text=”@string/hello”
  />
<ListView android:layout_width=”wrap_content”
  android:layout_height=”wrap_content”
  android:id=”@+id/android:list”>
</ListView>
<TextView android:layout_width=”wrap_content”
  android:layout_height=”wrap_content”
  android:id=”@+id/android:empty”
  android:text=”@string/nodata”>
</TextView>
</LinearLayout>


LinearLayoutの中に、TextView、ListView、TextViewを配置しています。
一つ目のTextViewは、単純にアプリ名を表示するだけのTextViewです。
二つ目のListViewと三つ目のTextViewですが、1点注目して欲しいところが、android:id属性の値です。
@+id/android:list@+id/android:emptyとしています。
そもそも最後のTextViewは何かと言いますと、これはListが1件もデータが無い場合に表示される文言です。
text属性に、@string/nodataとしていますが、これはstring.xmlに、自分で定義している文字列です。
例えば「1件もありません。」のような文言を設定しておきます。
このようにする事で、ListActivityを継承させたActivityが、以下の処理を自動的に行ってくれます。


@+id/android:listで指定されているListViewが何も表示していない場合は、代わりに@+id/android:emptyで指定されたViewを表示する。


上記が、普通のActivityではなくて、ListActivityを継承させた方が好都合になる点の一つです。
画面のレイアウトの定義はここまでです。


次は、ListViewに設定する文字列などの情報を入れる箱となるクラスを用意します。
JavaBeanというものをご存知でしょうか。
ServletやJSPを勉強した事が有る方ならご存知かと思います。
厳密にJavaBeanとはいくつかルールがあるのですが、・・・
ちょっと話が違う方向に行きかけました。すみません。
単純に、一つのprivateな変数に対して、publicなセッタとゲッタのメソッドがある、というだけのクラスだと思ってください。
ここではそれで充分です。
例として、今回、ListViewには、商品名と、その商品に関するURLへ飛ばすボタンが配置されるという仕様だとします。
必要な情報は、商品名とURLです。
両方String型として、商品名とURLが格納される変数とそれぞれのセッタ、ゲッタメソッドを用意するだけのクラスを作っておきます。
例として以下のような感じです。


public class ItemBean {
  private String name = “”;
  private String url = “”;
  public void setName(String name) {
    this.name = name;
  }
  public String getName() {
    return name;
  }
  public void setUrl(String url) {
   this.url = url;
  }
  public String getUrl() {
    return url;
  }
}


このクラスのインスタンスを、ListViewへ表示するデータの一つ一つに対して生成していきます。
後でサンプルコードを掲載するので、そのときわかるかなと思います。


スポンサーリンク




次はActivityの実装です。
まず、ListActivityを継承させます。
ListActivityはActivityのサブクラスなので、onCreate等は、普通のActivityと一緒です。
このActivityでは、ListViewに表示するデータの生成と表示です。

ListViewへは、setListAdapter()というメソッドで、引数にAdapterのインスタンスを設定する事でデータが表示されます。
これも、ListActivityを継承させる事で受ける利点です。
Adapterは、Listへ表示させる1つ1つのデータのインスタンスです。
複数のAdapterを扱うために、ArrayAdaterというクラスがありますが、今回、カスタマイズするListViewを表示させる為、このArrayAdapterクラスを継承させて、Adapterクラスを自作します。
以下のクラスを、Acitvity内に実装してください。(もちろん個別のjavaファイルとしてもいいです。)


class ListAdapter extends ArrayAdapter<ItemBean>{
  private LayoutInflater mInflater;
  private TextView mTitle;
  private Button mButton;

  public ListAdapter(Context context, List<ItemBean> objects) {
    super(context, 0, objects);
    mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
  }

  public View getView(final int position, View convertView, ViewGroup parent) {
    if (convertView == null) {
      convertView = mInflater.inflate(R.layout.row, null);
    }
    final ItemBean item = this.getItem(position);
    if(item != null){
      mTitle = (TextView)convertView.findViewById(R.id.nameText);
      mTitle.setText(item.getName());
      mButton = (Button)convertView.findViewById(R.id.detailButton);
      mButton.setOnClickListener(new OnClickListener() {
        public void onClick(View v) {
          Uri uri = Uri.parse(item.getUrl());
          Intent i = new Intent(Intent.ACTION_VIEW,uri);
          startActivity(i);
        }
      });
    }
    return convertView;
  }

}


継承させているクラスは、ArrayAdapter<ItemBean>としています。
(ArrayAdapterじゃなくて、BaseAdapterでもいいはずなのですが、BaseAdapterだと、解決できない?バグと遭遇してしまったので、それ以来私はArrayAdapterを使ってます。)
このクラスを利用する側、つまりActivityは、先ほど自作したItemBeanを要素に持つArrayListを生成した後、このクラスのコンストラクタに渡してAdapterを生成して使います。
getViewメソッドは、VMから呼ばれるメソッドで、表示させるデータ1行ずつに対してコールされます。
以下の部分
final ItemBean item = this.getItem(position);
これで、このメソッドがコールされる行に関連付いているItemBeanのインスタンスを取る事ができますので、その後は好きなように処理を書いてください。
今回の例では、名前をTextViewに設定して、ボタンが押されたときは、関連付いているURLにブラウザを使って接続するという事をするように設定しています。
ちょっと今の段階ではまだよくわからないかもしれませんね。
次は、実際にこのAdapterを生成してセットする、という処理を見ていきましょう。


今回は、名前に、検索エンジンの名前を、そしてボタンを押すことでその検索エンジンのページへ遷移する、というアプリにしましょう。
onCreate()を以下のようにするだけです。


public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);

  ItemBean yahoo = new ItemBean();
  yahoo.setName(“Yahoo”);
  yahoo.setUrl(“http://www.yahoo.co.jp”);

  ItemBean google = new ItemBean();
  google.setName(“Google”);
  google.setUrl(“http://www.google.co.jp”);

  List<ItemBean> list = new ArrayList<ItemBean>();
  list.add(yahoo);
  list.add(google);

  ListAdapter adapter = new ListAdapter(getApplicationContext(),list);
  setListAdapter(adapter);
}


今回はベタ書きですが、yahooとGoogleのItemBeanインスタンスを作り、それをList<ItemBean>のインスタンスへ追加して、その後そいつを使って自作AdapterであるListAdapterを生成して、これをsetListAdapter()で渡してやる。
これで完了です。
以下のような画面が表示されます。



今回のサンプルコードは下記からダウンロードできます。
ListViewのカスタマイズ方法 サンプルコード
今回紹介した方法を理解できれば、あとは好きなようにレイアウトを作り、それに合わせたAdapterを自作して、getView()をカスタマイズするだけで、どんなレイアウトのListViewもたいがいは作る事ができるはずです。
WebサービスAPIを利用する場合等は、返ってくる情報に併せて、JavaBeanクラスを作っておいて、レスポンスを分析するところで、データ1件に対してインスタンスを生成、そしてArrayListに追加、みたいな感じにしてやれば、あとは今回紹介した方法を応用できます。
結構、汎用的に使えそうって思っていただけたら幸いです・・・。