多くのデータを一度に表示しようとすると、メモリなどの資源の問題でスラッシングまたはひどいOOM(Out of Memory)が発生することがあります。
RecyclerView
は、このようなパフォーマンスの問題を解決し、データを効率的に示すViewです。
RecyclerViewは以前から公開されたライブラリであり、今までに多く使われてきました。例がたくさんありますが、ここでは鼻間違ったとAndroidXに実装する方法を説明します。
プロジェクトの作成
Kotlin
とUse AndroidX artifacts
を選択してください。(Android Studioのバージョンが3.4未満であれば、 Use AndroidX artifacts
オプションがありません。移行をしたり、コードを変更してくれるとし)
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で実装する必要が関数は onCreateViewHolder
、onBindViewHolder
、 getItemCount
、ViewHolder
程度です。
- 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
}
}
今のアプリを実行してみてください。データのユーチューブサムネイルとタイトルが順次出力がされます。
分周器(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))
...
再度アプリを起動して、結果を確認すると、出力されるデータの間に区切り線が追加されたことを見ることができます。
波(Ripple)効果
今データのリストをタッチすると、トーストが発生するようにリスナーを追加しました。 トーストが見えますかクリックがされたことを知ることができるために、もう少し視覚的にRipple effectが見えたらと思います。
list_item.xml
のConstraintLayoutにbackground
属性を追加し、 ?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">
....
実行してクリックしてみると波効果が発生します。
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列のデータが出力されます。
まとめ
RecyclerViewを実装するには、Adapter、Divider、LayoutManagerを実装する必要があります。 開発者が自分のアプリに柔軟に適用することができるように役割が分離されて設計がされた。 基本的には、頻繁に使用されるオブジェクトは、SDKで提供をしており、必要に応じて、自分だけのオブジェクトを生成することができます。
参考
- サンプルコードは、GitHubにあります
- RecyclerView - Android developer
Related Posts
- Android - 振動、Vibrator、VibrationEffectの例
- Android - TabLayoutの実装方法(+ ViewPager2)
- Android - PackageManagerにPackage情報を取得する
- Android - ACTION_BOOT_COMPLETEDイベント受信
- Android - FusedLocationProviderClientに位置情報を取得する
- Android - GPS、Network位置情報を取得する(LocationManager)
- Android - Foreground Service実行
- Android - 時間、日付、変更イベント受信
- Android - currentTimeMillis()、elapsedRealtime()、uptimeMillis()
- Android-PowerManager WakeLock
- Android - ファイル入出力の例(Read、Write、内部、外部ストレージ)
- Android - Screen On / Offイベントの受信、状態確認
- Android - 他のアプリのServiceにバインド
- Android - Handler vs Executor
- Android - Darkmode有効にする方法
- Android - hasSystemFeature()、サポートされているFeature確認
- Android - アプリの権限を確認(Permission check)
- Android - インストールされてアプリリストをインポートする
- Android App Shortcuts実装
- Android - ContentProviderを実装、および例
- Android - AIDLを利用して、Remote Serviceの実装
- Android - Uri、Scheme、SSP(Scheme Specific Part)説明
- Android - アプリのインストール、削除、イベントダウンロード(BroadcastReceiverインテントを受け取る)
- Android - SharedPreferencesに簡単なデータを保存する方法
- Android - AlarmManagerにアラームを登録する方法、および例
- Android - Quick SettingsにCustom Tile追加する方法(kotlin)
- Android - Broadcast Receiver登録およびイベントの受信方法
- Android - Runtime permissionリクエスト方法と例(kotlin)
- Android - ネットワーク(WIFI)の接続状態を確認し、変更の検出
- Mockito - static、final methodをmockingする方法
- Andriod - カスタムパーミッションを定義する方法
- RobolectricにUnit Testを作成する(kotlin)
- Android Mockitoのテストコードを作成する(kotlin)
- Android - Handlerの使用方法、および例
- Android - IntentService使用方法
- Android - JobIntentService使用方法