【Android】RecyclerViewでアイテムが中心に来るように自動スクロール

# はじめに

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/694762/0ef64ff9-9c9e-9945-0b69-b4634c244e12.gif" width="30%">

RecyclerViewでのアイテム一覧画面で、スワイプ後に指を離した時、アイテムが中心に来るように自動スクロールする処理を作成。
ViewPager2でも作成可能だが、今回はRecyclerViewで作成。

# 方法

アイテムが中心に来るように自動スクロールするには、`LinearSnapHelper`を使用。
両端のオフセットは、`ItemDecoration`を使用。
中心アイテムが変更されたことを検知するためには、`OnScrollListener`を使用。
プログラムのポイントとしては、`MainActivity.kt`内の処理。

まず、カードビューのレイアウト。テキストビューを配置。
```list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView
	xmlns:android="http://schemas.android.com/apk/res/android"
	xmlns:tools="http://schemas.android.com/tools"
	android:id="@+id/text"
	android:layout_width="280dp"
	android:layout_height="160dp"
	android:layout_marginHorizontal="8dp"
	android:background="@color/main"
	android:gravity="center"
	android:textColor="@color/white"
	android:textSize="28sp"
	tools:text="1"/>
```

メイン画面。RcyclerViewを配置。
```activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
	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:layout_width="match_parent"
	android:layout_height="match_parent"
	android:background="@color/white">

	<androidx.recyclerview.widget.RecyclerView
		android:id="@+id/recycler_view"
		android:layout_width="0dp"
		android:layout_height="160dp"
		android:orientation="horizontal"
		app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
		app:layout_constraintBottom_toBottomOf="parent"
		app:layout_constraintEnd_toEndOf="parent"
		app:layout_constraintStart_toStartOf="parent"
		app:layout_constraintTop_toTopOf="parent"
		tools:listitem="@layout/list_item"/>
</androidx.constraintlayout.widget.ConstraintLayout>
```

アダプタークラス。`selectItemIndex`で中心アイテムのインデックスを指定し、背景色の変更を行う。
```ItemAdapter.kt
class ItemAdapter : RecyclerView.Adapter<ItemAdapter.ViewHolder>() {

	var selectItemIndex = 0
		set(value) {
			field = value
			notifyDataSetChanged()
		}

	class ViewHolder(val binding: ListItemBinding) : RecyclerView.ViewHolder(binding.root)

	override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(ListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false))

	override fun onBindViewHolder(holder: ViewHolder, position: Int) {
		holder.binding.text.text = position.toString()

		val color = if (position == selectItemIndex) R.color.main else R.color.sub
		holder.binding.text.setBackgroundColor(ContextCompat.getColor(holder.binding.root.context, color))
	}

	override fun getItemCount() = 10
}
```

メイン画面の処理。中心自動スクロールや両端のオフセット設定、スクロール検知を行う。
```MainActivity.kt
class MainActivity : AppCompatActivity() {

	/** ViewBinding */
	private val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }

	override fun onCreate(savedInstanceState: Bundle?) {
		super.onCreate(savedInstanceState)
		setContentView(binding.root)

		binding.recyclerView.adapter = ItemAdapter()

		//両端のオフセット設定
		binding.recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() {
			override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
				val margin = (parent.width - view.layoutParams.width) / 2
				val position = parent.getChildAdapterPosition(view)
				if (position == 0) outRect.left = margin
				if (position == state.itemCount - 1) outRect.right = margin
			}
		})

		//アイテムが中心に来るように自動スクロール
		val snapHelper = LinearSnapHelper()
		snapHelper.attachToRecyclerView(binding.recyclerView)

		//中心アイテム色変更
		binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
			override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
				super.onScrollStateChanged(recyclerView, newState)
				val view = snapHelper.findSnapView(recyclerView.layoutManager)
				if (view != null) {
					val position = recyclerView.getChildAdapterPosition(view)
					if (position >= 0) (recyclerView.adapter as? ItemAdapter)?.selectItemIndex = position
				}
			}
		})
	}
}
```

# 参考サイト

https://ryuta46.com/1207

https://blog.fits-inc.jp/2020/03/03/android-%E3%81%A7-recylcerview-%E3%81%AE%E3%82%AA%E3%83%BC%E3%83%90%E3%83%BC%E3%82%B9%E3%82%AF%E3%83%AD%E3%83%BC%E3%83%AB%E3%82%92%E6%A4%9C%E7%9F%A5%E3%81%99%E3%82%8B/

# 関連記事
ViewPager2を使用

https://qiita.com/nakashimaakio/items/2e908483949874728f25