Android RecyclerView: とは何か、簡単な例で学ぶ

⚡ スマートサマリー

RecyclerViewは高度な Android 限られた数のビューを再利用することで、大規模でスクロール可能なデータセットを効率的に表示するウィジェットです。実行時に変化するデータコレクションをバインドするために、アダプタ、レイアウトマネージャ、およびビューホルダーを利用します。

  • ???? それは何ですか: RecyclerViewは、大規模なデータコレクションを表示するための、ListViewやGridViewの後継となる柔軟なビューアです。
  • 🧱 ビューとビューグループ: Android ViewクラスとViewGroupクラスを使用してインターフェースを描画し、LayoutParamsでサイズと位置を制御します。
  • 🔄 アダプタ: アダプターパターンは、データ配列をRecyclerViewが受け入れ可能なViewHolderに変換します。
  • 📐 レイアウトマネージャー: レイアウトマネージャは、アイテムを直線状またはグリッド状に配置し、ビューを再利用できるタイミングを通知します。
  • ♻️ リサイクル: 画面外のビューは再作成するのではなく再利用されるため、メモリを節約し、スクロールをスムーズに行うことができます。
  • 🛠️ ビルド手順: 加えます Gradle 依存関係を作成し、アイテムレイアウト、ViewHolder、アダプター、およびLayoutManagerを作成します。

Android リサイクラービュー

RecyclerView とは何ですか Android?

その リサイクラービュー RecyclerViewは、GridViewやListViewよりも柔軟で高度なバージョンです。限られた数のビューを維持することで効率的にスクロールできる、大規模なデータセットを表示するためのコンテナです。ネットワークイベントやユーザー操作に応じて実行時に要素が変化するデータコレクションを扱う場合に、RecyclerViewウィジェットを使用できます。

ビュー

その Android プラットフォームは、View クラスと ViewGroup クラスを使用して画面上にアイテムを描画します。これらのクラスは、tract と t は、ユースケースに合わせてさまざまな実装に拡張されます。たとえば、TextView は、画面にテキストコンテンツを表示するというシンプルな目的を持っています。EditText は同じ View クラスを継承し、ユーザーがデータを入力できるようにするための機能を追加しています。

開発時に柔軟性を高めるために独自のカスタムビューを作成することが可能です。ping ユーザーインターフェース。Viewクラスは、画面に描画するためのオーバーライド可能なメソッドと、幅、高さ、およびViewに追加して意図した動作を実現するための独自のカスタム属性などのパラメータを渡す手段を提供します。

ビューグループ

ViewGroupクラスはViewの一種ですが、単に表示することだけを担う単純なViewクラスとは異なり、ViewGroupでは複数のビューを1つのビューにまとめることができ、それを全体として参照できます。この場合、最上位レベルで作成され、そこに他の単純なビューを追加するビューは「親」と呼ばれ、その中に追加されるビューは「子」と呼ばれます。

ビューを配列、ビューグループを配列の配列と考えることができます。配列の配列自体が配列であることを考えると、ビューグループをビューのように扱うことができることがわかります。

var arr1 = [1,2,3] //imagine a simple View as an Array
//we can imagine this as a NumberTextView which doesn't really exist
//but we could imagine there's one that makes it easy to use numbers
var arr2 = ["a","b","c"] // We can imagine this as another simple view

var nestedArr = [arr1,arr2] //in our analogy, we can now group views
//together and the structure that would hold that would be what we call the ViewGroup

ViewGroupを使用すると、ビュー内の子要素の配置方法(例えば、縦方向に並べるか横方向に並べるか)を定義できます。また、ビュー内でのインタラクションに関するルールを複数設定することも可能です。例えば、TextView同士が連続して表示される場合は12dpの間隔を空ける一方、ImageViewの後にTextViewが表示される場合は5dpの間隔を空けるといったルールを設定できます。

これは、私たちが開発されていた場合の話です。ping 独自の ViewGroup をゼロから作成します。これらの設定を容易にするために、 Android には、これらの構成を入力するために使用できる LayoutParams というクラスが用意されています。

その Android ドキュメント は、独自の ViewGroup を設定する際に実装するデフォルト パラメータを提供します。一般的なパラメータには、幅、高さ、マージンなどがあります。デフォルトでは、これらの設定は高さに android:layout_height、幅に android:layout_width という構造になっています。この点に関して、ViewGroup を作成する際に、ViewGroup の動作方法に合わせて LayoutParams をさらに作成することができます。

Android には、私たちがよく行う多くのタスクを実行するために使用できるデフォルトのビューとビューグループが付属しています。例として、すでに述べたTextViewがあります。これは、高さ、幅、テキストサイズなどの設定が可能なシンプルなビューです。その他にも、画像を表示するためのImageViewや、前述のEditTextなど、多くのビューが用意されています。 Android ビューを追加して期待どおりの動作を得ることができるカスタム ViewGroups もあります。

LinearLayout

LinearLayout を使用すると、その中に View アイテムを追加できます。LinearLayout には、画面上でのレイアウト方法を指定する orientation 属性があります。また、内部のビューのルールを指定する LinearLayout.LayoutParams もあります。たとえば、android:center_horizo​​ntal 属性はビューを水平軸に沿って中央揃えにし、android:center_vertical 属性はビューのコンテンツを垂直軸に沿って中央揃えにします。

中央揃えを理解するのに役立つ画像をいくつかご紹介します。ここでは、200px x 200pxのスペース内にあるシンプルなTextViewを想定します。中央揃え属性を設定すると、以下のように動作します。

アンドロイド:中央_水平

コンテンツを水平方向に中央揃え

コンテンツを水平方向に中央揃え

アンドロイド:中央垂直

縦方向に中央揃えされたコンテンツ

縦方向に中央揃えされたコンテンツ

アンドロイド:センター

中心にあるコンテンツ

中心にあるコンテンツ

RecyclerView のコア コンポーネント

RecyclerView のコア コンポーネント

RecyclerView のコア コンポーネント

RecyclerView の重要なコンポーネントは次のとおりです。

RecyclerView.Adapter

基礎 #1 – アダプターのパターン

アダプタとは、あるシステムまたはデバイスの特性を、本来互換性のない別のシステムまたはデバイスの特性に変換する装置です。アダプタの中には、信号や電源の特性を変更するものもあれば、単にコネクタの形状を別のコネクタの形状に適合させるだけのものもあります。

アダプターの分かりやすい実例として、機器同士を接続する必要があるものの、接続ポートの形状が合わない場合が挙げられます。これは、異なる種類のコンセントを使用している国を訪れた際に起こり得ます。スマートフォンやノートパソコンの充電器を持参しても、現地のコンセントに接続することはできません。しかし、諦める必要はありません。コンセントと充電器の間にアダプターを差し込むことで、充電が可能になります。

これはプログラミングにおいて、あるタスクを実行するために2つのデータ構造を接続したい場合などに当てはまりますが、それらのデフォルトのポートには互いに通信する方法がありません。

ここでは、デバイスと充電器というシンプルな例を用います。充電器はアメリカ製のものとイギリス製のものの2種類を用意します。

class AmericanCharger() {
    var chargingPower = 10
}
class BritishCharger(){
    var chargingPower = 5
}

次に、2つのデバイスを作成します。

class AmericanDevice()
class BritishDevice()

例として、一緒に遊ぶためのデバイスのインスタンスをいくつか作成できます。

var myAmericanPhone = new AmericanDevice()
var myBritishPhone = new BritishDevice()

次に、デバイスにcharge()というメソッドを追加することで、両方のデバイスの充電機能を導入します。このメソッドはそれぞれの充電器を引数として受け取り、それに基づいて充電処理を行います。

sealed trait Device
class AmericanDevice : Device{
    fun charge(charger:AmericanCharger){
        //Do some American charging
    }
}
class BritishDevice: Device{
    fun charge(charger:BritishCharger){
        //Do some British charging
    }
}

この場合、私たちの類推に基づくと、何らかの理由で、アメリカ製のデバイスを使用する際にイギリス製の充電器を使用する必要が生じるか、あるいはその逆のケースが生じるでしょう。

プログラミングの世界では、これは通常、同じ機能を提供するライブラリを混在させる場合に発生します (この文脈では、共有機能は課金されます)。 これを可能にする方法を見つける必要があります。

この例えに従うなら、イギリス製の充電器を使ってアメリカ製の機器を充電できるように、電気店に行ってアダプターを購入する必要があるでしょう。プログラミングの観点から言えば、私たちはそのアダプターの製造者ということになります。

他のアダプターを作成するために必要なパターンと完全に一致するアダプターを作成します。以下のようにクラスとして実装します。必ずしもクラスである必要はなく、アダプターパターンが一般的に何をするかを強調する関数でも構いません。クラスを使用するのは、ほとんどの使用例と一致するためです。 Android.

class AmericanToBritishChargerAdapter(theAmericanCharger:AmericanCharger){
    fun returnNewCharger(): BritishCharger{
            //convert the American charger to a BritishCharger
            //we would change the American charging functionality
            //to British charging functionality to make sure the
            //adapter doesn't destroy the device. The adapter could
            //, for example, control the power output by dividing by 2
            //our adapter could encompass this functionality in here

           var chargingPower:Int = theAmericanCharger.chargingPower / 2
           var newBritishCharger = new BritishCharger()
           newBritishCharger.chargingPower = theAmericanCharger.chargingPower/2

        return newBritishCharger
    }
}

プログラミングの世界では、ソケットの違いは充電方法の違いに例えられます。充電器の充電方法が異なれば、充電器同士が互換性を持たなくなってしまうでしょう。

var myBritishDevice = new BritishDevice()
var americanChargerIFound = new AmericanCharger()

myBritishDevice の charge() メソッドを americanChargerIFound で呼び出そうとしても機能しません。AmericanDevice は AmericanCharger しか受け付けないためです。したがって、これは不可能です。

var myBritishDevice = new BritishDevice()
var americanChargerIFound = new AmericanCharger()
myBritishDevice.charge(americanChargerIFound)

このシナリオでは、作成したアダプター AmericanToBritishChargerAdapter が役立ちます。returnNewCharger() メソッドを使用して新しい BritishCharger を作成し、それを使って充電できます。必要なのは、アダプターのインスタンスを作成し、用意した AmericanCharger を渡すだけです。そうすれば、使用可能な BritishCharger が作成されます。

var myBritishDevice = new BritishDevice()
var americanChargerIFound = new AmericanCharger()
//We create the adapter and feed it the americanCharger
var myAdapter =  AmericanToBritishChargerAdapter(americanChargerIFound)
//calling returnNewCharger from myAdapter would return a BritishCharger
var britishChargerFromAdapter = myAdapter.returnNewCharger()
//and once we have the britishCharger we can now use it
myBritishDevice.charge(britishChargerFromAdapter)

RecyclerView.LayoutManager

ViewGroupを扱う場合、その中にViewが配置されます。LayoutManagerは、Viewがどのように配置されるかを記述する役割を担います。

比較のために、LinearLayout ViewGroup を使用する場合、必要なユースケースは、アイテムを垂直または水平に配置できることです。これは、LinearLayout が画面上でどのように配置されるかを示す orientation 属性を追加することで簡単に実装できます。これを行うには、 android:orientation=VERTICAL|HORIZONTAL 属性。

また、GridLayoutという別のViewGroupもあります。これは、ビューを長方形のグリッド構造に配置したい場合に使用します。例えば、アプリユーザーに表示するデータを見やすくするためなどが挙げられます。GridLayoutは、グリッドの寸法を定義することで、この目的を達成するための設定を可能にするように設計されています。例えば、4×4グリッドや3×2グリッドを作成できます。

RecyclerView.ViewHolder

ViewHolderは絶対的なtracViewHolder は、RecyclerView から継承するクラスです。ViewHolder は、RecyclerView のリサイクル機構によって、私たちが知らないさまざまな参照が変更された後でも、RecyclerView に配置した View を参照するのに役立つ共通のメソッドを提供します。

大きなリスト

RecyclerView は、非常に多くの View をユーザーに表示したいが、 RAM 作成されたビューのすべてのインスタンスについて、デバイス上で。

連絡先リストを例にとると、リスト内で個々の連絡先がどのように表示されるかはおおよそ把握しているはずです。次に、連絡先リストから様々なデータが入力されるためのスロットを持つテンプレートレイアウト(実際にはビュー)を作成します。以下は、その目的全体を説明する擬似コードです。

//OneContactView
<OneContact>
<TextView>{{PlaceHolderForName}}</TextView>
<TextView>{{PlaceHolderForAddress}}</TextView>
<ImageView>{{PlaceHolderForProfilePicture}}</ImageView>
<TextView>{{PlaceHolderForPhoneNumber}}</TextView>
</OneContact>

すると、以下のような連絡先リストが作成されます。

<ContactList>
</ContactList>

コンテンツをハードコーディングすると、アプリを書き換えずにリストに新しいコンテンツを追加するプログラム的な方法がなくなってしまいます。幸いなことに、ViewGroup に View を追加することは、 addView(view:View) この方法は、RecyclerViewに子ビューを追加する方法ではありません。

今回のユースケースでは、連絡先リストが長くなります。リスト内の各連絡先に対して、OneContactViewを作成し、View内のデータをContactクラスのフィールドに合わせて設定する必要があります。そして、Viewを作成したら、それをRecyclerViewに追加してリストを表示する必要があります。

data  class Contact(var name:String, var address:String, var pic:String, var phoneNumber:Int)

var contact1 = Contact("Guru","Guru97", "SomePic1.jpg", 991)
var contact2 = Contact("Guru","Guru98", "SomePic2.jpg", 992)
var contact3 = Contact("Guru","Guru99", "SomePic3.jpg", 993)

var myContacts:ArrayList<Contact> = arrayListOf<Contact>(contact1,contact2,contact3)

連絡先の配列があります。OneContactViewには、Contactクラスからコンテンツを取得して表示するためのスロットが含まれています。RecyclerViewでは、リサイクル機能を利用するために、ビューを追加する必要があります。

RecyclerView ではビューを追加することはできませんが、ViewHolder を追加することはできます。つまり、このシナリオでは、接続したい 2 つのパーツがありますが、それらは一致しません。ここでアダプターが登場します。RecyclerView は、私たちの AmericanToBritishChargerAdapter() 以前の情報のおかげで、イギリス製のデバイスでは使用できなかったアメリカ製の充電器を、実際の電源アダプターのように使えるものに変換することができました。

このシナリオでは、アダプタは連絡先の配列とビューを受け取り、そこからRecyclerViewが受け入れ可能なViewHolderを生成します。

RecyclerViewは、RecyclerView.Adapterクラスを介してアダプターを作成するために拡張できるインターフェースを提供しています。このアダプターの中には、RecyclerViewが操作したいViewHolderクラスを作成する方法があります。つまり、状況は以前と同じですが、アダプターという要素が一つ追加されています。

連絡先の配列、1つの連絡先を表示するビュー(OneContactView)、そしてリサイクルサービスを提供するビューのリストであるRecyclerViewがあります。ただし、RecyclerViewはViewHolderのみを受け入れます。このシナリオでは、ViewHolderを作成するメソッドを持つRecyclerView.Adapterクラスを使用します。

fun createViewHolder(@NonNull parent: ViewGroup, viewType: Int): ViewHolder

RecyclerView.ViewHolder は絶対的なtracViewを引数として受け取り、それをViewHolderに変換するクラスです。クラスの機能を拡張するために使用されるラッパーパターンを採用しています。

基礎 #2 – ラッパーパターン

動物に言葉を話させる方法を、簡単な例を使って説明しましょう。

sealed trait Animal{
    fun sound():String
}

data class Cat(name:String):Animal{
    fun sound(){
        "Meow"
        }
}
data class Dog(name:String):Animal{
    fun sound(){
        "Woof"
        }
}

var cat1 = Cat("Tubby")
var dog1 = Dog("Scooby")
cat1.sound() //meow
dog1.sound() //woof

上記の例では、2匹の動物が登場します。仮に、動物に喋らせるメソッドを追加したいと思ったものの、ライブラリの作者が使い物にならなかったとしても、何らかの方法を見つけることは可能です。必要なのは、Animalクラスのラッパーです。これは、Animalクラスをコンストラクタとして受け取ることで実現できます。

class SpeechPoweredAnimalByWrapper(var myAnimal:Animal){

    fun sound(){
        myAnimal.sound()
    }

    fun speak(){
        println("Hello, my name is ${myAnimal.name}")
    }
}

これで、動物のインスタンスを SpeechPoweredAnimalByWrapper に渡すことができます。そのインスタンスの sound() メソッドを呼び出すと、渡された動物の sound() メソッドが呼び出されます。また、渡された動物に追加される新しい機能として、speak() メソッドも用意されています。これは次のように使用できます。

var cat1 =  Cat("Garfield")
cat1.sound()//"meow"
cat1.speak()// doesn't work as it isn't implemented
var talkingCat = new SpeechPoweredAnimalByWrapper(cat1)
talkingCat.sound() //"meow" the sound method calls the one defined for cat1
talkingCat.speak() //"Hello, my name is Garfield"

このパターンを使用すると、クラスに機能を追加できます。必要なのは、クラスインスタンスと、ラップで定義された新しいメソッドを渡すだけです。ping とに提供されます。

上記の例では具象クラスを使用しましたが、抽象クラスでも同様の処理を実装できます。tract クラス。SpeechPoweredAnimalByWrapper クラスを abs に変更する必要があります。tracこれで完了です。クラス名をもっと短くして、読みやすくしましょう。

abstract class SpeechPowered(var myAnimal:Animal){

    fun sound(){
        myAnimal.sound()
    }

    fun speak(){
        println("Hello, my name is ${myAnimal.name}")
    }
}

以前と同じですが、意味が異なります。通常のクラスでは、cat1 と dog1 を作​​成したのと同じように、クラスのインスタンスを持つことができます。tracしかし、t クラスはインスタンス化されることを意図したものではなく、他のクラスを拡張することを目的としています。では、新しい SpeechPowered(var myAnimal:Animal) をどのように使用するのでしょうか。tract クラスはどうでしょうか?それを拡張する新しいクラスを作成することで、その機能を取得できます。

この例では、クラスを継承するクラス SpeechPoweredAnimal を作成します。

class SpeechPoweredAnimal(var myAnimal:Animal):SpeechPowered(myAnimal)
var cat1 =  Cat("Tubby")
var speakingKitty = SpeechPoweredAnimal(cat1)
speakingKitty.speak() //"Hello, my name is Tubby"

これは ViewHolder で使用されているパターンと同じです。RecyclerView.ViewHolder クラスは絶対的なものです。tract クラスは、動物に speak メソッドを追加したのと同様に、View に機能を追加します。追加された機能によって、RecyclerView を扱う際に正しく動作するようになります。

OneContactViewからOneContactViewHolderを作成する方法は次のとおりです。

//The View argument we pass is converted to a ViewHolder which uses the View to give it more abilities and in turn work with the RecyclerView
class OneContactViewHolder(ourContactView: View) : RecyclerView.ViewHolder(ourContactView)

RecyclerViewには、連絡先配列をRecyclerViewを介してContactsViewに接続できるアダプターがあります。

ビューの追加

ViewGroupは自動的に再描画されるのではなく、特定のスケジュールに従って再描画されます。お使いのデバイスによっては、10msごと、あるいは100msごとに再描画される場合もあります。また、仮に極端な例を挙げると、ViewGroupにビューを追加した場合、ViewGroupが「更新」される1分後に変更が反映されます。

RecyclerView.Recycler

基礎作業その3 – キャッシュ

定期的にページを更新する最も良い例の一つは、ブラウザです。例えば、アクセスしているサイトが静的で、動的にコンテンツを送信していないとしましょう。変更を確認するには、ページを更新し続ける必要があります。

この例では、対象となるサイトをTwitterだと仮定してみましょう。静的なツイートが一覧表示され、新しいツイートを見るには、更新ボタンをクリックしてコンテンツを再取得するしか方法はありません。

画面全体を再描画するのは明らかにコストがかかる。例えば、携帯電話会社の通信帯域幅が限られていて、ツイートリストに画像や動画がたくさん含まれている場合、ページを更新するたびにすべてのコンテンツを再ダウンロードするのはコストがかかるだろう。

既に読み込まれたツイートを保存し、次のリクエストで既に取得済みのツイートを識別できるようにする必要があります。そのため、すべてを再ダウンロードするのではなく、新しいツイートのみを取得し、ローカルに保存されていたツイートが既に存在しないかどうかを確認して、ローカルから削除します。ここで説明しているのは、キャッシュと呼ばれるものです。

私たちがウェブサイトに送信する、私たちが保有するコンテンツに関する情報はメタデータと呼ばれます。つまり、実際には「あなたのサイトを読み込みたい」と言っているのではなく、「あなたのサイトを読み込みたいのですが、前回読み込んだ際に保存したコンテンツの一部があります。そこにないものだけを送信してください。そうすれば帯域幅をあまり消費しません」と言っているのです。

レイアウト呼び出し - ツイートリストはクレイジーに違いありません

レイアウト呼び出しの一例として、scrollToPosition があります。これはチャットアプリなどでよく見られる例です。チャットスレッドで誰かが前のチャットバブルに返信した場合、一部のチャットアプリでは返信と元のチャットバブルへのリンクが表示され、そのリンクをクリックすると元のメッセージがあった場所に移動します。

RecyclerViewにLayoutManagerが追加される前、かつRecyclerView.Adapterが存在する前にこのメソッドを呼び出すと、scrollToPosition(n:Int)は単純に無視されます。

RecyclerView コンポーネント間の通信

基礎知識その4 – コールバック

RecyclerView は、その動作において多くの要素が複雑に絡み合っています。まず、ビューを直線状に並べるかグリッド状に並べるかを指示する LayoutManager と連携する必要があります。次に、アイテム (contactList) をビュー (OneContactView) に変換し、さらに RecyclerView が提供するメソッドを使って処理できる ViewHolder (OneContactViewHolder) に変換するアダプタと連携する必要があります。

RecyclerViewの原材料は、OneContactViewなどのViewとデータソースです。

contactList:Array<Contact>

RecyclerViewが目指すものを理解するために、まずはシンプルなシナリオから始めましょう。ユーザーに表示したい連絡先が1000件ある静的な配列の場合、基本的なケースは理解しやすいでしょう。RecyclerViewの仕組みが真価を発揮するのは、リストが静的ではなくなったときです。動的なリストの場合、リストに項目を追加したり、リストから項目を削除したりしたときに、画面上のビューがどのように変化するかを考慮する必要があります。

RecyclerView.LayoutManager

ビューのレイアウト方法(直線的かグリッド的か)を決定する以外にも、LayoutManager は内部で多くの処理を行っています。ping リサイクル業者は、いつリサイクルを行うべきかを知っている。

それは維持する責任があるping tracLayoutManager は、画面上に現在表示されている View の k 個を一覧表示し、その情報をリサイクル メカニズムに伝達します。ユーザーが下方向にスクロールすると、LayoutManager は、上部でフォーカスが外れた View をリサイクル システムに通知し、それらが再利用できるようにします。これにより、View は上部に残ってメモリを消費したり、新しい View を作成するためだけに破棄されたりすることがなくなります。

これは、LayoutManager が保持する必要があることを意味します tracユーザーがリストをスクロールしている位置を把握します。これは、インデックスに基づいた位置リストを使用することで実現されます。つまり、最初の項目は0から始まり、リスト内の項目の数に合わせて増加します。

例えば、100個のアイテムがあるリストのうち、最初に10個のアイテムを表示できる場合、LayoutManagerはView-0からView-9までがフォーカスされていることを認識します。スクロールしていくと、LayoutManagerはフォーカスが外れたビューを計算できるようになります。

LayoutManager はこれらのビューをリサイクル メカニズムに解放して再利用できるようにします (新しいデータをバインドできます。たとえば、ビューの連絡先データを削除し、次のセグメントの新しい連絡先データでプレースホルダーを置き換えることができます)。

リストが静的な場合は問題ありませんが、RecyclerView の最も一般的な使用例の 1 つは、オンラインのエンドポイントやセンサーからデータが取得される動的なリストです。データが追加されるだけでなく、リスト上のデータが削除されたり更新されたりすることもあります。

データの動的な状態は、LayoutManager の動作を理解するのを非常に困難にする可能性があります。そのため、LayoutManager は、リサイクル コンポーネントが使用するリストとは別に、アイテムと位置に関する独自のリストを保持します。これにより、レイアウト処理が正しく実行されることが保証されます。

同時に、RecyclerView の LayoutManager は、保持しているデータを誤って表示しないようにする必要があります。正しく動作するために、LayoutManager は一定間隔 (60 ミリ秒) で RecyclerView.Adapter と同期し、リスト項目に関する情報 (追加、更新、削除、または位置の変更など) を共有します。LayoutManager はこの情報を受け取ると、必要に応じて画面上のコンテンツを再編成し、変更内容に合わせます。

RecyclerView を扱う多くのコア操作は、RecyclerView.LayoutManager と、静的または動的なデータのリストを格納する RecyclerView.Adapter 間の通信を中心に展開されます。

さらに、RecyclerView には、イベントをリッスンするために使用できるメソッドが用意されています。たとえば、RecyclerView.Adapter がリストからコンテンツ (連絡先など) を ViewHolder にバインドして、画面に情報を表示するために使用されるときの onBindViewHolder などです。

もう1つはonCreateViewHolderで、RecyclerView.AdapterがOneContactViewのような通常のViewを受け取り、それをRecyclerViewが操作できるViewHolderアイテムに変換したときに通知されます。

リサイクルを可能にするコアメカニズムとは別に、RecyclerViewはリサイクルに影響を与えずに動作をカスタマイズする方法を提供します。ビューの再利用により、onClickイベントへの反応など、静的ビューで通常行うような一般的な操作が難しくなります。

ご存じのとおり、 RecyclerView.LayoutManager ユーザーにビューを表示するものには、一時的に、 RecyclerView.Adapter リストがデータベースに保存されているか、ソースからストリーミングされているかのいずれかです。ビューにOnClickイベントを直接配置すると、誤った連絡先が削除されるなど、予期しない動作が発生する可能性があります。

Gradle

RecyclerViewを使用するには、build.gradleファイルに依存関係として追加する必要があります。以下の例では、この記事で紹介されている最新バージョンである「androidx.recyclerview:recyclerview:1.1.0」を使用しています。

依存関係を追加した後、 Gradle ファイルを開くと、 Android Studio 変更を同期させる方法は次のとおりです。 Gradle デフォルト設定のみの空のプロジェクトに RecyclerView を追加した後、ファイルがどのように動作するかを確認します。

apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.2"
    defaultConfig {
        applicationId "com.guru99.learnrecycler"
        minSdkVersion 17
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),
             'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.core:core-ktx:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

    implementation "androidx.recyclerview:recyclerview:1.1.0"
}

現時点ではレイアウトファイルは1つしかありません。まずは、RecyclerViewを使って果物の名前一覧を画面に表示する簡単な例から始めましょう。

アイテム一覧

MainActivity ファイルに移動し、セットアップ時に生成された onCreate() メソッドの直前に、果物の名前を含む配列を作成します。

package com.guru99.learnrecycler

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

class MainActivity : AppCompatActivity() {

    var fruitNames:Array<String> = arrayOf<String>("Banana", "Mango", "Passion fruit", "Orange", "Grape")

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}

次の目標は、このリストをRecyclerViewを使って画面に表示することです。そのためには、レイアウトが格納されているレイアウトディレクトリに移動し、果物を1つ表示するビューを作成します。

リスト内の各項目に使用されるレイアウト

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/fruitName"
    />
</TextView>

上記のTextViewでは、ビューを識別するために使用するidフィールドを追加しました。このフィールドはデフォルトでは生成されません。データと一致するように、TextViewにfruitNameというidを設定しました。データはTextViewにバインドされます。

RecyclerView をメイン レイアウトに追加する

同じアクティビティには、デフォルトで生成された main_layout.xml レイアウト ファイルがあります。空のプロジェクトを選択した場合、 XML ConstraintLayout を含み、その中に「Hello」というテキストが表示された TextView があります。すべてのコンテンツを削除し、レイアウトには以下のように RecyclerView のみが含まれるようにします。

<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/fruitRecyclerView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" />

また、RecyclerViewにid属性を追加しました。これは、コード内でRecyclerViewを参照する際に使用します。

android:id="@+id/fruitRecyclerView"

次に、MainActivity ファイルに戻ります。作成した ID を使用して、先ほど作成した View を参照できます。まず、findViewById() メソッドを使用して RecyclerView を参照します。 AndroidこれはonCreate()メソッド内で行います。onCreate()メソッドは次のようになります。

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView)
}

ViewHolderを作成する

次に、RecyclerView.ViewHolderを作成します。これは、Viewを受け取ってViewHolderに変換する役割を担い、RecyclerViewはこのViewHolderを使ってアイテムを表示します。この処理は、onCreate()メソッドの直後に行います。

package com.guru99.learnrecycler

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import androidx.recyclerview.widget.RecyclerView

class MainActivity : AppCompatActivity() {

    var fruitNames:Array<String> = arrayOf<String>("Banana", "Mango", "Passion fruit", "Orange", "Grape")

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView)
    }

    class FruitViewHolder(fruitView: View): RecyclerView.ViewHolder(fruitView)

}

RecyclerViewAdapter を作成する

次に、RecyclerView.Adapter クラスを継承する FruitArrayAdapter クラスを作成します。作成する FruitArrayAdapter は、以下の処理を担当します。フルーツ配列からフルーツ名を取得し、one_fruit_view.xml ビューを使用して ViewHolder を作成し、そのフルーツを ViewHolder にバインドし、作成した View にコンテンツを動的にバインドします。

package com.guru99.learnrecycler

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import androidx.recyclerview.widget.RecyclerView

class MainActivity : AppCompatActivity() {

    var fruitNames:Array<String> = arrayOf<String>("Banana", "Mango", "Passion fruit", "Orange", "Grape")

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView)
    }

    class FruitViewHolder(fruitView: View): RecyclerView.ViewHolder(fruitView)

    class FruitArrayAdapter(var fruitArray: Array<String>) : RecyclerView.Adapter<FruitViewHolder>()
}

Android Studio FruitArrayAdapter に赤い波線が追加され、RecyclerView が配列を ViewHolder に接続するために使用できるメソッドを実装する必要があることが示されます。

class FruitListAdapter(var fruitArray: Array<String>) : RecyclerView.Adapter<FruitViewHolder>() {
     override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitViewHolder {

     }

     override fun getItemCount(): Int {

     }

     override fun onBindViewHolder(holder: FruitViewHolder, position: Int) {

     }
 }

生成されたコードの中で最も簡単な部分であるgetItemCount()メソッドから始めましょう。配列のsizeプロパティを呼び出すことで、配列内のアイテム数を取得する方法は既にわかっています。

class FruitListAdapter(var fruitArray: Array<String>) : RecyclerView.Adapter<FruitViewHolder>() {
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitViewHolder {

        }

        override fun getItemCount(): Int {
         return fruitArray.size
        }

        override fun onBindViewHolder(holder: FruitViewHolder, position: Int) {

        }
    }

次に、onCreateViewHolder メソッドを実装します。ここで、RecyclerView は FruitViewHolder の構築を手伝うように求めてきます。思い出してみてください。FruitViewHolder クラスは次のようになっていました。

class FruitViewHolder(fruitView: View): RecyclerView.ViewHolder(fruitView)

これには、私たちが作成した XML ファイル one_fruit_view.xml という fruitView が必要です。この XML への参照を作成し、次のように View に変換できます。

class FruitListAdapter(var fruitArray: Array<String>) : RecyclerView.Adapter<FruitViewHolder>() {

        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitViewHolder {

            var fruitView = LayoutInflater.from(parent.context).inflate(R.layout.one_fruit_view, parent, false)

            var fruitViewHolder = FruitViewHolder(fruitView)

            return fruitViewHolder

        }

        override fun getItemCount(): Int {
            return fruitArray.size
        }

        override fun onBindViewHolder(holder: FruitViewHolder, position: Int) {

        }
    }

残りの部分は、onBindViewHolder のオーバーライドです。

fun onBindViewHolder(holder: FruitViewHolder, position: Int)

RecyclerView.Adapter は位置の整数値を要求します。この値を使用してリストからアイテムを取得します。また、ホルダーも提供されるため、fruitArray から取得したアイテムを、ビューホルダー内に保持されている View にバインドできます。ViewHolder 内に保持されている View には、ViewHolder.itemView フィールドからアクセスできます。ビューを取得したら、先ほど作成した fruitName という ID を使用してコンテンツを設定できます。

override fun onBindViewHolder(holder: FruitViewHolder, position: Int) {

          var ourFruitTextView = holder.itemView.findViewById<TextView>(R.id.fruitName)

          var aFruitName = fruitArray.get(position)

          ourFruitTextView.setText(aFruitName)
      }

これでFruitArrayAdapterが完成し、以下のようになります。

class FruitListAdapter(var fruitArray: Array<String>) : RecyclerView.Adapter<FruitViewHolder>() {

        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitViewHolder {

            var fruitView = LayoutInflater.from(parent.context).inflate(R.layout.one_fruit_view, parent, false)

            var fruitViewHolder = FruitViewHolder(fruitView)

            return fruitViewHolder
        }

        override fun getItemCount(): Int {
            return fruitArray.size
        }

        override fun onBindViewHolder(holder: FruitViewHolder, position: Int) {

            var ourFruitTextView = holder.itemView.findViewById<TextView>(R.id.fruitName)

            var aFruitName = fruitArray.get(position)

            ourFruitTextView.setText(aFruitName)
        }
    }

最後に、RecyclerViewの残りの部分を配線する準備が整いました。これは、RecyclerViewにリストの内容をどのように表示するか(LinearLayoutManagerを使用して線形に表示するか、GridLayoutManagerまたはStaggeredGridLayoutManagerを使用してグリッド状に表示するか)を指示するLayoutManagerを作成するものです。

レイアウトマネージャーの作成

onCreate関数に戻って、LayoutManagerを追加します。

override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContentView(R.layout.activity_main)

       var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView)

       var fruitLinearLayout = LinearLayoutManager(this)

       myFruitRecyclerView.layoutManager =fruitLinearLayout

   }

アダプターをアイテムにフックし、RecyclerView に設定します

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView)

        var fruitLinearLayout = LinearLayoutManager(this)

        myFruitRecyclerView.layoutManager =fruitLinearLayout

        var fruitListAdapter = FruitListAdapter(fruitNames)

        myFruitRecyclerView.adapter =fruitListAdapter
    }

また、fruitListAdapter インスタンスを作成し、果物名の配列を渡しました。これで基本的にすべて完了です。MainActivity.kt ファイル全体は以下のようになります。

package com.guru99.learnrecycler

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView

class MainActivity : AppCompatActivity() {

    var fruitNames:Array<String> = arrayOf<String>("Banana", "Mango", "Passion fruit", "Orange", "Grape")

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView)

        var fruitLinearLayout = LinearLayoutManager(this)

        myFruitRecyclerView.layoutManager =fruitLinearLayout

        var fruitListAdapter = FruitListAdapter(fruitNames)

        myFruitRecyclerView.adapter =fruitListAdapter
    }

    class FruitViewHolder(fruitView: View): RecyclerView.ViewHolder(fruitView)

    class FruitListAdapter(var fruitArray: Array<String>) : RecyclerView.Adapter<FruitViewHolder>() {

        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitViewHolder {

            var fruitView = LayoutInflater.from(parent.context).inflate(R.layout.one_fruit_view, parent, false)

            var fruitViewHolder = FruitViewHolder(fruitView)

            return fruitViewHolder
        }

        override fun getItemCount(): Int {
            return fruitArray.size
        }

        override fun onBindViewHolder(holder: FruitViewHolder, position: Int) {

            var ourFruitTextView = holder.itemView.findViewById<TextView>(R.id.fruitName)

            var aFruitName = fruitArray.get(position)

            ourFruitTextView.setText(aFruitName)
        }
    }
}

プロジェクトをダウンロードする

よくあるご質問

RecyclerViewは、ListViewの後継として、より柔軟で効率的なビューアです。ViewHolderパターンを採用し、レイアウトマネージャを介して線形、グリッド、千鳥配置のレイアウトをサポートし、組み込みのアイテムアニメーション機能も備えています。一方、ListViewはこれらの機能をデフォルトでは提供していません。

GridViewは固定グリッドにアイテムを表示するだけです。RecyclerViewはGridLayoutManagerを使用してグリッドを再現できますが、線形レイアウトや千鳥配置レイアウト、ビューのリサイクル、アニメーションもサポートしているため、GridViewよりもはるかに汎用性が高いです。

この RecyclerView の例は Kotlin で書かれています。Kotlin は、 Google の Android 開発。同じ RecyclerView、アダプター、レイアウトマネージャ API は、 Java.

ViewHolder または onBindViewHolder 内にクリックリスナーをアタッチし、インターフェースコールバックを介してアクティビティまたはフラグメントに公開します。ViewHolder の位置を使用することで、クリックが誤ったリサイクルアイテムにバインドされるのを防ぐことができます。

DiffUtilは、2つのリスト間の差分を計算し、最小限の更新セットをアダプタに送信するユーティリティクラスです。動的なリスト変更を効率的に処理し、要素の追加、削除、移動を自動的にアニメーション表示します。

Jetpack Compose は、新規画面の作成に RecyclerView の代わりに LazyColumn と LazyRow を使用します。ただし、RecyclerView は既存の View ベースのプロジェクトで広く使用されており、相互運用 API を介して Compose に組み込むことができます。

はい。GitHub CopilotやGPTベースのツールなどのAIアシスタントは、簡単なプロンプトからRecyclerViewアダプター、ViewHolder、onBindViewHolderの定型コードを作成できます。ただし、開発者は生成されたコードの正確性とパフォーマンスを必ず確認してください。

機械学習 Android アプリはRecyclerViewを使用して、おすすめ情報、検索ランキング、画像分類などの動的な出力を表示します。アダプターはモデルの結果をリスト項目にバインドするため、大規模で頻繁に変化するデータセットでもスムーズにスクロールできます。