feat(android): 控件浮层化——statusView/sensBar/controlBar 移入 stage 底部叠加层

三个控件原是 stage 的垂直兄弟节点,显示即抢高度,于是 surfaceView 的
letterbox 几何跟着变:横屏实测控件可见时画面 1216x684、隐藏时 1920x1080,
即用户感知的「一按按钮画面就被挤小」。

改为 stage 内的 chromeOverlay(LinearLayout,Gravity.BOTTOM,
WRAP_CONTENT,底色 0x99000000 半透明):画面尺寸现在与控件显隐无关。
setControlsVisible 仍统一 GONE 三个子视图,容器随之塌成 0 高,隐藏态与
改动前逐字节等价,4a 的黑边唤出逻辑一行未动。

不碰解码、触摸映射、协议与 CursorOverlayView。竖屏实测 stage 高 2069
(等于内容区高度)、画面恒定 1080x607,连续 4 次采样无差异;横屏可见态与
隐藏态画面均为 1920x1080;R7 绝对映射三点误差 1px/0px/1px;JVM 60 例全绿。
This commit is contained in:
郑博文 2026-11-02 16:53:27 +08:00
parent 3b219a442d
commit 06bb7bcd1c

View File

@ -56,6 +56,12 @@ class MainActivity : Activity(), RemoteSurfaceView.Listener {
/** 4a:横屏自动隐藏的两个控制条(Q3=A)+ 状态行 */
private lateinit var controlBar: LinearLayout
private lateinit var sensBar: LinearLayout
/**
* 循环 5:三个控件的**浮层容器**,是 [stage] 的底部叠加子视图——不再是画面
* 的垂直兄弟。`setControlsVisible` 把三个视图一起 GONE,容器 WRAP_CONTENT
* 随之塌成 0 高,于是 stage / 画面的 letterbox 几何与控件显隐**无关**。
*/
private lateinit var chromeOverlay: LinearLayout
@Volatile
private var handle = 0L
@ -301,7 +307,9 @@ class MainActivity : Activity(), RemoteSurfaceView.Listener {
statusView = TextView(this).apply {
textSize = 12f
setPadding(dp(12), dp(6), dp(12), dp(6))
setBackgroundColor(0xCC000000.toInt())
// 循环 5:底色统一移到浮层容器(chromeOverlay)上,这里保持透明——
// 两层半透明黑叠加会让浮层比设计值明显更黑。
setBackgroundColor(android.graphics.Color.TRANSPARENT)
setTextColor(0xFFFFFFFF.toInt())
maxLines = 3
}
@ -423,16 +431,34 @@ class MainActivity : Activity(), RemoteSurfaceView.Listener {
addView(sensLabel, LayoutParams(0, LayoutParams.WRAP_CONTENT, 1f))
addView(sensUpButton, LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT))
}
return LinearLayout(this).apply {
// 循环 5/Q2=A:三个控件收进浮层容器,作为 stage 的**底部叠加层**。
// 为什么必须这样:它们是 stage 的垂直兄弟时,显示即抢高度 → surfaceView
// 的 letterbox 几何跟着变(横屏实测可见态画面 1216x684、隐藏态 1920x1080,
// 即"一按按钮画面就被挤小")。改成 stage 的子视图后,画面尺寸与控件显隐无关。
//
// 半透明底放在**容器**上而不是各条上:一次绘制、一层叠色,且 GONE 时整层
// 一起消失(三个子视图仍由 setControlsVisible 统一 GONE,容器纵向塌成 0)。
chromeOverlay = LinearLayout(this).apply {
orientation = LinearLayout.VERTICAL
visibility = View.GONE
addView(stage, LayoutParams(LayoutParams.MATCH_PARENT, 0, 1f))
// 固定高度而非 WRAP_CONTENT:与 weight=1 的兄弟共存时,
// WRAP_CONTENT 可能被压成 0(实测 bounds=[0,0][0,0],状态栏不可见)
setBackgroundColor(CHROME_OVERLAY_BG)
addView(statusView, LayoutParams(LayoutParams.MATCH_PARENT, dp(64)))
addView(sensBar, LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT))
addView(controlBar, LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT))
}
stage.addView(
chromeOverlay,
FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT,
Gravity.BOTTOM,
),
)
return LinearLayout(this).apply {
orientation = LinearLayout.VERTICAL
visibility = View.GONE
// 画面撑满整个远控面板:控件的显隐不再参与垂直分配。
addView(stage, LayoutParams(LayoutParams.MATCH_PARENT, 0, 1f))
}
}
private fun prefs() = getSharedPreferences(PREFS, Context.MODE_PRIVATE)
@ -1015,6 +1041,13 @@ class MainActivity : Activity(), RemoteSurfaceView.Listener {
/** 4a/Q7=A:横屏无操作隐藏控制条的延时 */
private const val CONTROLS_HIDE_MS = 3000L
/**
* 循环 5/Q2=A:控件浮层底色——60% 黑。半透明是为了让被覆盖的画面仍然可辨
* (横屏画面占满 stage 高度,浮层必然盖住底部;透明度是可读性与不遮挡的折中)。
* 只用在这一处(容器上),各控件自身保持默认/透明底。
*/
private const val CHROME_OVERLAY_BG = 0x99000000.toInt()
/**
* 与协议版本保持一致:远控画面状态栏显示当前握手版本(方案 R5 要求)。
* 值随 `common::PROTO_VERSION` 人工同步——Android 侧不做线格式解析,