-
Notifications
You must be signed in to change notification settings - Fork 353
2.06. ListView と ViewPager
この章では、ListView と ViewPager の2つの特殊な View について解説します。
参考:Building Layouts with an Adapter | Android Developers
参考:Making ListView Scrolling Smooth | Android Developers
参考:PagerAdapter | Android Developers
参考:FragmentPagerAdapter | Android Developers
- ListView - ListAdapter - ListViewを表示する - カスタマイズしたリストアイテムを表示する - Viewの再利用について - ListViewの描画処理について
- ViewPager - PagerAdapter
ListView は、縦にスクロールする一覧表示のための View です。
ListView 自身には、一覧の中身を管理する機能はありません。
代わりに、Adapter という仕組みを用いて、データソースの管理と、データの View へのバインドをさせ、ListView は、スクロール位置に合わせて必要な View を Adapter から取り出すことをします。
リストデータは、List インタフェースを実装したデータソースや、或いは、データベースへ問い合わせた結果のデータソースである場合もあります。
データをバインドして表示する際には、ListAdapterインタフェースを実装したAdapterを使用します。
Adapterにはいくつかの種類が提供されています。以下はその一部となります。
名前 | 役割 |
---|---|
ArrayAdapter | 配列やリストデータソースをバインドする際に使用します。 |
SimpleCursorAdapter | データベースへ問い合わせた結果をバインドする際に使用します。 |
ListViewを表示するには以下の作業が必要となります。
- ListViewをレイアウトxmlに配置
- ListViewにAdapterをセットする
activity_main.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="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が表示されます。
カスタマイズしたリストアイテムを表示するには以下の作業が必要となります。
- リストアイテムのレイアウトxmlの作成
- 独自Adapterの作成
新規にレイアウトxmlを作成します。
リストアイテムのレイアウトは以下のように作成します。
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内ではリストアイテムの生成と表示位置に対応するデータの取得、設定といった処理を行います。
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メソッドで取得することができます。
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が使いまわされていることがわかります。
ListViewがスクロールされるたびにfindViewByIdメソッドを呼び出すとパフォーマンスが低下します。
再利用したViewを表示するときでも各要素を更新することがあります。
頻繁にfindViewByIdメソッドを呼び出す場合には"view holder"デザインパターンを利用すると良いです。
ViewPager は、横にフリックして View を切り替えるための View です。
単純な View だけでなく、Fragment を持たせて、複数の Fragment を切り替える用途にも使用出来ます。
ViewPager も、ListView と同じく、Adapter に中身を管理させます。
また、ViewPager は Support Package で提供されている機能です。
データをバインドして表示する際には、ListAdapterインタフェースを実装したAdapterを使用します。
PagerAdapterはViewPagerを表示するための基本的なAdapterです。
ここではTextViewをもつページが5つあるViewPagerを作成します。
ViewPagerを表示するには以下の作業が必要となります。
- ViewPagerをレイアウトxmlに配置
- 独自PagerAdapterを実装
- ViewPagerにAdapterをセットする
ViewPagerはSupport Package で提供されているため、android.support.v4.view.ViewPager
と記述します。
activity_main.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="match_parent"
tools:context=".MainActivity" >
<android.support.v4.view.ViewPager
android:id="@+id/Pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
PagerAdapterを実装するには最低限以下のメソッドを実装する必要があります。
メソッド名 | 役割 |
---|---|
Object instantiateItem(ViewGroup container, int position) | ページを生成します。 |
void destroyItem(ViewGroup container, int position, Object object) | ページを削除します。 |
int getCount() | ページ数を返却します。 |
boolean isViewFromObject(View view, Object object) | instantiateItemで返却されたキーObjectとページが関連付いているかどうかを確認します。 |
SamplePagerAdapter.java |
class SamplePagerAdapter extends PagerAdapter {
private static final int PAGE_COUNT = 5;
private Context mContext;
public SamplePagerAdapter(Context context) {
super();
mContext = context;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
// TextViewを生成
TextView textView = new TextView(mContext);
textView.setText("Position:" + position);
//コンテナに追加
container.addView(textView);
// ここではTextView自体をキーとして返しています
return textView;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
// viewの削除
// objectはinstantiateItemで返却したオブジェクトです
container.removeView((View) object);
}
@Override
public int getCount() {
// ページ数を返します。今回は固定値としています。
return PAGE_COUNT;
}
@Override
public boolean isViewFromObject(View view, Object object) {
// キーが正しいかを判定
return view == (TextView) object;
}
ViewPagerはキーとなるobjectでそれぞれのページを関連付けています。このキーはinstantiateItemで返却されたobjectでありAdapter内でのユニークな識別子として使用されます。
上記のようなシンプルなコードではTextView自身をキーとして使用しています。
MainActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ViewPager pager = (ViewPager) findViewById(R.id.Pager);
pager.setAdapter(new SamplePagerAdapter(this));
}
ViewPagerはFragmentの切り替えに使われるケースが多いです。
Fragmentは簡単に提供できますし、それぞれのページのライフサイクルの管理ができるためです。
ViewPagerでFragmentを使う際のよくあるユースケースをカバーし実装されたものが、 FragmentPagerAdapter と FragmentStatePagerAdapterになります。
FragmentPagerAdapterは表示したページをメモリに保持します。
FragmentStatePagerAdapterは非表示になったページはステートの保持のみをしてFragmentを破棄します。ページ数が多い場合メモリ消費が大きくなるためページ数が多い時に使用すると良いです。
- ViewPagerをレイアウトxmlに配置
- Fragmentを作成
- 独自FragmentPagerAdapterを実装
- ViewPagerにAdapterをセットする
PagerAdapterの使い方と同様のため割愛します。
サンプルでは表示位置(position)をTextView表示します。
そのため、newInstanceメソッドを用意しpositionを引数として渡しBundleを設定しています。
Bundleからpositionを取得し、TextViewに設定しています。
SampleFragment.java
public class SampleFragment extends Fragment {
public static SampleFragment newInstance(int position) {
SampleFragment sampleFragment = new SampleFragment();
Bundle bundle = new Bundle();
bundle.putInt("position", position);
sampleFragment.setArguments(bundle);
return sampleFragment;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Bundle bundle = getArguments();
int position = 0;
if (bundle != null) {
position = bundle.getInt("position");
}
View view = inflater.inflate(R.layout.fragment_main, container, false);
TextView text = (TextView) view.findViewById(R.id.TextView1);
text.setText(String.valueOf(position));
return view;
}
}
FragmentPagerAdapterを実装するには最低限以下のメソッドを実装する必要があります。
メソッド名 | 役割 |
---|---|
Fragment getItem (int position) | Fragmentを生成します。 |
int getCount() | ページ数を返します。 |
SampleFragmentPagerAdapter.java |
public class SampleFragmentPagerAdapter extends FragmentPagerAdapter {
private static final int PAGE_COUNT = 5;
public SampleFragmentPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
return SampleFragment.newInstance(position);
}
@Override
public int getCount() {
return PAGE_COUNT;
}
}
Fragmentを管理するためFragmentManagerをコンストラクタに渡します。
Support Packageを使用しているため、FragmentManagerを生成するメソッド名はgetSupportFragmentManagerとなります。
(Support Packageでない場合はgetFragmentManagerとなります)
MainActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ViewPager pager = (ViewPager) findViewById(R.id.Pager);
FragmentManager fm = getSupportFragmentManager();
SampleFragmentPagerAdapter sampleFragmentPagerAdapter = new SampleFragmentPagerAdapter(fm);
pager.setAdapter(sampleFragmentPagerAdapter);
}
- (実習)
- (実習)
- (課題)
- (課題)
- (実習)
- (実習)
Portions of this page are reproduced from work created and shared by the Android Open Source Project and used according to terms described in the Creative Commons 2.5 Attribution License.