fix(android): Rollbalken verschluckte das Kontextmenue der Songzeilen
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d5b5c8cf84
commit
2641a6df44
3 files changed
+62
-42
No files matched your search
@@ -53,13 +53,19 @@ import kotlin.math.roundToInt
|
||||
*
|
||||
* Gescrollt wird nicht im Zeigerereignis, sondern in einem eigenen Effekt, der [zielPos] verfolgt.
|
||||
* Ein `launch` pro Fingerbewegung erzeugte bei 2000 Titeln hunderte konkurrierende Scroll-Aufrufe.
|
||||
*
|
||||
* Und eine vierte, die erst nach dem Ausliefern auffiel: **nur der Daumen nimmt Beruehrungen an,
|
||||
* nicht die ganze Spur.** Compose reicht eine Beruehrung nur an das oberste getroffene Element
|
||||
* weiter — ein Streifen ueber die volle Hoehe verschluckte damit das ⋮ jeder Songzeile, obwohl er
|
||||
* die Beruehrung gar nicht wollte. Der aeussere Rahmen hat deshalb KEIN `pointerInput` und ist
|
||||
* dadurch durchlaessig; nur der Daumen selbst hoert zu, mit etwas Luft nach oben und unten.
|
||||
*/
|
||||
@Composable
|
||||
fun Rollbalken(
|
||||
state: LazyListState,
|
||||
modifier: Modifier = Modifier,
|
||||
breite: Dp = 6.dp,
|
||||
griffBreite: Dp = 28.dp,
|
||||
griffBreite: Dp = 24.dp,
|
||||
minHoehe: Dp = 44.dp,
|
||||
farbe: Color = Color.White,
|
||||
) {
|
||||
@@ -90,7 +96,7 @@ fun Rollbalken(
|
||||
val anteil = if (ziehen) zielPos / maxIndex
|
||||
else state.firstVisibleItemIndex.toFloat() / maxIndex
|
||||
val daumenY = frei * anteil.coerceIn(0f, 1f)
|
||||
val spielPx = with(dichte) { 24.dp.toPx() }
|
||||
val spielPx = with(dichte) { 10.dp.toPx() } // Luft ober- und unterhalb des Daumens
|
||||
|
||||
// Was die Geste zur Laufzeit braucht — nicht als pointerInput-Schluessel, siehe oben.
|
||||
val geo = rememberUpdatedState(Geometrie(maxIndex.toFloat(), frei, daumenY, daumenPx, spielPx))
|
||||
@@ -106,47 +112,55 @@ fun Rollbalken(
|
||||
.width(griffBreite)
|
||||
.fillMaxHeight()
|
||||
.onSizeChanged { spurPx = it.height.toFloat() }
|
||||
.pointerInput(Unit) {
|
||||
awaitEachGesture {
|
||||
val start = awaitFirstDown(requireUnconsumed = false)
|
||||
val g = geo.value
|
||||
val aufDemDaumen = start.position.y >= g.daumenY - g.spiel &&
|
||||
start.position.y <= g.daumenY + g.daumenPx + g.spiel
|
||||
if (!aufDemDaumen || g.frei <= 0f) return@awaitEachGesture
|
||||
start.consume()
|
||||
zielPos = state.firstVisibleItemIndex.toFloat()
|
||||
ziehen = true
|
||||
try {
|
||||
while (true) {
|
||||
val ereignis = awaitPointerEvent()
|
||||
val punkt = ereignis.changes.firstOrNull { it.id == start.id } ?: break
|
||||
if (!punkt.pressed) break
|
||||
val delta = punkt.positionChange().y
|
||||
punkt.consume()
|
||||
if (delta != 0f) {
|
||||
val jetzt = geo.value
|
||||
if (jetzt.frei > 0f) {
|
||||
zielPos = (zielPos + delta * jetzt.maxIndex / jetzt.frei)
|
||||
.coerceIn(0f, jetzt.maxIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
ziehen = false
|
||||
}
|
||||
}
|
||||
}
|
||||
) {
|
||||
val luftDp = with(dichte) { spielPx.toDp() }
|
||||
Box(
|
||||
Modifier
|
||||
.align(Alignment.TopEnd)
|
||||
.offset { IntOffset(0, daumenY.roundToInt()) }
|
||||
.padding(end = 2.dp)
|
||||
.width(breite)
|
||||
.height(with(dichte) { daumenPx.toDp() })
|
||||
.clip(RoundedCornerShape(50))
|
||||
.background(farbe.copy(alpha = alpha))
|
||||
)
|
||||
// Der Greifbereich beginnt eine Spielbreite ueber dem Daumen und ist entsprechend
|
||||
// hoeher; der sichtbare Balken sitzt durch das Innenmass wieder genau auf daumenY.
|
||||
.offset { IntOffset(0, (daumenY - spielPx).roundToInt()) }
|
||||
.width(griffBreite)
|
||||
.height(with(dichte) { (daumenPx + 2 * spielPx).toDp() })
|
||||
.pointerInput(Unit) {
|
||||
awaitEachGesture {
|
||||
val start = awaitFirstDown(requireUnconsumed = false)
|
||||
if (geo.value.frei <= 0f) return@awaitEachGesture
|
||||
start.consume()
|
||||
zielPos = state.firstVisibleItemIndex.toFloat()
|
||||
ziehen = true
|
||||
try {
|
||||
while (true) {
|
||||
val ereignis = awaitPointerEvent()
|
||||
val punkt = ereignis.changes.firstOrNull { it.id == start.id } ?: break
|
||||
if (!punkt.pressed) break
|
||||
val delta = punkt.positionChange().y
|
||||
punkt.consume()
|
||||
if (delta != 0f) {
|
||||
val jetzt = geo.value
|
||||
if (jetzt.frei > 0f) {
|
||||
zielPos = (zielPos + delta * jetzt.maxIndex / jetzt.frei)
|
||||
.coerceIn(0f, jetzt.maxIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
ziehen = false
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(vertical = luftDp, horizontal = 0.dp),
|
||||
contentAlignment = Alignment.CenterEnd,
|
||||
) {
|
||||
Box(
|
||||
Modifier
|
||||
.padding(end = 2.dp)
|
||||
.width(breite)
|
||||
.fillMaxHeight()
|
||||
.clip(RoundedCornerShape(50))
|
||||
.background(farbe.copy(alpha = alpha))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user