android: lock portrait - UI composition glued to phone frame (top/bottom bars + image area absolutely fixed)
This commit is contained in:
@@ -16,7 +16,7 @@
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:screenOrientation="fullSensor"
|
||||
android:screenOrientation="portrait"
|
||||
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden|uiMode"
|
||||
android:resizeableActivity="true">
|
||||
<intent-filter>
|
||||
|
||||
@@ -36,9 +36,10 @@ private val TABS = listOf(
|
||||
)
|
||||
|
||||
/**
|
||||
* App shell. The bottom navigation bar is always at the bottom in both
|
||||
* orientations (top/bottom bars keep their positions; only their inner
|
||||
* icons/text follow the screen). Tab content lives in a stable slot.
|
||||
* App shell. The activity is portrait-locked, so the bottom navigation bar
|
||||
* is glued to the phone's portrait bottom edge at all times (its absolute
|
||||
* position never moves, however the phone is physically held). Tab content
|
||||
* lives in a stable slot.
|
||||
*/
|
||||
@Composable
|
||||
fun AppRoot() {
|
||||
@@ -58,7 +59,7 @@ fun AppRoot() {
|
||||
}
|
||||
}
|
||||
|
||||
// bottom navigation overlay (both orientations)
|
||||
// bottom navigation overlay (glued to the portrait bottom edge)
|
||||
Surface(
|
||||
color = MaterialTheme.colorScheme.surface,
|
||||
modifier = Modifier
|
||||
|
||||
@@ -2,7 +2,7 @@ package com.mag160c.thermal.ui
|
||||
|
||||
/** Shared UI inset state (px), written by AppRoot's nav overlay. */
|
||||
object UiInsets {
|
||||
/** Bottom navigation overlay height in px (0 in landscape). */
|
||||
/** Bottom navigation overlay height in px (portrait-locked app: constant). */
|
||||
@Volatile
|
||||
var navPx: Int = 0
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ fun GalleryScreen(vm: GalleryViewModel = viewModel()) {
|
||||
Text("媒体库 (${items.size})", style = MaterialTheme.typography.titleMedium)
|
||||
Button(onClick = { vm.refresh() }) { Text("刷新") }
|
||||
}
|
||||
LazyVerticalGrid(columns = GridCells.Fixed(if (isLandscape()) 4 else 3)) {
|
||||
LazyVerticalGrid(columns = GridCells.Fixed(3)) {
|
||||
items(items.size) { idx ->
|
||||
val item = items[idx]
|
||||
var bmp by remember(item.name) { mutableStateOf<android.graphics.Bitmap?>(null) }
|
||||
@@ -109,8 +109,3 @@ fun GalleryScreen(vm: GalleryViewModel = viewModel()) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun isLandscape(): Boolean =
|
||||
androidx.compose.ui.platform.LocalConfiguration.current.orientation ==
|
||||
android.content.res.Configuration.ORIENTATION_LANDSCAPE
|
||||
|
||||
@@ -11,11 +11,13 @@ import android.view.SurfaceView
|
||||
/**
|
||||
* Software canvas renderer for the live IR stream.
|
||||
*
|
||||
* The thermal image is ALWAYS drawn rotated 90 deg CW (3:4 vertical) into a
|
||||
* fixed fitted rect inside the available area (full screen minus the control
|
||||
* top bar and the bottom navigation bar). The image region does NOT move or
|
||||
* rotate with the phone; only the OSD text stays screen-horizontal so the
|
||||
* labels are always readable.
|
||||
* The activity is portrait-locked, so the composition below is glued to the
|
||||
* phone's portrait frame: the thermal image is ALWAYS drawn rotated 90 deg
|
||||
* CW (3:4 vertical) into a fixed fitted rect inside the available area
|
||||
* (full screen minus the control top bar and the bottom navigation bar).
|
||||
* The image region does NOT move or rotate however the phone is physically
|
||||
* held, so the on-screen area always matches the thermal lens direction;
|
||||
* OSD text stays screen-horizontal so the labels are always readable.
|
||||
*/
|
||||
class LiveRenderer(
|
||||
private val surfaceView: SurfaceView,
|
||||
|
||||
@@ -43,10 +43,12 @@ import com.mag160c.thermal.core.Palettes
|
||||
import android.view.SurfaceView
|
||||
|
||||
/**
|
||||
* Stable single-branch live view: one SurfaceView for the whole screen,
|
||||
* a control TOP bar (always visible) and overlays anchored by orientation.
|
||||
* Image content stays world-fixed across rotations (renderer handles the
|
||||
* draw rotation from the display rotation).
|
||||
* Stable single-branch live view: one SurfaceView for the whole screen, a
|
||||
* control TOP bar (always visible) and the bottom navigation overlay.
|
||||
* The activity is portrait-locked, so the whole composition (top bar /
|
||||
* image region / bottom bar) is glued to the phone's portrait frame and
|
||||
* never moves or rotates, however the phone is physically held — the on-
|
||||
* screen area always matches the thermal lens direction.
|
||||
*/
|
||||
@Composable
|
||||
fun LiveScreen(vm: LiveViewModel = viewModel()) {
|
||||
@@ -86,7 +88,7 @@ fun LiveScreen(vm: LiveViewModel = viewModel()) {
|
||||
)
|
||||
}
|
||||
|
||||
// ---- control TOP bar (always at top, both orientations) ----
|
||||
// ---- control TOP bar (glued to the portrait top edge) ----
|
||||
Surface(
|
||||
color = MaterialTheme.colorScheme.surfaceVariant,
|
||||
modifier = Modifier
|
||||
|
||||
Reference in New Issue
Block a user