V1.3.7: 调音弹窗文字自适应 —— 修"文字显示不全"
问题: 上一版按分辨率压缩时把「文字行」也一起压扁了。label() 自带 6dp 上下内边距, 行高必须 >= 文本行高+12dp, 而紧凑模式给了 18dp(频段刻度)/28dp(音量标题)/42dp(标题), sp 文本(尤其系统字体放大时)直接被裁掉 —— 这就是"文字显示不全"的根因。 修复: 改成「文字行 wrap_content + 只有无文字区域才用计算高度」 - 标题行/标题文字、音量标题、EQ 开关、预设行与预设按钮、频段数值与频率刻度 全部改为 wrap_content, 高度由文本自身决定, 任何字体缩放下都不会被裁。 - 频段刻度新增 caption(): 无内边距 + includeFontPadding=false, 12sp 完整显示。 - 预设按钮改 includeFontPadding=false + 等距 padding, 不再被固定 46dp 压扁。 - 滑杆用自身固有高度(-2), SeekBar 无文字不会被裁。 - 两遍 fit: afterShow 里先 panel.measure() 实测内容高度, 若超出「屏幕高*0.92 - 框架」 就把 EQ 频段区减掉(下限 dp(96)), 再调 fitDialogToScreen 收敛窗口 —— 收缩的永远是频段区而不是文字; 仍放不下才滚动。 新增 tuning dialog shrink band 日志便于现场核对。 版本号 1.3.6 -> 1.3.7(versionCode 12 -> 13)。按约定本次不发布 Release, 仅本地打包测试。
This commit is contained in:
@@ -108,7 +108,7 @@ $BT/apksigner verify 目标名.apk
|
||||
|
||||
### 版本号
|
||||
|
||||
- `app/build.gradle`:`versionName "1.3.6"` / `versionCode 12`
|
||||
- `app/build.gradle`:`versionName "1.3.7"` / `versionCode 13`
|
||||
- App 内显示统一用纯版本号(不加 `(versionCode)` 括号)
|
||||
|
||||
## 三、关键实现约束(避免踩坑)
|
||||
@@ -185,5 +185,10 @@ $BT/apksigner verify 目标名.apk
|
||||
ScrollView 限制为 `屏幕高*0.92 - 弹窗标题/按钮框架(chromeDp)` —— **不要再用 `WRAP_CONTENT` 作为
|
||||
弹窗高度**。内容高度由「屏幕预算 - 固定区高度」反推(调音弹窗的 EQ 频段区就是这样自适应),
|
||||
预算不足时弹窗内滚动而不是被裁掉。已应用:调音弹窗、扫码点歌弹窗、声音设置弹窗。
|
||||
新增/改动弹窗时请复用该 helper,别再写死 `setLayout(w, WRAP_CONTENT)`
|
||||
新增/改动弹窗时请复用该 helper,别再写死 `setLayout(w, WRAP_CONTENT)`。
|
||||
⚠️ **文字行必须 wrap_content**:`label()` 自带 6dp 上下内边距,固定矮行(曾用 18/28/42dp)
|
||||
会把文字裁掉 —— 这是"文字显示不全"的根因。做法是「文字行 wrap_content + 只有无文字区域
|
||||
才用计算高度」:调音弹窗里唯一可伸缩的是 EQ 频段区,采用**两遍 fit**(先 `panel.measure()`
|
||||
实测内容高度,超出预算就把频段区减掉,再调 `fitDialogToScreen`),这样收缩的永远是频段区
|
||||
而不是文字;频段区下限 dp(96),再放不下才滚动。大字体(系统字体缩放)同理自动兜住
|
||||
- **仓库可见性**:Gitea 仓库保持 **public**(若后续把 App 更新/热更新端点迁到 Gitea,非 public 会导致 403)
|
||||
|
||||
+2
-2
@@ -11,8 +11,8 @@ android {
|
||||
applicationId "com.local.ktv"
|
||||
minSdk 21
|
||||
targetSdk 25
|
||||
versionCode 12
|
||||
versionName "1.3.6"
|
||||
versionCode 13
|
||||
versionName "1.3.7"
|
||||
ndk {
|
||||
// 双 ABI 打包:兼容 64 位(Exynos 1580 等 arm64-v8a)与 32 位设备(极米 Play 超越版
|
||||
// 的 Amlogic 950X2/S905X2 采用 32 位 Android 用户态,必须包含 armeabi-v7a,否则
|
||||
|
||||
@@ -2556,28 +2556,22 @@ class MainActivity : AppCompatActivity() {
|
||||
val working = KtvEqualizer.levelsSnapshot().let { saved ->
|
||||
FloatArray(caps.bandCount) { saved.getOrElse(it) { 0f } }
|
||||
}
|
||||
// 分辨率自适应:按屏幕高度算出「弹窗内容能占多少」,把剩余空间留给 EQ 频段区,
|
||||
// 矮屏自动压缩频段区高度(而不是把弹窗顶出屏幕),再叠加 fitDialogToScreen 的
|
||||
// 高度上限兜底 —— 任何分辨率下都不会被裁切。
|
||||
// 分辨率自适应:**文字行全部 wrap_content**(高度由文本决定,永不被压扁/裁字),
|
||||
// 只有 EQ 频段区是可伸缩的 —— 屏幕放不下时收缩的是频段区,不是文字。
|
||||
val metrics = resources.displayMetrics
|
||||
val screenHeightDp = metrics.heightPixels / metrics.density
|
||||
// 矮屏(高度<620dp)只用来收窄竖杆列宽;文字尺寸不随分辨率变化。
|
||||
val compact = screenHeightDp < 620f
|
||||
val chromeDp = if (compact) 132 else 152
|
||||
val contentBudgetDp = (screenHeightDp * 0.92f - chromeDp).coerceAtLeast(240f)
|
||||
// 固定区:面板内边距 30 + 标题 42 + 音量标题 28 + 音量杆 + 开关(含间距) 50 + 预设(含间距)
|
||||
val fixedHeightDp = 30f + 42f + 28f +
|
||||
(if (compact) 38 else 44) + 50f +
|
||||
(if (compact) 44 else 52) + 10f
|
||||
val bandAreaDp = (contentBudgetDp - fixedHeightDp).coerceIn(112f, 216f)
|
||||
val bandAreaHeight = dp(bandAreaDp.toInt())
|
||||
// 文字/控件行占位的保守估计(内边距 + 标题 + 音量标题 + 开关 + 预设),最终以实测量为准。
|
||||
val textRowsDp = 30f + 34f + 30f + 50f + 58f
|
||||
var bandAreaHeight = dp((contentBudgetDp - textRowsDp).coerceIn(112f, 216f).toInt())
|
||||
val barColumnWidth = dp(if (compact) 38 else 46)
|
||||
val barCaptionHeight = dp(if (compact) 18 else 20)
|
||||
val volumeRowHeight = dp(if (compact) 38 else 44)
|
||||
val presetRowHeight = dp(if (compact) 44 else 52)
|
||||
Log.i(
|
||||
TAG,
|
||||
"tuning dialog adaptive: screen=${screenHeightDp}dp compact=$compact " +
|
||||
"budget=${contentBudgetDp}dp bandArea=${bandAreaDp}dp",
|
||||
"budget=${contentBudgetDp}dp band=${bandAreaHeight}px",
|
||||
)
|
||||
|
||||
val panel = LinearLayout(this).apply {
|
||||
@@ -2603,8 +2597,11 @@ class MainActivity : AppCompatActivity() {
|
||||
// ── 标题 ──
|
||||
val titleRow = FrameLayout(this)
|
||||
titleRow.addView(
|
||||
label("调音", 23, Color.WHITE).apply { gravity = Gravity.CENTER },
|
||||
FrameLayout.LayoutParams(-1, dp(42)),
|
||||
label("调音", 23, Color.WHITE).apply {
|
||||
gravity = Gravity.CENTER
|
||||
includeFontPadding = false
|
||||
},
|
||||
FrameLayout.LayoutParams(-1, -2),
|
||||
)
|
||||
val close = label("×", 22, Color.argb(153, 255, 255, 255)).apply {
|
||||
gravity = Gravity.CENTER
|
||||
@@ -2616,11 +2613,11 @@ class MainActivity : AppCompatActivity() {
|
||||
close,
|
||||
FrameLayout.LayoutParams(dp(44), dp(38), Gravity.END or Gravity.CENTER_VERTICAL),
|
||||
)
|
||||
panel.addView(titleRow, LinearLayout.LayoutParams(-1, dp(42)))
|
||||
panel.addView(titleRow, LinearLayout.LayoutParams(-1, -2))
|
||||
|
||||
// ── 1. 音量:横向控制杆(即时生效)──
|
||||
val volumeTitle = label("音量 $musicVolume%", 16, Color.WHITE)
|
||||
panel.addView(volumeTitle, LinearLayout.LayoutParams(-1, dp(28)))
|
||||
panel.addView(volumeTitle, LinearLayout.LayoutParams(-1, -2))
|
||||
var lastVolumeAppliedAt = 0L
|
||||
val volumeBar = SeekBar(this).apply {
|
||||
max = 100
|
||||
@@ -2649,7 +2646,7 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
})
|
||||
}
|
||||
panel.addView(volumeBar, LinearLayout.LayoutParams(-1, volumeRowHeight))
|
||||
panel.addView(volumeBar, LinearLayout.LayoutParams(-1, -2))
|
||||
|
||||
// ── 2. 均衡器开关(默认关闭)──
|
||||
lateinit var eqSwitch: TextView
|
||||
@@ -2666,7 +2663,7 @@ class MainActivity : AppCompatActivity() {
|
||||
applyEqualizerSettings()
|
||||
toast(if (KtvEqualizer.enabled) "均衡器已开启" else "均衡器已关闭")
|
||||
}
|
||||
panel.addView(eqSwitch, LinearLayout.LayoutParams(dp(190), dp(42)).apply { topMargin = dp(8) })
|
||||
panel.addView(eqSwitch, LinearLayout.LayoutParams(dp(200), -2).apply { topMargin = dp(8) })
|
||||
|
||||
eqArea = LinearLayout(this).apply { orientation = LinearLayout.VERTICAL }
|
||||
panel.addView(eqArea, LinearLayout.LayoutParams(-1, -2))
|
||||
@@ -2684,9 +2681,18 @@ class MainActivity : AppCompatActivity() {
|
||||
orientation = LinearLayout.HORIZONTAL
|
||||
gravity = Gravity.CENTER
|
||||
}
|
||||
eqArea.addView(presetRow, LinearLayout.LayoutParams(-1, presetRowHeight).apply { topMargin = dp(10) })
|
||||
eqArea.addView(presetRow, LinearLayout.LayoutParams(-1, -2).apply { topMargin = dp(10) })
|
||||
|
||||
// ── 4. EQ 频段:竖向控制杆 ──
|
||||
// 刻度文字用无内边距的 caption:高度由文本自身决定, 不会再被固定 dp 裁掉。
|
||||
fun caption(text: String, sp: Int, color: Int): TextView = TextView(this).apply {
|
||||
this.text = text
|
||||
textSize = sp.toFloat()
|
||||
setTextColor(color)
|
||||
gravity = Gravity.CENTER
|
||||
includeFontPadding = false
|
||||
setPadding(0, 0, 0, 0)
|
||||
}
|
||||
val bars = ArrayList<VerticalEqBar>(caps.bandCount)
|
||||
val valueViews = ArrayList<TextView>(caps.bandCount)
|
||||
fun syncBars() {
|
||||
@@ -2707,13 +2713,13 @@ class MainActivity : AppCompatActivity() {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
gravity = Gravity.CENTER_HORIZONTAL
|
||||
}
|
||||
val value = label(
|
||||
val value = caption(
|
||||
eqLevelText(working.getOrElse(index) { 0f }),
|
||||
12,
|
||||
Color.argb(170, 255, 255, 255),
|
||||
).apply { gravity = Gravity.CENTER }
|
||||
)
|
||||
valueViews.add(value)
|
||||
column.addView(value, LinearLayout.LayoutParams(-1, barCaptionHeight))
|
||||
column.addView(value, LinearLayout.LayoutParams(-1, -2))
|
||||
val bar = VerticalEqBar(this).apply {
|
||||
setRange(caps.minDb, caps.maxDb, eqSliderSteps)
|
||||
setLevel(working.getOrElse(index) { 0f })
|
||||
@@ -2735,10 +2741,8 @@ class MainActivity : AppCompatActivity() {
|
||||
bars.add(bar)
|
||||
column.addView(bar, LinearLayout.LayoutParams(barColumnWidth, 0, 1f))
|
||||
column.addView(
|
||||
label(bandLabels.getOrElse(index) { "${index + 1}" } + "Hz", 12, Color.WHITE).apply {
|
||||
gravity = Gravity.CENTER
|
||||
},
|
||||
LinearLayout.LayoutParams(-1, barCaptionHeight),
|
||||
caption(bandLabels.getOrElse(index) { "${index + 1}" } + "Hz", 12, Color.WHITE),
|
||||
LinearLayout.LayoutParams(-1, -2),
|
||||
)
|
||||
barRow.addView(column, LinearLayout.LayoutParams(0, -1, 1f))
|
||||
}
|
||||
@@ -2752,6 +2756,8 @@ class MainActivity : AppCompatActivity() {
|
||||
isClickable = true
|
||||
isFocusable = true
|
||||
background = getDrawable(R.drawable.bg_tuning_counter)
|
||||
includeFontPadding = false
|
||||
setPadding(dp(6), dp(11), dp(6), dp(11))
|
||||
TvFocusStyler.installAction(this)
|
||||
setOnClickListener {
|
||||
if (name == KtvEqualizer.CUSTOM_PRESET) {
|
||||
@@ -2768,7 +2774,7 @@ class MainActivity : AppCompatActivity() {
|
||||
presetButtons[name] = presetButton
|
||||
presetRow.addView(
|
||||
presetButton,
|
||||
LinearLayout.LayoutParams(0, dp(46), 1f).apply {
|
||||
LinearLayout.LayoutParams(0, -2, 1f).apply {
|
||||
marginStart = dp(4)
|
||||
marginEnd = dp(4)
|
||||
},
|
||||
@@ -2787,7 +2793,29 @@ class MainActivity : AppCompatActivity() {
|
||||
attributes = attributes.apply { dimAmount = 0.72f }
|
||||
addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
|
||||
}
|
||||
// 分辨率自适应:宽度上限 + 内容区高度上限(超出时弹窗内滚动,绝不裁切)。
|
||||
// 两遍自适应:先按内容实测高度判断是否超出可用空间,超出就只压缩 EQ 频段区
|
||||
// (文字行 wrap_content 保持不变),最后再收敛窗口尺寸。
|
||||
val availableHeight = (metrics.heightPixels * 0.92f).toInt()
|
||||
val maxContentHeight = (availableHeight - dp(chromeDp)).coerceAtLeast(dp(150))
|
||||
val probeWidth = min(dp(760), (metrics.widthPixels * 0.94f).toInt())
|
||||
panel.measure(
|
||||
View.MeasureSpec.makeMeasureSpec(probeWidth, View.MeasureSpec.AT_MOST),
|
||||
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
|
||||
)
|
||||
val overflow = panel.measuredHeight - maxContentHeight
|
||||
if (overflow > 0) {
|
||||
val shrunk = (bandAreaHeight - overflow).coerceAtLeast(dp(96))
|
||||
Log.i(
|
||||
TAG,
|
||||
"tuning dialog shrink band: measured=${panel.measuredHeight}px " +
|
||||
"max=${maxContentHeight}px band=$bandAreaHeight->$shrunk",
|
||||
)
|
||||
if (shrunk < bandAreaHeight) {
|
||||
bandAreaHeight = shrunk
|
||||
barRow.layoutParams = barRow.layoutParams.apply { height = bandAreaHeight }
|
||||
}
|
||||
}
|
||||
// 最终兜底:宽度上限 + 内容区高度上限(仍放不下时弹窗内滚动,绝不裁切)。
|
||||
fitDialogToScreen(
|
||||
dialog = dialog,
|
||||
scroll = scroll,
|
||||
|
||||
Reference in New Issue
Block a user