5
1

More than 3 years have passed since last update.

【Android】MergeAdapterを使ってBanner + GridItemのパターンを実装する

Last updated at Posted at 2020-06-21

概要

巷によくあるアプリのパターンとして、画面の上に横長のバナーやレイアウトを置いて、その下に格子状のレイアウトを置くというものがあります。

SmartNews

グノシー

このデザインに対応するためのよくある実装として、一つのRecylerAdapterで、ViewHolderを複数用意して、item毎にViewHolderを切り替えるといったものがあります。
しかし、ロジックが複雑化するので、実装コストや後のメンテナンスコストが増えてしまう原因でもあります。

今回はよくあるこのパターンを、recyclerviewのalpha版に実装されているMergeAdapterを使って実装していきます。

Sample

https://github.com/alpha2048/MergeAdapterTest

スクリーンショット 2020-06-21 17.00.08.png

解説

MergeAdapterはrecyclerviewの1.2.0のalpha02から入っています。

    implementation "androidx.recyclerview:recyclerview:1.2.0-alpha03"

実装はとても簡単です

  1. Banner用のAdapterを作成
  2. GridItem用のAdapterを作成
  3. MergeAdapterに上の2つをマージする

Banner用のAdapter

class BannerAdapter() : RecyclerView.Adapter<ItemBannerViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemBannerViewHolder {
        val layoutInflater = LayoutInflater.from(parent.context)
        val binding = ItemListBannerBinding.inflate(layoutInflater, parent, false)
        return ItemBannerViewHolder(binding)
    }

    override fun onBindViewHolder(holder: ItemBannerViewHolder, position: Int) {
    }

    override fun getItemCount(): Int {
        return 1
    }
}

class ItemBannerViewHolder(val binding: ItemListBannerBinding) : RecyclerView.ViewHolder(binding.root)

GridItem用のAdapter

class GridItemAdapter (private val viewModel: MainViewModel) : RecyclerView.Adapter<ItemGridViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemGridViewHolder {
        val layoutInflater = LayoutInflater.from(parent.context)
        val binding = ItemListGridItemBinding.inflate(layoutInflater, parent, false)
        return ItemGridViewHolder(binding)
    }

    override fun onBindViewHolder(holder: ItemGridViewHolder, position: Int) {
        holder.binding.image.load(viewModel.repoItems[position].owner.avatar_url)
    }

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

}

class ItemGridViewHolder(val binding: ItemListGridItemBinding) : RecyclerView.ViewHolder(binding.root)

この2つをMergeAdapterにマージする

        val bannerAdapter = BannerAdapter()
        val gridItemAdapter = GridItemAdapter(viewModel)

        val mergeAdapter = MergeAdapter(
            bannerAdapter,
            gridItemAdapter
        )

        binding.recyclerView.apply {
            val gridLayoutManager = GridLayoutManager(context,3)
            gridLayoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
                override fun getSpanSize(position: Int): Int = if (position == 0) 3 else 1
            }
            layoutManager = gridLayoutManager
            adapter = mergeAdapter
        }

注意点として、LayoutManagerはGridLayoutManagerを使うので、バナーのように1列に表示する場合はSpanSizeの制御をきちんと入れましょう。

あとがき

簡単な実装で、性質の違うデータ、レイアウトを別々のAdapterで管理できるのはとてもよいですね!
地味にいろんなところで使えそうなので、安定版のリリースが待ち遠しいです。

参考資料

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1