fixed online connection error and class scale question bug
This commit is contained in:
1
.idea/misc.xml
generated
1
.idea/misc.xml
generated
@ -1,4 +1,3 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
|
||||||
|
|||||||
@ -9,11 +9,10 @@ import androidx.core.widget.TextViewCompat
|
|||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
Zweck:
|
Zweck:
|
||||||
- Stellt eine „Glas-Skala“-Frage dar, bei der pro Symptom (Zeile) genau eine von fünf Antwortstufen gewählt wird: never / little / moderate / much / extreme.
|
- „Glas-Skala“-Frage: pro Symptom (Zeile) genau eine von fünf Stufen.
|
||||||
- Die Stufen werden sowohl als RadioButtons als auch über eine feste Icon-Leiste visualisiert.
|
- Stufen als RadioButtons (clickbar) + Icon-Header.
|
||||||
|
- FIX: Pro Zeile wird Single-Select erzwungen (auch im Bearbeiten-Modus / Restore).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class HandlerGlassScaleQuestion(
|
class HandlerGlassScaleQuestion(
|
||||||
@ -54,6 +53,9 @@ class HandlerGlassScaleQuestion(
|
|||||||
"extreme_glass" to R.drawable.ic_glass_4
|
"extreme_glass" to R.drawable.ic_glass_4
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Damit wir beim programmatic-check keine Endlosschleifen triggern
|
||||||
|
private var suppressRowListener: Boolean = false
|
||||||
|
|
||||||
override fun bind(layout: View, question: QuestionItem) {
|
override fun bind(layout: View, question: QuestionItem) {
|
||||||
if (question !is QuestionItem.GlassScaleQuestion) return
|
if (question !is QuestionItem.GlassScaleQuestion) return
|
||||||
this.layout = layout
|
this.layout = layout
|
||||||
@ -68,12 +70,13 @@ class HandlerGlassScaleQuestion(
|
|||||||
setTextSizePercentOfScreenHeight(titleTv, 0.03f)
|
setTextSizePercentOfScreenHeight(titleTv, 0.03f)
|
||||||
setTextSizePercentOfScreenHeight(questionTv, 0.03f)
|
setTextSizePercentOfScreenHeight(questionTv, 0.03f)
|
||||||
|
|
||||||
// feste Icon-Leiste
|
// Header Icons
|
||||||
val header = layout.findViewById<LinearLayout>(R.id.glass_header)
|
val header = layout.findViewById<LinearLayout>(R.id.glass_header)
|
||||||
header.removeAllViews()
|
header.removeAllViews()
|
||||||
header.addView(Space(context).apply {
|
header.addView(Space(context).apply {
|
||||||
layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 4f)
|
layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 4f)
|
||||||
})
|
})
|
||||||
|
|
||||||
val iconSizePx = (context.resources.displayMetrics.density * 36).toInt()
|
val iconSizePx = (context.resources.displayMetrics.density * 36).toInt()
|
||||||
scaleLabels.forEach { labelKey ->
|
scaleLabels.forEach { labelKey ->
|
||||||
val cell = FrameLayout(context).apply {
|
val cell = FrameLayout(context).apply {
|
||||||
@ -88,13 +91,12 @@ class HandlerGlassScaleQuestion(
|
|||||||
cell.addView(img)
|
cell.addView(img)
|
||||||
header.addView(cell)
|
header.addView(cell)
|
||||||
}
|
}
|
||||||
//
|
|
||||||
|
|
||||||
val tableLayout = layout.findViewById<TableLayout>(R.id.glass_table)
|
val tableLayout = layout.findViewById<TableLayout>(R.id.glass_table)
|
||||||
tableLayout.removeAllViews()
|
tableLayout.removeAllViews()
|
||||||
addSymptomRows(tableLayout)
|
addSymptomRows(tableLayout)
|
||||||
|
|
||||||
// ggf. Antworten aus DB wiederherstellen
|
// Restore aus DB (nur wenn noch nicht im answers)
|
||||||
val anySymptomNeedsRestore = question.symptoms.any { !answers.containsKey(it) }
|
val anySymptomNeedsRestore = question.symptoms.any { !answers.containsKey(it) }
|
||||||
if (anySymptomNeedsRestore) {
|
if (anySymptomNeedsRestore) {
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
@ -104,21 +106,14 @@ class HandlerGlassScaleQuestion(
|
|||||||
val answerMap = allAnswersForClient.associateBy({ it.questionId }, { it.answerValue })
|
val answerMap = allAnswersForClient.associateBy({ it.questionId }, { it.answerValue })
|
||||||
|
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
val table = tableLayout
|
|
||||||
for ((index, symptomKey) in question.symptoms.withIndex()) {
|
for ((index, symptomKey) in question.symptoms.withIndex()) {
|
||||||
val answerMapKey = "$questionnaireMeta-$symptomKey"
|
val answerMapKey = "$questionnaireMeta-$symptomKey"
|
||||||
val dbAnswer = answerMap[answerMapKey]?.takeIf { it.isNotBlank() }?.trim()
|
val dbAnswer = answerMap[answerMapKey]?.takeIf { it.isNotBlank() }?.trim()
|
||||||
if (!answers.containsKey(symptomKey) && !dbAnswer.isNullOrBlank()) {
|
if (!answers.containsKey(symptomKey) && !dbAnswer.isNullOrBlank()) {
|
||||||
if (index < table.childCount) {
|
if (index < tableLayout.childCount) {
|
||||||
val row = table.getChildAt(index) as? TableRow ?: continue
|
val row = tableLayout.getChildAt(index) as? TableRow ?: continue
|
||||||
val radioGroup = row.getChildAt(1) as? RadioGroup ?: continue
|
val radioGroup = row.getChildAt(1) as? RadioGroup ?: continue
|
||||||
for (i in 0 until radioGroup.childCount) {
|
setSingleSelection(radioGroup, dbAnswer) // <-- FIXED restore
|
||||||
val rb = getRadioFromChild(radioGroup.getChildAt(i)) ?: continue
|
|
||||||
if ((rb.tag as? String)?.trim() == dbAnswer) {
|
|
||||||
rb.isChecked = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
answers[symptomKey] = dbAnswer
|
answers[symptomKey] = dbAnswer
|
||||||
points.add(pointsMap[dbAnswer] ?: 0)
|
points.add(pointsMap[dbAnswer] ?: 0)
|
||||||
}
|
}
|
||||||
@ -165,30 +160,75 @@ class HandlerGlassScaleQuestion(
|
|||||||
setPadding(0, 0, 0, 0)
|
setPadding(0, 0, 0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build buttons
|
||||||
scaleLabels.forEach { labelKey ->
|
scaleLabels.forEach { labelKey ->
|
||||||
val cell = FrameLayout(context).apply {
|
val cell = FrameLayout(context).apply {
|
||||||
layoutParams = RadioGroup.LayoutParams(0, RadioGroup.LayoutParams.WRAP_CONTENT, 1f)
|
layoutParams = RadioGroup.LayoutParams(0, RadioGroup.LayoutParams.WRAP_CONTENT, 1f)
|
||||||
}
|
}
|
||||||
|
|
||||||
val rb = RadioButton(context).apply {
|
val rb = RadioButton(context).apply {
|
||||||
tag = labelKey
|
tag = labelKey
|
||||||
id = View.generateViewId()
|
id = View.generateViewId()
|
||||||
isChecked = savedLabel == labelKey
|
isChecked = false
|
||||||
setPadding(0, 0, 0, 0)
|
setPadding(0, 0, 0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
rb.layoutParams = FrameLayout.LayoutParams(
|
rb.layoutParams = FrameLayout.LayoutParams(
|
||||||
FrameLayout.LayoutParams.WRAP_CONTENT,
|
FrameLayout.LayoutParams.WRAP_CONTENT,
|
||||||
FrameLayout.LayoutParams.WRAP_CONTENT,
|
FrameLayout.LayoutParams.WRAP_CONTENT,
|
||||||
Gravity.CENTER
|
Gravity.CENTER
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// <<< FIX: erzwinge pro Zeile genau eine Auswahl
|
||||||
|
rb.setOnCheckedChangeListener { buttonView, isChecked ->
|
||||||
|
if (suppressRowListener) return@setOnCheckedChangeListener
|
||||||
|
if (!isChecked) return@setOnCheckedChangeListener
|
||||||
|
|
||||||
|
val selectedLabel = buttonView.tag as? String ?: return@setOnCheckedChangeListener
|
||||||
|
// wenn einer checked -> alle anderen in dieser Row unchecken
|
||||||
|
suppressRowListener = true
|
||||||
|
try {
|
||||||
|
for (i in 0 until radioGroup.childCount) {
|
||||||
|
val other = getRadioFromChild(radioGroup.getChildAt(i)) ?: continue
|
||||||
|
if (other != buttonView) other.isChecked = false
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
suppressRowListener = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional (wenn du willst): sofort im answers setzen (Edit-Mode fühlt sich dann "direkt" an)
|
||||||
|
answers[symptomKey] = selectedLabel
|
||||||
|
}
|
||||||
|
|
||||||
cell.addView(rb)
|
cell.addView(rb)
|
||||||
radioGroup.addView(cell)
|
radioGroup.addView(cell)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Restore aus answers (falls vorhanden)
|
||||||
|
if (!savedLabel.isNullOrBlank()) {
|
||||||
|
setSingleSelection(radioGroup, savedLabel)
|
||||||
|
}
|
||||||
|
|
||||||
row.addView(radioGroup)
|
row.addView(radioGroup)
|
||||||
table.addView(row)
|
table.addView(row)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setzt in diesem Row-RadioGroup genau einen Wert aktiv und alle anderen aus.
|
||||||
|
*/
|
||||||
|
private fun setSingleSelection(radioGroup: RadioGroup, labelKey: String) {
|
||||||
|
suppressRowListener = true
|
||||||
|
try {
|
||||||
|
for (i in 0 until radioGroup.childCount) {
|
||||||
|
val rb = getRadioFromChild(radioGroup.getChildAt(i)) ?: continue
|
||||||
|
val tag = (rb.tag as? String)?.trim()
|
||||||
|
rb.isChecked = (tag == labelKey.trim())
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
suppressRowListener = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun validate(): Boolean {
|
override fun validate(): Boolean {
|
||||||
val table = layout.findViewById<TableLayout>(R.id.glass_table)
|
val table = layout.findViewById<TableLayout>(R.id.glass_table)
|
||||||
@ -217,6 +257,7 @@ class HandlerGlassScaleQuestion(
|
|||||||
val row = table.getChildAt(i) as TableRow
|
val row = table.getChildAt(i) as TableRow
|
||||||
val symptomKey = question.symptoms[i]
|
val symptomKey = question.symptoms[i]
|
||||||
val radioGroup = row.getChildAt(1) as RadioGroup
|
val radioGroup = row.getChildAt(1) as RadioGroup
|
||||||
|
|
||||||
for (j in 0 until radioGroup.childCount) {
|
for (j in 0 until radioGroup.childCount) {
|
||||||
val rb = getRadioFromChild(radioGroup.getChildAt(j)) ?: continue
|
val rb = getRadioFromChild(radioGroup.getChildAt(j)) ?: continue
|
||||||
if (rb.isChecked) {
|
if (rb.isChecked) {
|
||||||
|
|||||||
Reference in New Issue
Block a user