ListViewは、垂直リストを表示するための基本的なAndroidコンポーネントで、API 1(Android 1.0)から利用可能です。ListViewはArrayAdapterを使用してアイテムのリストを作成し、カスタムセルレイアウトをサポートします。ListViewとは何か、その設定方法、そしてこの古いながらもまだ使用されているコンポーネントに隠された落とし穴を理解しましょう。IT Sectrでは、レガシープロジェクトでListViewに遭遇し、Android 4.4との後方互換性を維持しています。最新の代替手段との比較については、RecyclerViewに関する記事をお読みください。
重要なポイント
ListViewは、android.widgetパッケージのViewGroupで、スクロール可能なアイテムのリストを表示するために設計されています。ListViewはAdapterViewパターンを実装しています。データはアダプター(Adapter)を介して渡され、データアイテムを画面に表示されるViewに変換します。ListViewは最初のバージョン(API 1)からAndroid SDKに存在し、追加のライブラリを必要としません。
アーキテクチャはRecyclerViewよりもシンプルです。LayoutManagerは組み込み(垂直スクロールのみ)、変更アニメーションはなく、区切り線はXML属性android:dividerで設定します。ListViewは自動的にOverScrollViewとフォーカスのハイライトを追加し、プロトタイピングを簡素化します。Statista(2025年)によると、Play Storeのトップ100 Androidアプリの約15%にまだListViewが含まれています — ほとんどが最小限のサポートのレガシープロジェクトです。
ListViewは2008年から2014年までAndroidの主要なリストコンポーネントでしたが、2014年のGoogle I/OでRecyclerViewが発表されました。部分的に非推奨(soft-deprecated)ではあるものの、ListViewはすべてのAndroidバージョンで引き続き利用可能であり、SDK API 35で@Deprecatedとマークされていません。プロジェクトがAndroid 4.4(API 19)以下をサポートする場合、RecyclerViewにはAndroidXとGradleの依存関係が必要なため、ListViewが唯一の組み込み選択肢です。
ArrayAdapterはListViewで最も一般的なアダプターです。List<T>と各アイテムのレイアウトを受け取ります。デフォルトでは、ArrayAdapterは各オブジェクトでtoString()を呼び出し、simple_list_item_1(単一のTextView)にテキストを表示します。カスタム表示の場合は、getView()をオーバーライドするか、カスタムレイアウトをコンストラクタに渡す必要があります。
// ArrayAdapterを使ったListView(シンプルテキスト)
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private val countries = listOf(
"ロシア", "アメリカ", "ドイツ",
"フランス", "日本", "中国", "ブラジル"
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val adapter = ArrayAdapter<String>(
this,
android.R.layout.simple_list_item_1,
countries
)
binding.listView.adapter = adapter
// クリック処理
binding.listView.onItemClickListener =
AdapterView.OnItemClickListener { _, _, position, _ ->
Toast.makeText(this, countries[position], Toast.LENGTH_SHORT).show()
}
}
}
SimpleAdapterはMapデータからのリスト用に設計されています。List<Map<String, Any>>と、Mapキーをレイアウト内のウィジェットIDにバインドするためのfrom/to配列を受け取ります。SimpleAdapterは、個別のアダプタークラスが不要な場合のクイックプロトタイプに便利です。欠点は、タイプセーフな型付けの欠如とカスタムロジックの複雑さです。
<!-- ListViewレイアウト -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="@color/divider"
android:dividerHeight="1px"
android:scrollbars="vertical" />
</LinearLayout>
カスタムアダプターはListViewのgetView()メソッドをオーバーライドします。RecyclerViewではonCreateViewHolder + onBindViewHolderに分割されますが、ListViewでは両方のステップが結合されているため、開発者はconvertView(再利用可能なView)とsetTag()メソッドを介してViewHolderパターンを独自に実装する必要があります。
// データモデル
data class Product(
name: String,
price: String,
imageResId: Int
)
// ViewHolderを使ったカスタムアダプター
class ProductAdapter(
context: Context,
private val products: List<Product>
) : ArrayAdapter<Product>(context, 0, products) {
// キャッシュ用のViewHolder
private class ViewHolder(view: View) {
val nameText: TextView = view.findViewById(R.id.tvName)
val priceText: TextView = view.findViewById(R.id.tvPrice)
val imageView: ImageView = view.findViewById(R.id.ivProduct)
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val view = convertView ?: LayoutInflater.from(context)
.inflate(R.layout.item_product, parent, false)
.also { it.tag = ViewHolder(it) }
val holder = view.tag as ViewHolder
val product = getItem(position)
holder.nameText.text = product.name
holder.priceText.text = product.price
holder.imageView.setImageResource(product.imageResId)
return view
}
}
RecyclerViewとの主な違い:ListViewでは、getView()メソッドはアイテムが画面に表示されるたびに呼び出されます。convertViewとViewHolderを使用しない場合、スクロールごとに新しいViewオブジェクトが作成され、50個以上のアイテムのリストでメモリリークと遅延が発生します。カスタムレイアウトのListViewでは、view.tagを使用したViewHolderは必須の最適化です。
ListViewには、GoogleがRecyclerViewで修正した4つのアーキテクチャ上の制限があります:(1)垂直方向のみ — グリッド、水平リスト、カスケードなし;(2)細かい通知なし — データ変更があるとnotifyDataSetChanged()で完全な再描画がトリガーされ、ちらつきが発生;(3)アイテムの追加/削除のアニメーションなし;(4)責任の分離なし — LayoutManagerは組み込み、ItemAnimatorはなし。
<!-- item_product.xml — ListViewアイテムレイアウト -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="12dp">
<ImageView
android:id="@+id/ivProduct"
android:layout_width="56dp"
android:layout_height="56dp"
android:scaleType="centerCrop" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:paddingStart="12dp">
<TextView
android:id="@+id/tvName"
android:textSize="16sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvPrice"
android:textSize="14sp"
android:textColor="@color/price" />
</LinearLayout>
</LinearLayout>
実際の影響:20アイテムまでのリストでは、ListViewはRecyclerViewと同じくらい高速です。100アイテム以上になると、差分計算の欠如と強制的なViewHolderにより、ListViewのFPSが低下し始めます。頻繁に更新されるリスト(チャット、フィード、ログ)にはListViewは適していません — Paging 3とともにRecyclerViewを使用してください。IT Sectrでは、50アイテム以上のレガシー画面を扱う際に、ListViewをRecyclerViewに移行しています。
ListViewは、コードなしで迅速に設定するためのXML属性セットを提供します:android:divider(区切り線の色/ドロウアブル)、android:dividerHeight(太さ)、android:scrollbars(スクロールバー)、android:fastScrollEnabled(ハンドラーによる高速スクロール)、android:choiceMode(選択モード:singleChoice、multipleChoice)。android:entries属性を使用すると、アダプターなしでリソース配列(string-array)からリストを入力できます。
| 属性 | 説明 | 値の例 |
|---|---|---|
| android:divider | 区切り線の色またはドロウアブル | @color/gray_light |
| android:dividerHeight | px/dpでの区切り線の太さ | 1dp |
| android:entries | リソースからの文字列配列 | @array/countries |
| android:choiceMode | アイテム選択モード | singleChoice |
| android:fastScrollEnabled | 高速スクロール | true |
| android:overlapAnchor | ポップアップリストのアンカー(API 21+) | true |
選択モード:choiceMode="singleChoice"は、選択されたアイテムがハイライトされるRadioButtonスタイルを有効にします。choiceMode="multipleChoice" — 複数アイテムのチェックボックス。選択データはlistView.checkedItemPositionまたはlistView.checkedItemIdsを介して利用できます。重要:choiceModeでは、ListViewは背景にStateListDrawableを使用します — 標準の青がデザインに合わない場合は、独自のセレクターを設定してください。
選択はListViewとRecyclerViewの間で、プロジェクトの要件によって異なります。RecyclerViewが最新の標準ですが、ListViewはレガシーコードのメンテナンスコストが最小限のシンプルな画面では正当化できます。主要なパラメーターの比較:
| パラメーター | ListView | RecyclerView |
|---|---|---|
| ライブラリサイズ | SDKに組み込み(約60 KB) | AndroidX(依存関係含め約300 KB) |
| 最小バージョン | API 1 | API 14(AndroidX経由) |
| 方向 | 垂直のみ | 垂直、水平、グリッド、千鳥 |
| ViewHolder | 手動実装 | アーキテクチャ上必須 |
| アニメーション | フェード遷移のみ | DefaultItemAnimator(追加、削除、移動、変更) |
| 細かい更新 | なし(notifyDataSetChangedのみ) | notifyItemInserted/Removed/Changed |
| クリックリスナー | onItemClickListener(組み込み) | ViewHolder経由(組み込みなし) |
| パフォーマンス | 50アイテムまでのリストに十分 | 1000+アイテムで安定した60 FPS |
結論:新しいプロジェクトではRecyclerViewを使用してください。ListViewは、移行が不当に高くつくレガシーコードにのみ残します。Material Design 3ガイドライン(2026年)によると、あらゆる複雑さのリストは、最新のパフォーマンスとアクセシビリティ基準を満たすためにRecyclerViewを使用する必要があります。
よくある質問
ListViewはAndroid SDK API 35で@Deprecatedとマークされていませんが、Googleは新しいプロジェクトでの使用を推奨していません(soft-deprecated)。コンポーネントはすべてのAndroidバージョンで機能し続けます。RecyclerViewは2014年から公式の代替手段です。新しいプロジェクトを開始する場合はRecyclerViewを選択してください。既存のプロジェクトを維持する場合は、ListViewは変更なく動作し続けます。
ListViewはaddHeaderView(View, Object, boolean)メソッドとaddFooterView(View, Object, boolean)メソッドをサポートしています。ヘッダーとフッターは一般的なアイテムリストに追加されますが、アダプターには関与しません — 常にリストの先頭と末尾に表示されます。重要:アダプターを設定する(setAdapter())前にヘッダー/フッターを追加してください。そうしないと含まれません。ヘッダーを追加した後、アイテムの位置IDはヘッダーの数だけシフトします。
ListViewとScrollViewは両方とも垂直ジェスチャーを処理するため、競合が発生します。外部のScrollViewがスクロールをインターセプトし、ListViewがスクロールしなくなります。解決策:(1)ListViewをScrollView内にネストしない — NestedScrollingChildとともにRecyclerViewを使用する、(2)ScrollViewをNestedScrollView(API 21+)に置き換える、(3)ScrollView内で固定高さ(android:layout_height="...dp")のListViewを使用するが、これによりレスポンシブ性が損なわれます。
ListViewのデータクリアはアダプターの種類によって異なります。ArrayAdapterの場合:adapter.clear()とadapter.addAll(newList)の後にadapter.notifyDataSetChanged()を呼び出します。カスタムアダプターの場合:データリストを更新してnotifyDataSetChanged()を呼び出します。注意:listView.invalidateViews()はViewを再描画するだけで、データは更新しません。常にアダプターを介してデータを変更し、直接変更しないでください。
ListViewアイテムの高さはアイテムレイアウトで設定します。レイアウトがlayout_height="wrap_content"を使用する場合、高さはコンテンツに適応します。すべてのアイテムに固定の高さを設定するには、getView()でLayoutParamsを設定するカスタムアダプターを使用します:view.layoutParams.height = resources.getDimensionPixelSize(R.dimen.itemHeight)。異なる高さの場合は、異なるビュータイプでRecyclerViewを使用します。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。