アンドロイド - RecyclerViewの実装方法(AndroidX、Kotlin)

By JS | Last updated: November 23, 2018

多くのデータを一度に表示しようとすると、メモリなどの資源の問題でスラッシングまたはひどいOOM(Out of Memory)が発生することがあります。 RecyclerViewは、このようなパフォーマンスの問題を解決し、データを効率的に示すViewです。

RecyclerViewは以前から公開されたライブラリであり、今までに多く使われてきました。例がたくさんありますが、ここでは鼻間違ったとAndroidXに実装する方法を説明します。

プロジェクトの作成

Empty Activityに生成します。 android studio empty project

KotlinUse AndroidX artifactsを選択してください。(Android Studioのバージョンが3.4未満であれば、 Use AndroidX artifactsオプションがありません。移行をしたり、コードを変更してくれるとし) android studio androidx artifact

AndroidXでプロジェクトを作成していなかった場合、メニューから[Refactor] -> [Migrate to AndroidX ...]を押すとAndroidXを使用するプロジェクトに移行ができます。

RecyclerViewを使用するには、アプリgradleで依存性を追加する必要があります。

dependencies {
  ....
  implementation "androidx.recyclerview:recyclerview:1.0.0"
}

データWrapper

私たちが作るサンプルはYoutubeのサムネイルとタイトルを連続的に示してアプリです。 リサイクルロビュを作成する前にサムネイルとタイトルを管理するデータオブジェクトを作成し、開始することが良いです。

完成したサンプルコードは、GitHubにあります。 ここで、 /res/drawable/からimageで始まるファイルをダウンロードして、すべて自分のdrawableフォルダにコピーしてください。 そして strings.xmlにタイトルを追加してください。

/res/values/strings.xml

<resources>
    <string name="app_name">Sample</string>

    <string name="title01">10 Best Practices for Moving to a Single Activity</string>
    <string name="title02">Cost of a Pixel Color (Android Dev Summit 18)</string>
    <string name="title03">Foldables, App Bundles and more from Android Dev Summit 18!</string>
    <string name="title04">Fun with LiveData (Android Dev Summit 18)</string>
    <string name="title05">Keynote (Android Dev Summit 18)</string>
    <string name="title06">Modern WebView Best Practices (Android Dev Summit 18)</string>
    <string name="title07">Performance Analysis Using Systrace (Android Dev Summit 18)</string>
    <string name="title08">Preferential Practices for Preferences (Android Dev Summit 18)</string>
    <string name="title09">That’s a wrap on Android Dev Summit 2018!</string>
    <string name="title10">Vitals: Past, Present and Future (Android Dev Summit 18)</string>
</resources>

ファイルと文字列の両方を追加しました。これで、この二つのデータを管理する YoutubeItemクラスを作成してください。 データを保存してgetter/setterを提供するだけになるため、追加で実装することはできません。

YoutubeItem.kt

class YoutubeItem(val image: Drawable, val title: String) {

}

RecyclerView実装

まず、 activity_main.xmlファイルに既存にあったコードを削除して、LinearLayoutの中にRecyclerViewを追加してください。

/res/layout/activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:clipToPadding="false"
            app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
            tools:listitem="@layout/list_item"/>

</LinearLayout>

LayoutManager属性は、Viewをどの配列に示す決定するオブジェクトです。 私たちは、 LinearLayoutManagerに設定しました。

listitemプロパティは、デザインのプレビュー機能を適用する際にどのようなレイアウトを利用して示す設定することです。 list_item.xmlを作成し、以下のように実装をしてください。

/res/layout/list_item.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingLeft="10dp"
    android:paddingRight="10dp"
    android:paddingTop="15dp"
    android:paddingBottom="15dp">

    <ImageView
        android:id="@+id/thumbnail"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:adjustViewBounds="true"/>

    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        app:layout_constraintTop_toBottomOf="@+id/thumbnail"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

私たちは、RecylcerViewを利用してYoutubeItemオブジェクトを順次出力されるようにするのにね。 YoutubeItemのデータが表示されるレイアウトファイルを list_item.xmlに定義しました。 RecylcerViewはこのViewを順次出力するようになります。

今xmlはすべて生成し、鼻間違っコードを追加するだけです。 私たちが示すデータは、RecyclerViewに直接入れずAdapterに追加されます。 RecyclerViewはAdapterを介してデータを得Viewを作成します。だからAdapterオブジェクトも作成する必要があります。

RecyclerAdapterで実装する必要が関数は onCreateViewHolderonBindViewHoldergetItemCountViewHolder程度です。

  • getItemCount: 表示アイテム数がいくつあるかを示します
  • onCreateViewHolder: 表示アイテム数のViewを作成します
  • onBindViewHolder: 生成されたViewに示すデータを設定(set)してくれます
  • ViewHolder: ViewHolder単位オブジェクトでViewのデータを設定します
class RecyclerAdapter(private val items: ArrayList<YoutubeItem>) :
    RecyclerView.Adapter<RecyclerAdapter.ViewHolder>() {

    override fun getItemCount() = items.size

    override fun onBindViewHolder(holder: RecyclerAdapter.ViewHolder, position: Int) {
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
            RecyclerAdapter.ViewHolder {
    }

    class ViewHolder(v: View) : RecyclerView.ViewHolder(v) {
        fun bind(listener: View.OnClickListener, item: YoutubeItem) {
        }
    }
}

RecyclerViewが初期化されるときに onCreateViewHolderが呼び出されます。ここで、以前に実装した list_item.xmlをViewに生成します。

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
        RecyclerAdapter.ViewHolder {
    val inflatedView = LayoutInflater.from(parent.context)
            .inflate(R.layout.list_item, parent, false)
    return RecyclerAdapter.ViewHolder(inflatedView)
}

Viewが生成されると、 onBindViewHolderが呼び出されます。サムネイルとタイトルをViewに設定し、 クリックすると、トーストが発生するListenerも実装しました。

override fun onBindViewHolder(holder: RecyclerAdapter.ViewHolder, position: Int) {
    val item = items[position]
    val listener = View.OnClickListener {it ->
        Toast.makeText(it.context, "Clicked: ${item.title}", Toast.LENGTH_SHORT).show()
    }
    holder.apply {
        bind(listener, item)
        itemView.tag = item
    }
}

class ViewHolder(v: View) : RecyclerView.ViewHolder(v) {
    private var view: View = v
    fun bind(listener: View.OnClickListener, item: YoutubeItem) {
        view.thumbnail.setImageDrawable(item.image)
        view.title.text = item.title
        view.setOnClickListener(listener)
    }
}

完成された RecyclerAdapter.ktは次のとおりです。

RecyclerAdapter.kt

class RecyclerAdapter(private val items: ArrayList<YoutubeItem>) :
    RecyclerView.Adapter<RecyclerAdapter.ViewHolder>() {

    override fun getItemCount() = items.size

    override fun onBindViewHolder(holder: RecyclerAdapter.ViewHolder, position: Int) {
        val item = items[position]
        val listener = View.OnClickListener {it ->
            Toast.makeText(it.context, "Clicked: ${item.title}", Toast.LENGTH_SHORT).show()
        }
        holder.apply {
            bind(listener, item)
            itemView.tag = item
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):
            RecyclerAdapter.ViewHolder {
        val inflatedView = LayoutInflater.from(parent.context)
                .inflate(R.layout.list_item, parent, false)
        return RecyclerAdapter.ViewHolder(inflatedView)
    }

    class ViewHolder(v: View) : RecyclerView.ViewHolder(v) {

        private var view: View = v

        fun bind(listener: View.OnClickListener, item: YoutubeItem) {
            view.thumbnail.setImageDrawable(item.image)
            view.title.text = item.title
            view.setOnClickListener(listener)
        }
    }
}

MainActivity.ktでRecyclerViewとAdapterを設定して、データを追加します。 ArrayListにYoutubeItemオブジェクトを作成して追加しました。

ArrayListを引数としてRecyclerAdapterを生成し、RecyclerViewにAdapterを設定しました。 今RecyclerViewはAdapterを介してデータを取得し、そのデータに基づいてViewを作成して、画面に表示されます。

MainActivity.kt

class MainActivity : AppCompatActivity() {

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

        val list = ArrayList<YoutubeItem>()
        list.add(YoutubeItem(getDrawable(R.drawable.image01)!!, getString(R.string.title01)))
        list.add(YoutubeItem(getDrawable(R.drawable.image02)!!, getString(R.string.title02)))
        list.add(YoutubeItem(getDrawable(R.drawable.image03)!!, getString(R.string.title03)))
        list.add(YoutubeItem(getDrawable(R.drawable.image04)!!, getString(R.string.title04)))
        list.add(YoutubeItem(getDrawable(R.drawable.image05)!!, getString(R.string.title05)))
        list.add(YoutubeItem(getDrawable(R.drawable.image06)!!, getString(R.string.title06)))
        list.add(YoutubeItem(getDrawable(R.drawable.image07)!!, getString(R.string.title07)))
        list.add(YoutubeItem(getDrawable(R.drawable.image08)!!, getString(R.string.title08)))
        list.add(YoutubeItem(getDrawable(R.drawable.image09)!!, getString(R.string.title09)))
        list.add(YoutubeItem(getDrawable(R.drawable.image10)!!, getString(R.string.title10)))

        val adapter = RecyclerAdapter(list)
        recyclerView.adapter = adapter
    }
}

今のアプリを実行してみてください。データのユーチューブサムネイルとタイトルが順次出力がされます。

android recyclerview

分周器(Divider)付け

それぞれのYouTubeデータの間に区切り線を入れたいと思うことができます。 RecyclerViewにDividerを追加すると、分周器が出力されます。 MainActivity.kotlinのrecyclerViewコードの下にaddItemDecorationでDividerオブジェクトを追加してください。

....
list.add(YoutubeItem(getDrawable(R.drawable.image09)!!, getString(R.string.title09)))
list.add(YoutubeItem(getDrawable(R.drawable.image10)!!, getString(R.string.title10)))

val adapter = RecyclerAdapter(list)
recyclerView.adapter = adapter

recyclerView.addItemDecoration(
    DividerItemDecoration(this, DividerItemDecoration.VERTICAL))
...

android recyclerview

再度アプリを起動して、結果を確認すると、出力されるデータの間に区切り線が追加されたことを見ることができます。

波(Ripple)効果

今データのリストをタッチすると、トーストが発生するようにリスナーを追加しました。 トーストが見えますかクリックがされたことを知ることができるために、もう少し視覚的にRipple effectが見えたらと思います。

list_item.xmlConstraintLayoutbackground属性を追加し、 ?android:attr/selectableItemBackgroundを入力します。 Viewをクリックしたときの背景の効果を設定するものです。リソースは、SDKに組み込まれているものを使用しました。必要に応じて独自に作成することができます。

<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingLeft="10dp"
    android:paddingRight="10dp"
    android:paddingTop="15dp"
    android:paddingBottom="15dp"
    android:background="?android:attr/selectableItemBackground">

    ....

実行してクリックしてみると波効果が発生します。

android recyclerview

LayoutManager変更

今までRecyclerViewにデータを出力し、分周器と波効果を入れました。 今回は LayoutManagerを変更してみどのように出力されるかを確認してみましょう。

LinearLayoutManagerに設定んですけど、GridLayoutManagerに変更ましょう。 xmlコードのみ変更してくれればされます。 activity_main.xmlのRecyclerViewでlayoutManager属性を GridLayoutManagerに設定してくれ spanCount属性を3に設定してください。

修正されたコードは、次のとおりです。

/res/layout/activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:clipToPadding="false"
            app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
            app:spanCount="3"
            tools:listitem="@layout/list_item"/>

</LinearLayout>

spanCountはGridの熱をいくつかに設定するかです。 3に設定すると、3列のデータが出力されます。

android recyclerview

まとめ

RecyclerViewを実装するには、Adapter、Divider、LayoutManagerを実装する必要があります。 開発者が自分のアプリに柔軟に適用することができるように役割が分離されて設計がされた。 基本的には、頻繁に使用されるオブジェクトは、SDKで提供をしており、必要に応じて、自分だけのオブジェクトを生成することができます。

参考

Related Posts

codechachaCopyright ©2019 codechacha