VOOZH about

URL: https://qiita.com/ryo_mm2d/items/9ad223bec6774cbcc417

⇱ RecyclerViewでListItemの位置に応じた角丸を設定する #Android - Qiita


👁 Image
2

Go to list of users who liked

0

Share on X(Twitter)

Share on Facebook

Add to Hatena Bookmark

More than 3 years have passed since last update.

@ryo_mm2d(大前 良介)in👁 Image
Yahoo!デベロッパーネットワーク

RecyclerViewでListItemの位置に応じた角丸を設定する

2
Posted at

RecyclerViewでリストを角丸にしたくなる場合があります。
安直にはRecyclerView全体を角丸にしてしまう方法もありますが、以下のように、リスト全体の上下左右以外も角丸にしたい場合、各要素の位置に応じて角丸を設定する必要があります。

👁 Image
👁 Image

角丸の付け方はいろいろあると思いますが、制限はあるもののbackgroundに設定するdrawableを変更するのが簡単でしょうか。
GradientDrawableで角丸とマージン要素含めて定義しておいて、背景のdrawableを切り替えるだけで良いようにしてみます。
layer-listでitemにoffsetを設定し、shapeのpaddingに同じ値を設定することでそれっぽく定義できます。この場合、marginではなくpaddingなので要素サイズへの影響を考える必要があります。

bg_top.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list
 xmlns:android="http://schemas.android.com/apk/res/android">
 <item android:end="8dp" android:start="8dp" android:top="8dp">
 <shape android:shape="rectangle">
 <solid android:color="@color/white" />
 <corners android:radius="16dp" />
 <padding android:end="8dp" android:start="8dp" android:top="8dp"/>
 </shape>
 </item>
</layer-list>
bg_header.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list
 xmlns:android="http://schemas.android.com/apk/res/android">
 <item android:bottom="1dp" android:end="8dp" android:start="8dp" android:top="8dp">
 <shape android:shape="rectangle">
 <solid android:color="@color/white" />
 <corners android:topLeftRadius="16dp" android:topRightRadius="16dp"/>
 <padding android:bottom="1dp" android:end="8dp" android:start="8dp" android:top="8dp"/>
 </shape>
 </item>
</layer-list>
bg_middle.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list
 xmlns:android="http://schemas.android.com/apk/res/android">
 <item android:bottom="1dp" android:end="8dp" android:start="8dp">
 <shape android:shape="rectangle">
 <solid android:color="@color/white" />
 <padding android:bottom="1dp" android:end="8dp" android:start="8dp"/>
 </shape>
 </item>
</layer-list>
bg_footer.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list
 xmlns:android="http://schemas.android.com/apk/res/android">
 <item android:bottom="8dp" android:start="8dp" android:end="8dp">
 <shape android:shape="rectangle">
 <solid android:color="@color/white" />
 <corners android:bottomLeftRadius="16dp" android:bottomRightRadius="16dp"/>
 <padding android:bottom="8dp" android:start="8dp" android:end="8dp"/>
 </shape>
 </item>
</layer-list>

ってことで、onBindViewHolderでpositionに応じてbackgroundを変更します。

private class HogeAdapter(activity: Activity) : ListAdapter<Int, ItemViewHolder>(
 object : DiffUtil.ItemCallback<Int>() {
 override fun areItemsTheSame(oldItem: Int, newItem: Int): Boolean = oldItem == newItem
 override fun areContentsTheSame(oldItem: Int, newItem: Int): Boolean = oldItem == newItem
 }
) {
 private val inflater = activity.layoutInflater

 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder =
 ItemViewHolder(ItemContentBinding.inflate(inflater, parent, false))

 override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
 holder.binding.text.text = "item: " + getItem(position)
 holder.binding.root.setBackgroundResource(
 when (position) {
 0 -> R.drawable.bg_top
 1 -> R.drawable.bg_header
 itemCount - 1 -> R.drawable.bg_footer
 else -> R.drawable.bg_middle
 }
 )
 }
}

しかし、要素を更新したとき、単に順序が変化しただけでは、onBindViewHolderがコールされないという問題があります。

👁 Image
👁 Image

表示位置が変化したことを受け取れるコールバックはなさそうです。

対応方法

位置が変化したことを直接受け取ることはできないので、この背景要素もコンテンツの一部として、要素リストにパラメータを追加して、位置が変化し、背景を変更する必要がある場合にonBindViewHolderがコールされるようにします。

どの背景を指定するのかという情報を追加したdataクラスをつくって、

private enum class Background {
 TOP,
 HEADER,
 MIDDLE,
 FOOTER,
}
private data class Item(
 val background: Background,
 val id: Int,
)

リストの順序に応じてこの値を追加したものをAdapterにsubmitListします。

private fun makeItemList(): List<Item> =
 makeIdList().let { list ->
 list.mapIndexed { index, id ->
 Item(
 when (index) {
 0 -> Background.TOP
 1 -> Background.HEADER
 list.lastIndex -> Background.FOOTER
 else -> Background.MIDDLE
 }, id
 )
 }
 }

ListAdapterを以下のように実装し、DiffUtil.ItemCallbackで背景が変化した場合もコンテンツの変化として扱うようにします。

private class FugaAdapter(activity: Activity) : ListAdapter<Item, ItemViewHolder>(
 object : DiffUtil.ItemCallback<Item>() {
 override fun areItemsTheSame(oldItem: Item, newItem: Item): Boolean = oldItem.id == newItem.id
 override fun areContentsTheSame(oldItem: Item, newItem: Item): Boolean = oldItem == newItem
 }
) {
 private val inflater = activity.layoutInflater

 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder =
 ItemViewHolder(ItemContentBinding.inflate(inflater, parent, false))

 override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
 holder.binding.text.text = "item: " + getItem(position).id
 holder.binding.root.setBackgroundResource(
 when (getItem(position).background) {
 Background.TOP -> R.drawable.bg_top
 Background.HEADER -> R.drawable.bg_header
 Background.FOOTER -> R.drawable.bg_footer
 Background.MIDDLE -> R.drawable.bg_middle
 }
 )
 }
}

これで、リスト内の要素がシャッフルされた場合も、ちゃんと背景が位置に応じて変化するようになります。

👁 Image

以上です。

2

Go to list of users who liked

0
0

Go to list of comments

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
2

Go to list of users who liked

0