たくさんの自由帳
Androidのお話
たくさんの自由帳
投稿日 : | 0 日前
文字数(だいたい) : 2722
どうもこんばんわ。ネタ無いので記録として書いておきます、
健康診断行った結果が帰ってきました、ALT
が再検査とか書いてあります。なんすかこれ
あとは、A
→C12
になったせいなのかコメント欄がいっぱい書いてあります、
まあそもそもこんなくっそ暑い中、水すらも飲めない状態で行ったら不健康診断になるだろうがよ!!!!!!!!!!
Jetpack Compose
でできた、表みたいなUI
で縦横斜め方向にスクロールできるようにしてみます。verticalScroll()
とhorizontalScroll()
を両方兼ね備えた的なやつ。
動画編集アプリのタイムラインとかで使えそうですね(使います!!)
説明は後、Modifier.verticalScroll()
はスクロールジェスチャー検出 + スクロールに合わせて描画をずらす機能があります。
が、が、が
今のところModifier.scrollable2D()
にはスクロールジェスチャー検出機能しかありません、よって自前でずらす必要があります。
val offset = remember { mutableStateOf(Offset.Zero) }
val size = remember { mutableStateOf(IntSize.Zero) }
Box(
modifier = Modifier // あとは fillMaxSize() するとか
.clipToBounds()
.scrollable2D(state = rememberScrollable2DState { delta ->
// これをしないと見えないスクロール(スクロールしても UI がなかなか反映されない)が起きる
val newX = (offset.value.x + delta.x).toInt().coerceIn(-size.value.width..0)
val newY = (offset.value.y + delta.y).toInt().coerceIn(-size.value.height..0)
offset.value = Offset(newX.toFloat(), newY.toFloat())
// TODO 今回は面倒なのでネストスクロールを考慮していません。
// TODO 本来は利用した分だけ return するべきです
delta
})
.layout { measurable, constraints ->
// ここを infinity にすると左端に寄ってくれる
val childConstraints = constraints.copy(
maxHeight = Constraints.Infinity,
maxWidth = Constraints.Infinity,
)
// この辺は全部 Scroll.kt のパクリ
val placeable = measurable.measure(childConstraints)
val width = placeable.width.coerceAtMost(constraints.maxWidth)
val height = placeable.height.coerceAtMost(constraints.maxHeight)
val scrollHeight = placeable.height - height
val scrollWidth = placeable.width - width
size.value = IntSize(scrollWidth, scrollHeight)
layout(width, height) {
val scrollX = offset.value.x.toInt().coerceIn(-scrollWidth..0)
val scrollY = offset.value.y.toInt().coerceIn(-scrollHeight..0)
val xOffset = scrollX
val yOffset = scrollY
withMotionFrameOfReferencePlacement {
placeable.placeRelativeWithLayer(xOffset, yOffset)
}
}
}
) {
// ここに縦横にはみ出すレイアウト
}
連続で体温超え、っていうかお熱。するとかどーなってんだよおい。暑すぎる。Google Pixel
が毎日熱中症警戒アラート
出してる。
JetpackCompose
のこのバージョンから縦横斜め方向にスクロールした分を取得できるModifier
が追加されました。Modifier.scrollable2D()
ですね。
Modifier.scrollable2D(rememberScrollable2DState { delta ->
delta
})
delta
が移動した分で、あとは自分でスクロール分のオフセットを調整していくだけ。
・・・
どうやら斜め方向のジェスチャー登録までで、スクロール分だけオフセットを調整して表示する機能はないらしい。ええ
一応書いておくか
端末 | Pixel 8 Pro |
Android | 16 QPR 2 |
Jetpack Compose Bom | 2025.08.00 |
Android Studio | Android Studio Narwhal 3 Feature Drop 2025.1.3 |
こんな感じに縦横に数字を敷き詰めるようにしてみました。
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
JetpackComposeScrollable2DTheme {
MainScreen()
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun MainScreen() {
Scaffold(
topBar = {
TopAppBar(title = { Text(text = stringResource(R.string.app_name)) })
}
) { paddingValues ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
) {
for (i in 0..10) {
Row {
for (j in 0..10) {
NumberSquare(number = i * 10 + j)
}
}
}
}
}
}
@Composable
private fun NumberSquare(
modifier: Modifier = Modifier,
number: Int
) {
Box(
modifier = modifier
.border(1.dp, Color.Black)
.size(100.dp),
contentAlignment = Alignment.Center
) {
Text(
text = number.toString(),
fontSize = 20.sp
)
}
}
といってもModifier.verticalScroll()
のコードをパクって、Modifier.scrollable2D()
で動くように直しただけなのであんまり追求しないでください、
rememberScrollable2DState { }
のコールバックで移動した分がもらえるので、offset.value
で累積していきます。
ここで勢いよくスクロールしたときに備えて、-size.value.width .. 0
の範囲内でしか値が帰ってこないように制限します。Kotlin
便利。
仮に勢いよくの制限がないと、いくらスクロールしても戻ってこれなくなります(範囲外に行ったので、範囲内に戻ってくるまで描画は無反応...)
rememberScrollable2DState { }
の関数はちゃんとOffset
を返す必要がありますが、今回は別にネストスクロールしていないので諦めました。
layout { }
はModifier.verticalScroll()
の中を見てパクっただけなのよく分かっていません()。Constraints.Infinity
にするとはみ出させられるんだ~~くらいしか分かってません。
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun MainScreen() {
val offset = remember { mutableStateOf(Offset.Zero) }
val size = remember { mutableStateOf(IntSize.Zero) }
Scaffold(
topBar = { TopAppBar(title = { Text(text = stringResource(R.string.app_name)) }) }
) { paddingValues ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
.clipToBounds()
.scrollable2D(state = rememberScrollable2DState { delta ->
// これをしないと見えないスクロール(スクロールしても UI がなかなか反映されない)が起きる
val newX = (offset.value.x + delta.x).toInt().coerceIn(-size.value.width..0)
val newY = (offset.value.y + delta.y).toInt().coerceIn(-size.value.height..0)
offset.value = Offset(newX.toFloat(), newY.toFloat())
// TODO 今回は面倒なのでネストスクロールを考慮していません。
// TODO 本来は利用した分だけ return するべきです
delta
})
.layout { measurable, constraints ->
// ここを infinity にすると左端に寄ってくれる
val childConstraints = constraints.copy(
maxHeight = Constraints.Infinity,
maxWidth = Constraints.Infinity,
)
// この辺は全部 Scroll.kt のパクリ
val placeable = measurable.measure(childConstraints)
val width = placeable.width.coerceAtMost(constraints.maxWidth)
val height = placeable.height.coerceAtMost(constraints.maxHeight)
val scrollHeight = placeable.height - height
val scrollWidth = placeable.width - width
size.value = IntSize(scrollWidth, scrollHeight)
layout(width, height) {
val scrollX = offset.value.x.toInt().coerceIn(-scrollWidth..0)
val scrollY = offset.value.y.toInt().coerceIn(-scrollHeight..0)
val xOffset = scrollX
val yOffset = scrollY
withMotionFrameOfReferencePlacement {
placeable.placeRelativeWithLayer(xOffset, yOffset)
}
}
}
) {
for (i in 0..10) {
Row {
for (j in 0..10) {
NumberSquare(number = i * 10 + j)
}
}
}
}
}
}
どーぞ
以上です、おつ 888
rememberScrollable2DState { delta -> }
のdelta
をスクロールしたいコンポーネントに、Modifier.offset
すればいいじゃんって思いましたが普通に動きませんでした。