Skip to content
This repository has been archived by the owner on Jun 20, 2023. It is now read-only.

2.06. ListView と ViewPager

ketazawa edited this page Apr 30, 2013 · 24 revisions

この章では、ListView と ViewPager の2つの特殊な View について解説します。

参考:Building Layouts with an Adapter | Android Developers

参考:Making ListView Scrolling Smooth | Android Developers

目次

ListView

ListView は、縦にスクロールする一覧表示のための View です。

ListView 自身には、一覧の中身を管理する機能はありません。
代わりに、Adapter という仕組みを用いて、データソースの管理と、データの View へのバインドをさせ、ListView は、スクロール位置に合わせて必要な View を Adapter から取り出すことをします。

リストデータは、List インタフェースを実装したデータソースや、或いは、データベースへ問い合わせた結果のデータソースである場合もあります。

ListAdapter

データをバインドして表示する際には、ListAdapterインタフェースを実装したAdapterを使用します。
Adapterにはいくつかの種類が提供されています。以下はその一部となります。

名前 役割
ArrayAdapter 配列やリストデータソースをバインドする際に使用します。
SimpleCursorAdapter データベースへ問い合わせた結果をバインドする際に使用します。

ListViewを表示する

ListViewを表示するには以下の作業が必要となります。

  • ListViewをレイアウトxmlに配置
  • ListViewにAdapterをセットする
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <!-- ListViewを配置 -->
    <ListView
        android:id="@+id/ListView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mActivity = this;

        // ListViewに表示するデータを作成する
        ArrayList<String> list = new ArrayList<String>();
        for (int i = 0; i < 20; i++) {
            list.add("hoge" + i);
        }

        ListView listView = (ListView) findViewById(R.id.ListView);
        // android.R.layout.simple_list_item_1はAndroidで既に定義されているリストアイテムのレイアウトです
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(mActivity,
                android.R.layout.simple_list_item_1, list);

        listView.setAdapter(adapter);
        // タップした時の動作を定義する
        listView.setOnItemClickListener(new OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                // Adapterからタップした位置のデータを取得する
                String str = (String) parent.getItemAtPosition(position);
                Toast.makeText(mActivity, str, Toast.LENGTH_SHORT).show();
            }
        });
    }

まずはArrayAdapterのインスタンスを作成します。
ここではコンストラクタの引数にContextとリストアイテムのレイアウトIDと表示するデータをリスト形式で渡しています。
ArrayAdapterには様々なコンストラクタが用意されています。適宜、適切なものを使用すると良いでしょう。

レイアウトIDにはandroid.R.layout.simple_list_item_1を設定しています。これはAndroidで用意されているリストアイテムのレイアウトです。中にはTextViewが1つあるだけです。
表現豊かたなレイアウトを使用したい場合、自分で作成したレイアウトをリストアイテムに表示することもできます。
その方法は次項リストアイテムをカスタマイズするにて説明します。

次に、setAdapterメソッドでListViewにAdapterをセットします。
最後に、setOnItemClickListenerメソッドでリストをタップした時の動作を設定しています。
実行すると以下の様なListViewが表示されます。

listview

カスタマイズしたリストアイテムを表示する

カスタマイズしたリストアイテムを表示するには以下の作業が必要となります。

  • リストアイテムのレイアウトxmlの作成
  • 独自Adapterの作成

リストアイテムのレイアウトxmlを作成する

新規にレイアウトxmlを作成します。 リストアイテムのレイアウトは以下のように作成します。
custom_list_item_layout

custom_list_item.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="4dp" >

    <ImageView
        android:id="@+id/imageView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:src="@drawable/ic_launcher" />

    <TextView
        android:id="@+id/TitleText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:layout_toRightOf="@+id/imageView1"
        android:textSize="18sp" />

    <TextView
        android:id="@+id/SubTitleText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/TitleText"
        android:layout_alignParentRight="true"
        android:layout_below="@+id/TitleText" />

</RelativeLayout>

独自Adapterを作成します

Adapter内ではリストアイテムの生成と表示位置に対応するデータの取得、設定といった処理を行います。
CustomListItemAdapter.java

public class CustomListItemAdapter extends ArrayAdapter<String> {

    private LayoutInflater mLayoutInflater;

    public CustomListItemAdapter(Context context, List<String> objects) {
        // 第2引数はtextViewResourceIdとされていますが、カスタムリストアイテムを使用する場合は特に意識する必要のない引数です
        super(context, 0, objects);
        // レイアウト生成に使用するインフレーター
        mLayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        View view = null;

        // ListViewに表示する分のレイアウトが生成されていない場合レイアウトを作成する
        if (convertView == null) {
            // レイアウトファイルからViewを生成する
            view = mLayoutInflater.inflate(R.layout.custom_list_item, parent, false);
        } else {
            // レイアウトが存在する場合は再利用する
            view = convertView;
        }

        // リストアイテムに対応するデータを取得する
        String item = getItem(position);

        // 各Viewに表示する情報を設定
        TextView text1 = (TextView) view.findViewById(R.id.TitleText);
        text1.setText("Title:" + item);
        TextView text2 = (TextView) view.findViewById(R.id.SubTitleText);
        text2.setText("SubTitle:" + item);

        return view;
    }

}

ArrayAdapterを継承して独自のAdapterを作成しています。
getViewメソッドをオーバーライドして、戻り値に表示するリストアイテムのViewを返します。
表示している位置のデータはgetItemメソッドで取得することができます。

custom_list_item

Viewの再利用について

Androidではリストアイテムを表示する際に、既にViewが生成されていてる場合はそれを再利用します。
リストに表示するデータが50件あった場合、リストアイテムのViewを50個を生成することはしないということです。
画面を構成するのに必要な分のViewが生成され、あとは再利用するため無駄なViewを生成するといったことはありません。
どのようにViewが再利用されているかを確認するため先ほどのCustomListItemAdapterクラスのgetViewメソッドを以下のように修正します。

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        View view = null;

        // ListViewに表示する分のレイアウトが生成されていない場合レイアウトを作成する
        if (convertView == null) {
            // レイアウトファイルからViewを生成する
            view = mLayoutInflater.inflate(R.layout.custom_list_item, parent, false);
            
            // リストアイテムに対応するデータを取得する
            String item = getItem(position);

            // 各Viewに表示する情報を設定
            TextView text1 = (TextView) view.findViewById(R.id.TitleText);
            text1.setText("Title:" + item);
            TextView text2 = (TextView) view.findViewById(R.id.SubTitleText);
            text2.setText("SubTitle:" + item);

        } else {
            // レイアウトが存在する場合は再利用する
            view = convertView;
        }
        // 一度作成したレイアウトの表示データを変更しないことにより、再利用されたデータがどこに表示されるかを確認する
        return view;
    }

実行すると以下のようにViewが使いまわされていることがわかります。
recycle_list_item

ListViewの描画パフォーマンスについて

ListViewがスクロールされるたびにfindViewByIdメソッドを呼び出すとパフォーマンスが低下します。 再利用したViewを表示するときでも各要素を更新することがあります。
頻繁にfindViewByIdメソッドを呼び出す場合には"view holder"デザインパターンを利用すると良いです。

ViewPager

ViewPager は、横にフリックして View を切り替えるための View です。
単純な View だけでなく、Fragment を持たせて、複数の Fragment を切り替える用途にも使用出来ます。

ViewPager も、ListView と同じく、Adapter に中身を管理させます。

PagerAdapter

FragmentPagerAdapter

GitHub Pagesへ移行しましたmixi-inc.github.ioへお願いします。

Clone this wiki locally