Android 斜めスクロールView実装 ~2026年Jetpack Compose版
📌 関連記事: この記事は 【android】ScrollViewで縦横斜めにスクロール(2015年)の2026年版アップデートです。当時のコンセプトが、今どう実装されているかをご紹介します。
かつて「Androidで斜めスクロールって実装できないのか?」という問い合わせがあった。2015年時点では標準APIになく、自作が必須だった。今は Jetpack Compose の登場で、状況が大きく変わった。
2026年なら、Compose + LazyGrid で 1行で解決できる。
昔の問題(2015年)vs 現在(2026年)
2015年:框組みで解決しようとしていた時代
ScrollView(縦)+ HorizontalScrollView(横)
↓
マージして「斜めスクロール」を実現
↓
カスタムViewGroupを自作
当時は効果測定も「コンセプト段階」で、実装されていなかった。
2026年:Jetpack Compose なら標準
LazyVerticalGrid / LazyHorizontalGrid
↓
フリングスクロール、慣性スクロール完備
↓
nested scroll も自動対応
標準APIs の充実で、カスタム実装が不要になった。
2026年の実装:Jetpack Compose
ステップ 1:依存関係の追加
dependencies {
implementation "androidx.compose.foundation:foundation:1.7.0"
implementation "androidx.compose.material3:material3:1.3.0"
implementation "androidx.activity:activity-compose:1.9.0"
}
ステップ 2:LazyGrid で斜めスクロール実現
@Composable
fun DiagonalScrollView() {
LazyVerticalGrid(
columns = GridCells.Fixed(3),
modifier = Modifier
.fillMaxSize()
.padding(8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
items(100) { index ->
GridItem(index)
}
}
}
@Composable
fun GridItem(index: Int) {
Card(
modifier = Modifier
.fillMaxWidth()
.height(120.dp),
colors = CardDefaults.cardColors(
containerColor = Color(0xFF6200EE)
)
) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text(
text = "Item $index",
color = Color.White,
fontSize = 16.sp,
fontWeight = FontWeight.Bold
)
}
}
}
ステップ 3:複数方向スクロール(横 + 縦)
単純な縦スクロール + 横スクロール両対応が必要なら:
@Composable
fun BiDirectionalScroll() {
LazyVerticalGrid(
columns = GridCells.Fixed(4),
modifier = Modifier
.fillMaxSize()
.horizontalScroll(rememberScrollState())
) {
items(200) { index ->
GridItem(index)
}
}
}
注意:
horizontalScroll()はLazyVerticalGridでも使用可能- フリングスクロール、慣性スクロールは自動対応
- パフォーマンスは Compose の仮想化により最適化済み
実装時の落とし穴
1. NestedScrollConnection の設定を忘れずに
親スクロールと子スクロールが両方ある場合:
val nestedScrollDispatcher = remember { NestedScrollDispatcher() }
LazyVerticalGrid(
columns = GridCells.Fixed(3),
modifier = Modifier
.nestedScroll(nestedScrollDispatcher.asNestedScrollConnection())
) {
// ... items
}
2. パフォーマンス:アイテム数が大きい場合
❌ 悪い例:すべてのアイテムを描画
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
repeat(10000) { index ->
GridItem(index) // 全項目メモリに残る
}
}
✅ 良い例:仮想化による遅延描画
LazyVerticalGrid(columns = GridCells.Fixed(3)) {
items(10000) { index ->
GridItem(index) // 見える範囲だけ描画
}
}
3. スクロール位置の保存
ユーザーが別の画面から戻ってきた時、スクロール位置を復元:
val listState = rememberLazyGridState()
LazyVerticalGrid(
columns = GridCells.Fixed(3),
state = listState
) {
items(100) { index ->
GridItem(index)
}
}
// スクロール位置を保存
LaunchedEffect(listState) {
snapshotFlow { listState.firstVisibleItemIndex }
.collect { index ->
// ローカルDBに保存
saveScrollPosition(index)
}
}
2015年実装 vs 2026年実装:比較
| 項目 | 2015年 | 2026年 |
|---|---|---|
| フレームワーク | View(XML + Java) | Jetpack Compose(Kotlin) |
| コード行数 | 100~200行 | 30~50行 |
| パフォーマンス | メモリ負荷大(全アイテム保持) | 軽量(仮想化) |
| 実装難度 | 中~高(カスタムViewGroup必須) | 低(標準APIで解決) |
| フリングスクロール | 手実装 | 自動対応 |
| nested scroll対応 | 複雑 | シンプル |
| テスト容易性 | 低 | 高(Composable テスト用ツール充実) |
代替案:特殊な要件がある場合
要件 1:斜め45度のカスタムスクロール
@Composable
fun DiagonalCustomScroll(
angle: Float = 45f // 45度斜めスクロール
) {
LazyVerticalGrid(
columns = GridCells.Fixed(3),
modifier = Modifier
.fillMaxSize()
.rotate(angle) // 斜めに回転
) {
items(100) { index ->
GridItem(index)
}
}
}
要件 2:スクロール速度をカスタマイズ
@Composable
fun CustomSpeedScroll() {
val scrollState = rememberScrollState()
Column(
modifier = Modifier
.verticalScroll(
scrollState,
flingBehavior = ScrollableDefaults.flingBehavior()
)
) {
repeat(100) { index ->
Text("Item $index", modifier = Modifier.padding(16.dp))
}
}
}
まとめ
2015年:「カスタムViewGroup自作が必須」
2026年:「Compose の LazyGrid で 30行で完成」
Android 開発は急速に進化している。かつての「困難な実装」が「標準機能」に変わるのは珍しくない。
古い記事の実装方法に固執するのではなく、2026年のプラットフォーム能力を活用する方が圧倒的に効率的。