💝
ドラッグ&ドロップ実装2
前回の記事
前回のアプリを見たい方はこちら
前回のコード
HogeScreen.kt
@Composable
fun HogeScreen(
modifier: Modifier = Modifier,
viewModel: HogeViewModel = viewModel(),
) {
val ballPosition by viewModel.ballPosition.collectAsState()
Canvas(
modifier = modifier
.fillMaxSize()
.background(Color.LightGray)
.pointerInput(Unit) {
detectDragGestures(
onDragStart = { offset ->
viewModel.onDragStart(offset)
},
onDrag = { change, amount ->
viewModel.onDrag(amount)
},
onDragEnd = { viewModel.onDragEnd() },
onDragCancel = {},
)
}
) {
drawCircle(
color = Color.Red,
radius = 100f,
center = ballPosition,
)
}
}
HogeViewModel.kt
class HogeViewModel: ViewModel() {
//ボールの位置を保持するStateFlow
private val _ballPosition: MutableStateFlow<Offset> = MutableStateFlow(Offset(100f, 100f))
val ballPosition: StateFlow<Offset> = _ballPosition.asStateFlow()
private var dragStartOffset: Offset? = null //ドラッグ開始の位置
fun onDragStart(offset: Offset) {
dragStartOffset = offset
}
fun onDrag(dragAmount: Offset) {
if (dragStartOffset != null) {
val newPosition = Offset(
_ballPosition.value.x + dragAmount.x,
_ballPosition.value.y + dragAmount.y
)
_ballPosition.update { it.copy(newPosition.x, newPosition.y) }
}
}
fun onDragEnd() {
dragStartOffset = null
}
}
アプリのイメージ
🫡タップした場所とボールとの距離
前回まで、ボールのことを〇と表記していましたが、わかりづらいのでボールと表記します!!!
さて、ボールに触ってなくてもドラッグするとボールが動いてしまう問題に対応します。
タップとボールの距離を計算して、ボールに触っているかどうかを確認する関数をViewModelに追加します。
HogeViewModel.kt
class HogeViewModel: ViewModel() {
//省略
//ボールに触っているかどうか
fun isTapOnBall(tapPosition: Offset,ballPosition: Offset): Boolean {
val distance = (tapPosition - ballPosition).getDistance()
return distance <= 100f
}
}
タップの位置tapPosition
からボールの位置ballPosition
を引くことによって、2者間の差分(ベクトル)を計算。
'getDistance()`で2点をつないだ直線(直角三角形の斜め部分)の距離を計算。
結果がボールの半径(100f)以下であれば、ボールをタップしたと判断しtrue返す。
🫡タップした位置にボールがあるときだけドラッグ
次は、ドラッグを開始したとき、タップした位置にボールがあるときだけドラッグするようにviewModelのonDragStart
メソッドを修正します。
HogeViewModel.kt
class HogeViewModel: ViewModel() {
//省略
fun onDragStart(offset: Offset) {
if (isTapOnBall(offset, _ballPosition.value)) dragStartOffset = offset
}
//省略
}
もし、isTapOnBall
関数の結果がtrueだったら、dragStartOffset
にタップの位置を設定してドラッグを描画する、という処理。
offsetにはHogeScreenからタップ開始の位置が飛んできます。
そんなわけで、実行~~♪
実行結果はこちら
うん。思った通りにドラッグ&ドロップできた!
Discussion