PoolingContainerでRecyclerView内のComposeViewを上手に再利用する方法
概要
前回の記事でJetpack Composeを導入する際にComposeViewを使って、RecyclerViewを使うハイブリッドな方法は、RecyclerViewだけ/Jetpack Composeだけで利用するより、パフォーマンスがよく無さそうという結果が出ました。
ですが、前回の方法では、スクロールのたびにsetContentView()
を呼び出し、都度コンポーザブルを作っていたのでViewHolderの再利用という点でパフォーマンスが悪いのかもしれません。
で、recyclerview:1.3.0-alpha02
から、PoolingContainer
を使って再利用が可能になったようなので、試してみます。
(ただし、正直、ドキュメントがなく正しい実装方法なのかがいまいちわからず実装しています。ので、間違っている可能性もあります)
準備
以下のライブラリを入れておきます。
implementation "androidx.customview:customview:1.1.0"
implementation "androidx.customview:customview-poolingcontainer:1.0.0-beta02"
implementation "androidx.recyclerview:recyclerview:1.3.0-alpha02"
実装
まずはComposeViewを作成します。カスタムなComposeViewを作成するのでAbstractComposeView
を継承して作ります。
動的な部分のItemを保持しています。
ComposeListItemView
は、前回の記事でも書いたリストのアイテムをデザインしたコンポーザブル関数です。
class ComposeViewListItemView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyle: Int = 0
) : AbstractComposeView(context, attrs, defStyle) {
var item by mutableStateOf(Item("",""))
@Composable
override fun Content() {
key(item) {
ComposeListItemView(item)
}
}
}
layoutファイルで上記を指定しています。compose_view_pooling
というid です(=ComposeViewPooling
)。
<?xml version="1.0" encoding="utf-8"?>
<com.ko2ic.spike.ui.view.ComposeViewListItemView
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/compose_view_pooling"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
次にRecyclerView.AdapterとRecyclerView.ViewHolderの実装です。
class RecyclerViewWithPoolingComposeAdapter(private val listItems: List<Item>) :
RecyclerView.Adapter<PoolingComposeItemViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PoolingComposeItemViewHolder {
val binding = ListItemViewComposePoolingBinding.inflate(
LayoutInflater.from(parent.context), parent, false
)
return PoolingComposeItemViewHolder(binding)
}
override fun onBindViewHolder(holder: PoolingComposeItemViewHolder, position: Int) {
holder.bindView(listItems[position])
}
override fun getItemCount(): Int {
return listItems.size
}
}
class PoolingComposeItemViewHolder(
viewBinding: ListItemViewComposePoolingBinding
) : RecyclerView.ViewHolder(viewBinding.root) {
private val listItemView: ComposeViewListItemView = viewBinding.composeViewPooling
fun bindView(content: Item) {
listItemView.item = content
}
}
ポイントは、PoolingComposeItemViewHolderクラスで、ComposeViewListItemViewをフィールドで持ち、onBindViewHolder()
で、動的に変わるItemだけを渡している点です。
これで、ViewHolderを再利用するときにComposeViewListItemViewは再度作られないはずです。
結果
PoolingContainerを使っても代わりませんでした。まだbeta版なので今後改善されるか、もしくは実装の仕方が悪いのかもしれません。
PoolingContainer利用 | スクロールのたびにsetContentView()
|
---|---|
ソーシャル経済メディア NewsPicks メンバーの発信を集約しています。公式テックブログはこちら→ tech.uzabase.com/archive/category/NewsPicks
Discussion