Android RecyclerView: とは何か、簡単な例で学ぶ
RecyclerView とは何ですか Android?
この リサイクラービュー GridView と ListView のより柔軟で高度なバージョンのウィジェットです。 これは、限られた数のビューを維持することで効率的にスクロールできる大規模なデータセットを表示するためのコンテナーです。 RecyclerView ウィジェットは、ネットワーク イベントまたはユーザー アクションに応じて要素が実行時に変更されるデータ コレクションがある場合に使用できます。
ビュー
この Android プラットフォームは、View クラスと ViewGroup クラスを使用して画面上に項目を描画します。これらのクラスは抽象クラスであり、ユースケースに合わせてさまざまな実装に拡張されます。たとえば、TextView には、画面上にテキスト コンテンツを表示するという単純な目的があります。 EditText は同じ View クラスから拡張されており、ユーザーがデータを入力できるようにするための機能が追加されています。
独自のカスタム ビューを作成して、ユーザー インターフェイスを開発する際の柔軟性を高めることができます。 View クラスは、画面上に描画するためにオーバーライドできるメソッドと、幅、高さ、およびビューを希望どおりに動作させるためにビューに追加する独自のカスタム属性などのパラメーターを渡す手段を提供します。
ビューグループ
ViewGroup クラスは View の一種ですが、単に表示するだけの単純な View クラスとは異なり、ViewGroup は複数のビューを XNUMX つのビューにまとめて全体として参照できる機能を提供します。 この場合、他の単純なビュー (viewGroups も追加できます) を追加する最上位に作成されたビューは「親」と呼ばれ、内部に追加されたビューは「子」と呼ばれます。
View を配列としてイメージ化し、ViewGroup を配列の配列としてイメージ化できます。 配列の配列が配列自体であることを考えると、ViewGroup を View のように扱うことができることがわかります。
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 anology, we can now group views //together and the structure that would hold that would be what we call the ViewGroup
ViewGroup を使用すると、ビュー内で子要素をどのように配置するか (垂直に配置するか、水平に配置するかなど) を定義することもできます。View 内でのインタラクションにはさまざまなルールを設定できます。たとえば、TextView が連続する場合は 12dp の距離が必要ですが、TextView が続く ImageView の場合は 5dp の距離が必要です。
これは、独自の ViewGroup を最初から開発している場合に当てはまります。これらの設定を簡単にするには、 Android には、これらの構成を入力するために使用できる LayoutParams というクラスが用意されています。
Android ドキュメント 独自の ViewGroup を構成するときに実装するいくつかのデフォルト パラメータを提供します。一般的なパラメータには、幅、高さ、余白に関するものがあります。デフォルトでは、これらの構成は高さに対して android:layout_height という構造を持ちます (例: android:layout_width)。この点で、ViewGroup を作成するときに、ViewGroup の動作方法に固有の LayoutParams をさらに作成できます。
Android には、必要な多くの一般的なタスクを実行するために使用できるデフォルトのビューとビューグループが付属しています。例として挙げたのは TextView です。これは、高さ、幅、textSize などの構成可能な要素を備えたシンプルなビューです。前述したように、特に画像と EditText を表示するための ImageView があります。 Android ビューを追加して期待どおりの動作を得ることができるカスタム ViewGroups もあります。
LinearLayout
LinearLayout を使用すると、View アイテムを追加できます。LinearLayout は、画面上でのレイアウト方法を指定する方向属性です。また、内部のビューのルールを指定する LinearLayout.LayoutParams もあります。たとえば、属性 android:center_horizontal は、ビューを水平軸に沿って中央に配置しますが、android:center_vertical は、ビューのコンテンツを垂直軸に沿って中央に配置します。
センタリングを理解するためのいくつかの画像を次に示します。 これを 200 ピクセル x 200 ピクセルのスペース内の単純な TextView とみなします。属性を中央に配置すると、次のように動作します。
アンドロイド:中央_水平

アンドロイド:中央垂直

アンドロイド:センター

RecyclerView のコア コンポーネント

RecyclerView の重要なコンポーネントは次のとおりです。
RecyclerView.Adapter
基礎 #1 – アダプターのパターン
アダプタは、システムまたはデバイスの属性を、互換性のないデバイスまたはシステムの属性に変換するデバイスです。信号または電力の属性を変更するものもあれば、コネクタの物理的形状を別のコネクタに適合させるだけのものもあります。
アダプターを説明するために実際に見つかる簡単な例は、デバイスを接続する必要があるが、それらの接続ポートが互いに一致しない場合です。 これは、異なる種類のソケットを使用する別の国を訪問した場合に当てはまります。 携帯電話やノートパソコンの充電器を持ち歩いている場合、電源コンセントに接続することができません。 しかし、諦めるのではなく、電源コンセントと充電器の間に接続して充電を可能にするアダプターを入手するだけです。
これは、タスクを実行するために XNUMX つのデータ構造を接続したいものの、デフォルトのポートが相互に通信する方法を持たないプログラミングの場合に当てはまります。
デバイスと充電器の簡単な例を使用します。 充電器のインスタンスが XNUMX つあります。 アメリカのものとイギリスのもの
class AmericanCharger() { var chargingPower = 10 } class BritishCharger(){ var charginPower = 5 }
次に XNUMX つのデバイスを作成します
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 } }
この場合、私たちの類推に基づいて、AmericanDevice を使用するときに何らかの理由で BritishCharger を使用する必要がある、またはその逆の場合があります。
プログラミングの世界では、これは通常、同じ機能を提供するライブラリを混在させる場合に発生します (この文脈では、共有機能は課金されます)。 これを可能にする方法を見つける必要があります。
この例えに従うと、電気店に行って、イギリスの充電器を使ってアメリカのデバイスを充電できるアダプタを購入する必要があります。プログラミングの観点から言えば、アダプタの製造元は私たちになります。
もう一方の作成に必要なパターンと正確に一致するアダプターを作成します。以下のようにクラスとして実装していきます。これは必ずしもクラスである必要はなく、アダプター パターンが一般的に行うことを強調する関数にすることもできます。ほとんどの用途に一致するクラスを使用します。 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 charingPower:Int = charger.chargingPower / 2 var newBritishCharger = new BritishCharger() newBritishCharger.chargingPower = theAmericanCharger.chargingPower/2 return newBritishCharger } }
プログラミングの世界では、ソケットの違いは、充電に使用される内部メソッドの違いに似ています。 充電方式が異なると使用できなくなります。
var myBritishDevice = new BritishDevice() var americanChargerIFound = new AmericanCharger()
AmericanDevice は AmericanCharger のみを受け入れるため、myBritishDevice で americanChargerIFound を使用してcharge() メソッドを呼び出そうとすると機能しません。
だからこんなことは無理だよ
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(theAmericanCharger) //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 には、ビューが内部でどのようにレイアウトされるかを記述するタスクがあります。
比較のために、Linearlayout ViewGroup を使用する場合に必要なユースケースは、項目を垂直または水平に配置できることです。 これは、線形レイアウトが画面上にどのように配置されるかを示す方向属性を追加することで簡単に実装できます。 これを行うには、次のようにします。 android:orientation=VERTICAL|HORIZONTAL
属性。
GridLayout と呼ばれる別の ViewGroup もあります。これは、ビューを長方形のグリッド構造に配置する場合に使用します。 これには、アプリ ユーザーに提示するデータを利用しやすくするなどの理由が考えられます。 設計上、GridLayout では、グリッドの寸法を定義できる構成 (たとえば、4×4 グリッド、3×2 グリッドなど) を使用することで、この目標を達成するのに役立つ構成が可能になります。
RecyclerView.ViewHolder
ViewHolder は、RecyclerView から拡張された抽象クラスです。 ViewHolder は、RecyclerView のリサイクル機構が私たちの知らないさまざまな参照を変更した後でも、RecyclerView に配置したビューを参照するのに役立つ共通メソッドを提供します。
大きなリスト
RecyclerView は、非常に大規模なビューのセットをユーザーに提示したいときに使用されますが、同時にユーザーの負担を軽減します。 RAM 作成されたビューのすべてのインスタンスについて、デバイス上で。
連絡先リストを例にとると、リスト内の連絡先 1 つがどのように表示されるかは大体わかります。次に、連絡先リストのさまざまなデータを入力するスロットを備えたテンプレート レイアウト (実際にはビュー) を作成します。以下は、全体の目的を説明する疑似コードです。
//OneContactView <OneContact> <TextView>{{PlaceHolderForName}}</TextView> <TextView>{{PlaceHolderForAddress}}</TextView> <ImageView>{{PlaceHolderForProfilePicture}}</ImageView> <TextView>{{PlaceHolderForPhoneNumber}}</TextView> </OneContact>
このような性質の ContactList が得られます。
<ContactList> </ContactList>
もしそうなら、コンテンツをハードコーディングしていたことになり、アプリを書き直さずに新しいコンテンツをリストに追加するプログラム的な方法はありません。 私たちにとって幸運なことに。 ViewGroup へのビューの追加は、 addView(view:View)
方法。
たとえそうであったとしても、RecyclerView が子ビューを追加する方法は異なります。
私たちのユースケースでは、連絡先の長いリストが必要になります。 リスト内の連絡先ごとに、OneContactView を作成し、Contact クラスのフィールドと一致するようにビュー内にデータを設定する必要があります。 ビューを取得したら、それを 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 を追加できます。 したがって、このシナリオでは、接続したいものの一致しないものが XNUMX つあります。 ここでアダプターが登場します。RecyclerView は、私たちのアダプターとよく似たアダプターを提供します。 AmericanToBritishChargerAdapter()
これにより、BritishDevice では使用できなかった AmericanCharger を、実際の電源アダプターに似た、使用可能なものに変換できるようになりました。
このシナリオでは、アダプターは連絡先の配列とビューを取得し、そこから RecyclerView が受け入れる ViewHolders を生成します。
RecyclerView は、RecyclerView.Adapter クラスを通じてアダプタを作成するために拡張できるインターフェイスを提供します。 このアダプタの内部には、RecyclerView が操作する ViewHolder クラスを作成する方法があります。 つまり、前と同じ状況ですが、アダプターが XNUMX つ追加されています。
連絡先の配列、XNUMX つの連絡先を表示するビュー OneContactView があります。 RecyclerView は、リサイクル サービスを提供する View のリストですが、ViewHolders のみを受け入れます。
ただし、このシナリオでは、内部に ViewHolders を作成するメソッドを持つ RecyclerView.Adapter クラスが存在します。
fun createViewHolder(@NonNull parent: ViewGroup, viewType: Int): ViewHolder
RecyclerView.ViewHolder は、View を引数として受け取り、それを 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
上の例では、XNUMX 匹の動物がいます。 万が一、話させるメソッドを追加したかったが、ライブラリの作成者が面白くなかった場合でも、方法は見つかります。 必要なのは、Animal クラスのラッパーです。 これを行うには、Animal をクラスのコンストラクターとして取り込みます。
class SpeechPoweredAnimalByWrapper(var myAnimal:Animal){ fun sound(){ myAnimal.sound() } speak(){ println("Hello, my name is ${myAnimal.name}") } }
これで、動物のインスタンスを SpeechPoweredAnimalByWrapper に渡すことができます。 その上で sound() メソッドを呼び出すと、渡された Animal sound() メソッドが呼び出されます。 また、追加の speech() メソッドもあります。これは、渡された動物に追加する新しい機能としてカウントされます。これは次のように使用できます。
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"
このパターンを使用すると、クラスを取得し、機能を追加できます。 渡す必要があるのは、クラス インスタンスと、ラッピング クラスで定義された新しいメソッドだけです。
上記の例では、具象クラスを使用しました。抽象クラスで同じことを実装することも可能です。SpeechPoweredAnimalByWrapper クラスを抽象クラスに変更するだけで完了です。クラス名を短くして読みやすくします。
abstract class SpeechPowered(var myAnimal:Animal){ fun sound(){ myAnimal.sound() } speak(){ println("Hello, my name is ${myAnimal.name}") } }
前と同じですが、別の意味になります。 通常のクラスでは、cat1 と Dog1 を作成したのと同じ方法でクラスのインスタンスを作成できます。 ただし、抽象クラスはインスタンス化することを目的としたものではなく、他のクラスを拡張することを目的としています。 では、新しい SpeechPowered(var myAnimal:Animal) 抽象クラスをどのように使用すればよいでしょうか。 これを拡張し、その機能を獲得する新しいクラスを作成することでこれを使用できます。
この例では、クラスを拡張するクラス 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 クラスは、動物に speech メソッドを追加したのと同じように、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 を使用して Contacts 配列を ContactsView に接続できるアダプタがあります。
ビューの追加
ViewGroup は ViewGroup を自動的に再描画するのではなく、特定のスケジュールに従います。デバイスによっては、10 ミリ秒または 100 ミリ秒ごとに再描画される場合もありますが、ViewGroup にビューを追加するときに 1 分などの不合理な数値を選択した場合は、1 分後に ViewGroup が「更新」されて変更が表示されます。
RecyclerView.Recycler
基礎その3。 キャッシング
定期的に更新を行う最良の例の XNUMX つはブラウザーです。 たとえば、訪問しているサイトが静的で、コンテンツを動的に送信していない場合、変更を確認するには更新を続ける必要があるとします。
この例では、問題のサイトが twitter であると想像してみましょう。 一連の静的なツイートがリストされており、新しいツイートを表示するには更新ボタンをクリックしてコンテンツを再取得するしか方法がありません。
画面全体を再塗装するには、明らかに費用がかかります。 もしそうだと想像してみると、電話プロバイダーの帯域幅は限られていました。 また、ツイート リストには多くの画像やビデオが含まれており、更新するたびにページのすべてのコンテンツを再ダウンロードするにはコストがかかります。
すでにロードされているツイートを保存し、次のリクエストがすでに持っているツイートを発言できるようにする方法が必要です。 したがって、すべてを再ダウンロードするのではなく、新しいツイートのみを取得し、ローカルに保存されていたツイートがもう存在しないかどうかを確認して、ローカルで削除できるようにします。 私たちが説明しているのはキャッシングと呼ばれます。
当社が保有するコンテンツに関して当社が Web サイトに送信する情報はメタデータと呼ばれます。 したがって、本当の意味で、私たちは単に「あなたのサイトをロードしたい」と言っているのではなく、「あなたのサイトをロードしたいのですが、これは前回ロードしたときにすでに保存されていたコンテンツの一部です。それを使用してください」と言っています。そこにないものだけを送信するので、リソースがあまりないので帯域幅をあまり使用しません。」
レイアウト呼び出し - ツイートリストはクレイジーに違いありません
レイアウト呼び出しの例は、scrollToPosition です。
これはチャット アプリなどによく見られる例です。 チャット スレッド内の誰かが以前のチャット バブルに返信すると、一部のチャット アプリには返信とチャット バブルへのリンクが含まれ、クリックすると元のメッセージがあった場所に移動します。
この場合、RecyclerView に LayoutManager を追加する前、および RecyclerView.Adapter を作成する前にこのメソッドを呼び出します。scrollToPosition(n:Int) は単に無視されます。
RecyclerView コンポーネント間の通信
基礎工事その4。 コールバック
RecyclerView には、その動作中に多くの可動部分があります。 これは、ビューを線形またはグリッドで整理する方法を指示する LayoutManager を処理する必要があります。 これは、アイテム contactList を Views OneContactView に変換し、次に、RecyclerView が提供するメソッド内で動作する ViewHolders OneContactViewHolder に変換するジョブを実行するアダプターを処理する必要があります。
RecyclerView の原材料は、OneContactView などのビューとデータ ソースです。
contactList:Array<Contact>
RecyclerView が何を達成しようとしているのかを理解するための出発点として、単純なシナリオを使用しました。
ユーザーに表示したい 1000 件の連絡先の静的配列がある場合の基本的なケースは、簡単に理解できます。
リストが静的でなくなると、RecyclerView 機構が本格的に機能し始めます。
動的リストの場合、リストに項目を追加するとき、またはリストから項目を削除するときに、画面上のビューに何が起こるかを考える必要があります。
RecyclerView.LayoutManager
ビューを線形またはグリッドのどちらでレイアウトするかを決定することとは別に、LayoutManager は、Recycler がいつリサイクルを行うべきかを判断できるように、内部で多くの作業を行います。
現在画面上に表示されているビューを追跡し、この情報をリサイクル メカニズムに伝える役割を果たします。 ユーザーが下にスクロールすると、レイアウト マネージャーは、上部でフォーカスが外れたビューをリサイクル システムに通知し、ビューがそこに残ってメモリを消費する代わりに、またはビューが破棄されて作成しなければならない代わりに再利用できるようにする責任があります。新しいもの。
これが意味するのは、LayoutManager はユーザーがリストをスクロールするときにユーザーがどこにいるかを追跡する必要があるということです。 これは、インデックスベースの位置のリストを持つことによって行われます。つまり、最初の項目は 0 から始まり、リスト内の項目の数に一致するように増加します。
たとえば 10 のリストで 100 個の項目を表示できる場合、最初は、LayoutManager は view-0 から View-9 までフォーカスがあることを認識します。スクロールすると、LayoutManager は取得されるビューを計算できます。焦点の。
LayoutManager は、これらのビューをリサイクル メカニズムに解放して再利用できるようにすることができます (新しいデータをビューにバインドできます。たとえば、ビューの連絡先データを削除し、次のセグメントからの新しい連絡先データでプレースホルダーを置き換えることができます)。
所有するリストが静的であればこれは望ましいケースですが、RecyclerView を使用する最も一般的な使用例の XNUMX つは、データがオンライン エンドポイントまたはセンサーから取得される可能性がある動的リストを使用する場合です。 データが追加されるだけでなく、リスト上のデータが削除または更新される場合もあります。
データの動的な状態により、LayoutManager について推論することが非常に困難になる場合があります。 このため、LayoutManager は、Recycling コンポーネントが使用するリストとは別に、項目と位置に関する独自のリストを維持します。 これにより、レイアウト作業が正しく行われることが保証されます。
同時に、RecyclerView の LayoutManager は、保持しているデータを誤って表示しないようにします。正しく動作するために、LayoutManager は指定された間隔 (60 ミリ秒) で RecyclerView.Adapter と同期し、リスト項目に関する情報 (つまり、項目の追加、更新、削除、ある位置から別の位置への移動) を共有します。LayoutManager は、この情報を受け取ると、必要に応じて変更に合わせて画面上のコンテンツを再編成します。
RecylerView を扱うコア操作の多くは、静的または動的データのリストを格納する RecyclerView.LayoutManager と RecyclerView.Adapter 間の通信を中心に行われます。
さらに、RecyclerView は、RecyclerView.Adapter が List のコンテンツ (連絡先を ViewHolder など) にバインドするときに onBindViewHolder などのイベントをリッスンするために使用できるメソッドを提供します。これにより、Screen に情報を表示するために使用できるようになります。
もう XNUMX つは onCreateViewHolder で、RecyclerView がいつかを知らせます。アダプターは OneContactView などの通常の View を受け取り、RecyclerView が操作できる ViewHolder 項目に変換します。 RecyclerView で使用するための ViewHolder から。 onView分離
リサイクルを可能にするコアメカニズムとは別に。 RecyclerView は、リサイクルに影響を与えずに動作をカスタマイズする方法を提供します。
ビューを再利用すると、onClick イベントへの反応など、静的ビューで慣れ親しんでいる一般的な作業が困難になります。
私たちが認識しているように、 RecyclerView.LayoutManager
ユーザーにビューを表示するものには、一時的に、 RecyclerView.Adapter
これには、データベースに保存したリスト、またはソースからストリーミングしたリストが含まれます。 OnClick イベントをビューに直接配置すると、間違った連絡先の削除や変更など、予期しない動作が発生する可能性があります。
Gradle
RecyclerView を使用したい場合は、それをビルドの .gradle ファイルに依存関係として追加する必要があります。
次の例では、この記事に従って最新バージョンである実装「androidx.recyclerview:recyclerview:1.1.0」を使用しています。
依存関係を追加した後、 Gradle ファイルを開くと、 Android スタジオから Sync変化を時代に即したものにし、
これは私たちの方法です 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" }
現時点ではレイアウト ファイルは XNUMX つだけです。 まず、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 にデータと一致する ID FruitName を設定し、データにバインドします。
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 属性も追加しました。
android:id="@+id/fruitRecyclerView"
次に、MainActivity ファイルに戻ります。 作成した ID を使用して、作成したばかりのビューを参照できるようになります。
まず、RecyclerView が提供する findViewById() メソッドを使用して参照します。 Android。 これは onCreate() メソッドで行います。
onCreate() メソッドは次のようになります。
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView) }
ViewHolderを作成する
次に、ビューを取得して ViewHolder に変換する役割を担う RecyclerView.ViewHolder を作成します。RecyclerView はこれを使用して項目を表示します。
これは、楽しい 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 one_fruit_view.xml に動的にバインドします。
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 に配列を接続するために RecyclerView が使用できるメソッドを実装する必要があることを示します。
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() メソッドから始めます。 array.length メソッドを呼び出して、配列内の項目の数を取得する方法はわかりました。
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) { } }
次に、メソッド オーバーライド fun onCreateViewHolder を実装します。
ここで、RecyclerView は、FruitHolder の構築を支援するように求めます。
思い出してみると、FruitViewHolder クラスは次のようになっていました。
class FruitViewHolder(fruitView: View): RecyclerView.ViewHolder(fruitView)
XML ファイル one_fruit_view.xml として作成した FruitView が必要です。
次のように、この XML への参照を作成し、ビューに変換できます。
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) { } }
残りのビットはオーバーライドです
fun onBindViewHolder(holder: FruitViewHolder, position: Int)
RecyclerView.Adapter は、リストから項目をフェッチするために使用する位置整数を要求します。 また、fruitArray から取得した項目をビュー ホルダー内に保持されているビューにバインドできるホルダーも提供します。
ViewHoder 内に保持される View には、フィールド ViewHolder.itemView を通じてアクセスできます。 ビューを取得したら、前に作成した ID FruitName を使用してコンテンツを設定できます。
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 にリストの内容を表示する方法を指示する LayoutManager を作成しています。 LinearLayoutManager を使用して線形に表示するか、GridLayoutManager または StaggeredGridLayoutManager を使用してグリッドに表示するか。
レイアウトマネージャーの作成
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) } } }