RecyclerView — その概要とAndroidにおけるViewHolderおよびAdapter

著者: IT Sectr 公開日: 2026-02-23 読了時間: 8 分

RecyclerViewは、大規模なデータセットをリストやグリッドとして効率的に表示するためのAndroid Jetpackコンポーネントです。ListViewとは異なり、RecyclerViewはViewHolderを再利用し、LayoutManagerを通じてレイアウトを管理します。Google(Android Developers、2026)によると、RecyclerViewはViewHolderのプーリングとDiffUtil差分アルゴリズムにより、1000+アイテムのリストをスクロールする際に最大毎秒60フレームで処理します。Androidアプリのアーキテクチャについて詳しくは、ListViewに関する記事をご覧ください。

重要なポイント

  • RecyclerViewはAndroidにおけるリスト表示のための最新コンポーネントで、Android 5.0(API 21)でListViewに取って代わりました。
  • ViewHolderはView参照をキャッシュするパターンで、スクロール時のセル再利用を高速化します。
  • AdapterはデータとRecyclerViewの間の橋渡し役で、ViewHolderの作成とデータのバインドを担当します。
  • LayoutManagerは要素の配置を定義します:垂直/水平リスト(LinearLayoutManager)、グリッド(GridLayoutManager)、スタッガード(StaggeredGridLayoutManager)。
  • DiffUtilは古いリストと新しいリストの差分を計算するユーティリティで、要素の再描画を最小限に抑えます。

RecyclerViewとは?

RecyclerViewはAndroid Jetpackライブラリのコンポーネントで、Google I/O 2014でAndroid Support Library v7の一部として発表されました。動的なリストやグリッドを高いパフォーマンスで表示するために設計されています。RecyclerViewはListViewの後継として登場し、その前身の主要な問題であるViewHolderの強制的な再利用の欠如を解決します。

RecyclerViewのアーキテクチャはModel-View-Adapterパターンに基づいています。データはモデル(List、LiveData、Flow)に保存され、レンダリングはAdapterが管理し、レイアウトはLayoutManagerが制御します。各コンポーネントが自身の領域を担当するため、システムは柔軟で拡張可能です。

Android Developersの調査(2025年)によると、Google Playトップ100のAndroidアプリの78%でRecyclerViewが使用されています。1000+アイテムのリストのスクロールパフォーマンスは、3つのメカニズム(ViewHolderプーリング、差分ベースの更新計算、非同期データ読み込み)により、ミッドレンジデバイスで60 FPSを安定して維持します。

RecyclerViewを使用すべき場合

RecyclerViewは以下の場合に最適です:アイテム数が不明なリスト(フィード、チャット、ログ)、N列のグリッド(ギャラリー、カタログ)、スタッガードレイアウト(Pinterestボード)、アイテムの追加/削除アニメーションがあるリスト。2~5アイテムのシンプルな画面では、LinearLayoutまたはScrollViewの方がシンプルで効率的です — RecyclerViewはオーバーヘッドを追加します。

依存関係の設定

RecyclerViewを使用するには、build.gradle(app)に依存関係を追加します:implementation 'androidx.recyclerview:recyclerview:1.4.0'。最小APIバージョンは14(Android 4.0)です。AndroidX RecyclerView 1.3.0以降、アイテム選択用のrecyclerview-selectionモジュールとスワイプジェスチャ用のrecyclerview-swipeモジュールが利用可能です。

RecyclerViewの仕組み:3つの主要コンポーネント

RecyclerViewは3つの独立したレイヤーに分割されています:AdapterはデータとViewHolderを管理し、LayoutManagerはアイテムの配置を担当し、ItemAnimatorはアニメーションを制御します。このアーキテクチャにより、他のレイヤーを変更せずに任意のレイヤーを交換できます — 例えば、Adapterのコードを変更せずにLinearLayoutManagerからGridLayoutManagerに切り替えることができます。

コンポーネント責任実装
AdapterViewHolderの作成、データのViewへのバインド、変更の通知RecyclerView.Adapter<VH>
ViewHolderView参照のキャッシュ、findViewById()の回避RecyclerView.ViewHolder
LayoutManagerアイテムの配置、スクロール方向の決定LinearLayoutManager、GridLayoutManager、StaggeredGridLayoutManager
ItemAnimatorアイテムの追加、削除、移動のアニメーションDefaultItemAnimator
ItemDecorationアイテム間の区切り線、スペーシング、背景の描画RecyclerView.ItemDecoration

動作サイクル:(1)AdapterがonCreateViewHolder()を介してViewHolderを作成し、(2)LayoutManagerが画面上の新しいViewの位置を決定し、(3)AdapterがonBindViewHolder()を介してデータをバインドし、(4)スクロール中に画面外に出たViewHolderは再利用のためにプール(RecycledViewPool)に入ります。新しいViewを作成する代わりに、既存のViewを再利用します — これが主要なパフォーマンスメカニズムです。

RecyclerViewにおけるViewHolderの実装

ViewHolderは、リストアイテム内のView(TextView、ImageViewなど)への参照を保存するクラスです。ViewHolderがない場合、スクロールのたびにfindViewById()がトリガーされます — これはAndroid UIで最もコストの高い操作の1つです。ViewHolderは最初のバインド後に参照をキャッシュすることで、この呼び出しを排除します。

kotlin
// データモデル
data class Article(
    title: String,
    summary: String,
    author: String,
    imageUrl: String?
)

// ViewHolder — View参照を保存する
class ArticleViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    private val titleText = itemView.findViewById<TextView>(R.id.tvTitle)
    private val summaryText = itemView.findViewById<TextView>(R.id.tvSummary)
    private val authorText = itemView.findViewById<TextView>(R.id.tvAuthor)

    fun bind(article: Article) {
        titleText.text = article.title
        summaryText.text = article.summary
        authorText.text = article.author
    }
}

実践的なルール:アイテムレイアウト内のすべてのViewは、コンストラクタでfindViewById()を使用して1回だけViewHolderプロパティとして宣言する必要があります。bind()メソッドはfindViewById()を呼び出さず、テキスト、色、リスナーのみを設定します。このアプローチにより、Google(Android Performance Patterns、2025年)によると、リストのスクロールが30~50%高速化されます。

LayoutManagerの種類:要素の配置を選ぶ

LayoutManagerは、RecyclerView内でアイテムがどのように配置されるかを定義します。Androidは3つのデフォルト実装を提供しています:LinearLayoutManager、GridLayoutManager、StaggeredGridLayoutManager。LayoutManagerの選択は、データの認識とスクロールパフォーマンスに影響します。

LayoutManager配置使用すべき場合設定方法
LinearLayoutManagerリスト(垂直または水平)フィード、チャット、通知、商品リストLinearLayoutManager(context)
GridLayoutManagerN列のグリッドギャラリー、カタログ、アイコン、画像グリッドGridLayoutManager(context, spanCount)
StaggeredGridLayoutManagerスタッガードグリッド(異なるアイテム高さ)Pinterestボード、ノート、メーソンリーギャラリーStaggeredGridLayoutManager(spanCount, orientation)
kotlin
// FragmentでのLinearLayoutManager設定
class ArticleListFragment : Fragment() {
    private lateinit var binding: FragmentArticleListBinding

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        val recyclerView = binding.recyclerView
        recyclerView.layoutManager = LinearLayoutManager(requireContext())
        recyclerView.adapter = ArticleAdapter(articlesList)

        // 最適化:固定サイズ+プール
        recyclerView.setHasFixedSize(true)
        recyclerView.setItemViewCacheSize(20)

        // アイテム間の区切り線
        val divider = DividerItemDecoration(requireContext(), LinearLayoutManager.VERTICAL)
        recyclerView.addItemDecoration(divider)
    }
}

パフォーマンス:垂直スクロールのLinearLayoutManagerが最速のオプションです。アイテムが順番に配置され、LayoutManagerがO(1)で位置を計算するためです。GridLayoutManagerは列分割のために追加の計算が必要ですが、固定のspanCountでは効率的です。StaggeredGridLayoutManagerは3つの中で最も遅く、隣接するアイテムの高さを考慮してカスケード内のアイテム位置を計算する必要があるためです。視覚的に異なるセルの高さが必要な場合にのみ使用してください。

AdapterとDiffUtil:効率的なデータ更新

AdapterはRecyclerViewにおいてGoFのAdapterパターンを実装します:データ(List<T>)を画面に表示されるViewHolderに変換します。データが変更されると、AdapterはnotifyDataSetChanged()notifyItemInserted()などのメソッドを介してRecyclerViewに通知できます。問題は、notifyDataSetChanged()がすべての表示アイテムを再描画し、ジャンク(カクツキ)を引き起こすことです。

kotlin
// 効率的な更新のためのDiffUtil付きAdapter
class ArticleAdapter : RecyclerView.Adapter<ArticleViewHolder>() {

    private var articles: List<Article> = emptyList()

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ArticleViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.item_article, parent, false)
        return ArticleViewHolder(view)
    }

    override fun onBindViewHolder(holder: ArticleViewHolder, position: Int) {
        holder.bind(articles[position])
    }

    override fun getItemCount(): Int = articles.size

    // DiffUtilによるリスト更新
    fun updateArticles(newArticles: List<Article>) {
        val diffCallback = ArticleDiffCallback(articles, newArticles)
        val diffResult = DiffUtil.calculateDiff(diffCallback)
        articles = newArticles
        diffResult.dispatchUpdatesTo(this)
    }
}

// DiffUtilのCallback
class ArticleDiffCallback(
    private val oldList: List<Article>,
    private val newList: List<Article>
) : DiffUtil.Callback() {

    override fun getOldListSize() = oldList.size
    override fun getNewListSize() = newList.size

    override fun areItemsTheSame(oldPos: Int, newPos: Int): Boolean {
        return oldList[oldPos].title == newList[newPos].title
    }

    override fun areContentsTheSame(oldPos: Int, newPos: Int): Boolean {
        return oldList[oldPos] == newList[newPos]
    }
}

DiffUtilEugene W. Myersの差分アルゴリズムを使用し、古いリストを新しいリストに変換するための最小操作数(挿入、削除、移動、変更)を計算します。100アイテムのリストの場合、DiffUtilは1~5ミリ秒で動作します(Google、2025年)。大規模リスト(10000+)の場合は、PagingライブラリのAsyncListDifferまたはPagingDataAdapterを使用してください — これらはバックグラウンドスレッドで計算を実行します。

RecyclerView vs ListView:比較

ListViewはRecyclerViewの前身で、Android 1.0(API 1)で導入されました。リストを表示しますが、ViewHolderを強制せず、LayoutManagerをサポートせず、変更をアニメーション化できません。RecyclerViewは、より厳格なアーキテクチャと優れたパフォーマンスを備えたListViewの後継として設計されました。

基準RecyclerViewListView
ViewHolder必須(アーキテクチャ)推奨されるが必須ではない
LayoutManager分離:リスト、グリッド、スタッガード垂直リストのみ
変更アニメーションDefaultItemAnimatorが組み込み組み込みサポートなし
変更通知詳細:notifyItemInserted/Removedリスト全体に対してnotifyDataSetChanged()のみ
DiffUtilDiffUtil.Callbackによるサポート同等機能なし
区切り線ItemDecoration(カスタム)android:divider(組み込み)
最小バージョンAndroidX経由でAPI 14(Android 4.0)API 1(全バージョン)

Googleの推奨(Android Developers、2026年):すべての新規プロジェクトにはRecyclerViewを使用してください。ListViewは、移行コストが正当化されないレガシーコードベースに残すことができます。RecyclerViewは100+アイテムで優れたパフォーマンスを提供し、追加の回避策なしで水平スクロールとグリッドをサポートします。Android 15では、ListViewは公式に部分的に非推奨とマークされています。

よくある質問

RecyclerViewがアイテムを表示しない — どうすればよいですか?

3つの条件を確認してください:(1)recyclerView.adapter = adapterでAdapterが設定されている、(2)recyclerView.layoutManager = LinearLayoutManager(context)でLayoutManagerが設定されている、(3)データリストが空でなく、adapterに通知されている(notifyDataSetChanged())。よくある間違いは、notifyDataSetChanged()を呼び出した後にデータを設定することです。データは通知の前に割り当てる必要があります。リストサイズを確認してください:adapter.itemCountは0より大きくなければなりません。

RecyclerViewのスクロールが遅いのはなぜですか?

スクロール中の遅延(ジャンク)は、onBindViewHolder()が重い操作(キャッシュなしの画像読み込み、bind内のfindViewById()、複雑な計算)を実行するときに発生します。解決策:(1)非同期画像読み込みにはGlideまたはCoilを使用する、(2)すべてのfindViewById()呼び出しをViewHolderコンストラクタに移動する、(3)差分計算にはAsyncListDifferを使用する。Android StudioでGPUプロファイリング(Profile GPU Rendering)を有効にして、遅いフレームを見つけてください。

水平方向のRecyclerViewを作成するには?

LayoutManagerに向きを渡します:LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)。2番目のパラメータはreverseLayoutです(false = 左から右)。水平グリッドの場合は、水平方向のGridLayoutManagerまたはページスクロール用のカスタムLinearSnapHelperを使用します。水平リストは、商品、画像、カテゴリのカルーセルに適しています。

RecyclerViewアイテムにクリックリスナーを追加するには?

RecyclerViewには(onItemClickListenerを持つListViewとは異なり)組み込みのonClickListenerはありません。ViewHolderで実装します:コンストラクタにラムダを渡し、itemView.setOnClickListenerを設定します。例:class ArticleViewHolder(itemView: View, val onItemClick: (Article) -> Unit) : RecyclerView.ViewHolder(itemView)。bind()内でonItemClick(article)を呼び出します。代替として、ジェスチャ処理にRecyclerView.addOnItemTouchListener()を使用することもできます。

RecycledViewPoolとは何ですか?どのように設定しますか?

RecycledViewPoolは、スクロール中に非表示になったViewHolderの内部プールです。破棄される代わりに、プールに保持され再利用されます。デフォルトでは、プールは各タイプの5つのViewHolderを保存します。複雑なレイアウトの場合はサイズを増やしてください:recyclerView.getRecycledViewPool().setMaxRecycledViews(VIEW_TYPE_NORMAL, maxCount)。RecyclerViewに複数のViewタイプ(viewType)がある場合、プールはそれぞれに個別のスタックを保持します。異なるRecyclerView間でRecycledViewPoolを共有すると、ネストされたリストがある画面でメモリを節約できます。

まとめ

  • RecyclerViewは、最大60 FPSのパフォーマンスを備えたリストとグリッドのための最新のAndroid Jetpackコンポーネントです。
  • ViewHolderはViewキャッシングのための必須パターンで、スクロールのたびにfindViewById()を排除します。
  • AdapterはonCreateViewHolderとonBindViewHolderを通じてデータを管理しViewHolderを作成します。
  • LayoutManager — 3つの実装:LinearLayoutManager(リスト)、GridLayoutManager(グリッド)、StaggeredGridLayoutManager(スタッガード)。
  • DiffUtil — 最小限のリスト更新のためのアルゴリズム:完全リフレッシュではなく詳細な再描画。
  • RecyclerViewはトップ100の78%のAndroidアプリで使用され、GoogleがListViewに代わって公式に推奨しています。
  • RecyclerViewのパフォーマンスは、ViewHolderプーリング、詳細な通知、非同期の差分計算によって実現されます。

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください