diff --git a/.idea/misc.xml b/.idea/misc.xml index 147ee1f..6d98175 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,3 @@ - diff --git a/app/src/main/java/com/dano/test1/HandlerGlassScaleQuestion.kt b/app/src/main/java/com/dano/test1/HandlerGlassScaleQuestion.kt index c387e1c..503b757 100644 --- a/app/src/main/java/com/dano/test1/HandlerGlassScaleQuestion.kt +++ b/app/src/main/java/com/dano/test1/HandlerGlassScaleQuestion.kt @@ -9,11 +9,10 @@ import androidx.core.widget.TextViewCompat import kotlinx.coroutines.* /* - 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. -- Die Stufen werden sowohl als RadioButtons als auch über eine feste Icon-Leiste visualisiert. - +- „Glas-Skala“-Frage: pro Symptom (Zeile) genau eine von fünf Stufen. +- Stufen als RadioButtons (clickbar) + Icon-Header. +- FIX: Pro Zeile wird Single-Select erzwungen (auch im Bearbeiten-Modus / Restore). */ class HandlerGlassScaleQuestion( @@ -54,6 +53,9 @@ class HandlerGlassScaleQuestion( "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) { if (question !is QuestionItem.GlassScaleQuestion) return this.layout = layout @@ -65,15 +67,16 @@ class HandlerGlassScaleQuestion( titleTv.text = question.textKey?.let { LanguageManager.getText(languageID, it) } ?: "" questionTv.text = question.question?.let { LanguageManager.getText(languageID, it) } ?: "" - setTextSizePercentOfScreenHeight(titleTv, 0.03f) + setTextSizePercentOfScreenHeight(titleTv, 0.03f) setTextSizePercentOfScreenHeight(questionTv, 0.03f) - // feste Icon-Leiste + // Header Icons val header = layout.findViewById(R.id.glass_header) header.removeAllViews() header.addView(Space(context).apply { layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 4f) }) + val iconSizePx = (context.resources.displayMetrics.density * 36).toInt() scaleLabels.forEach { labelKey -> val cell = FrameLayout(context).apply { @@ -88,13 +91,12 @@ class HandlerGlassScaleQuestion( cell.addView(img) header.addView(cell) } - // val tableLayout = layout.findViewById(R.id.glass_table) tableLayout.removeAllViews() addSymptomRows(tableLayout) - // ggf. Antworten aus DB wiederherstellen + // Restore aus DB (nur wenn noch nicht im answers) val anySymptomNeedsRestore = question.symptoms.any { !answers.containsKey(it) } if (anySymptomNeedsRestore) { CoroutineScope(Dispatchers.IO).launch { @@ -104,21 +106,14 @@ class HandlerGlassScaleQuestion( val answerMap = allAnswersForClient.associateBy({ it.questionId }, { it.answerValue }) withContext(Dispatchers.Main) { - val table = tableLayout for ((index, symptomKey) in question.symptoms.withIndex()) { val answerMapKey = "$questionnaireMeta-$symptomKey" val dbAnswer = answerMap[answerMapKey]?.takeIf { it.isNotBlank() }?.trim() if (!answers.containsKey(symptomKey) && !dbAnswer.isNullOrBlank()) { - if (index < table.childCount) { - val row = table.getChildAt(index) as? TableRow ?: continue + if (index < tableLayout.childCount) { + val row = tableLayout.getChildAt(index) as? TableRow ?: continue val radioGroup = row.getChildAt(1) as? RadioGroup ?: continue - for (i in 0 until radioGroup.childCount) { - val rb = getRadioFromChild(radioGroup.getChildAt(i)) ?: continue - if ((rb.tag as? String)?.trim() == dbAnswer) { - rb.isChecked = true - break - } - } + setSingleSelection(radioGroup, dbAnswer) // <-- FIXED restore answers[symptomKey] = dbAnswer points.add(pointsMap[dbAnswer] ?: 0) } @@ -165,30 +160,75 @@ class HandlerGlassScaleQuestion( setPadding(0, 0, 0, 0) } + // Build buttons scaleLabels.forEach { labelKey -> val cell = FrameLayout(context).apply { layoutParams = RadioGroup.LayoutParams(0, RadioGroup.LayoutParams.WRAP_CONTENT, 1f) } + val rb = RadioButton(context).apply { tag = labelKey id = View.generateViewId() - isChecked = savedLabel == labelKey + isChecked = false setPadding(0, 0, 0, 0) } + rb.layoutParams = FrameLayout.LayoutParams( FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT, 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) radioGroup.addView(cell) } + // Restore aus answers (falls vorhanden) + if (!savedLabel.isNullOrBlank()) { + setSingleSelection(radioGroup, savedLabel) + } + row.addView(radioGroup) 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 { val table = layout.findViewById(R.id.glass_table) @@ -217,6 +257,7 @@ class HandlerGlassScaleQuestion( val row = table.getChildAt(i) as TableRow val symptomKey = question.symptoms[i] val radioGroup = row.getChildAt(1) as RadioGroup + for (j in 0 until radioGroup.childCount) { val rb = getRadioFromChild(radioGroup.getChildAt(j)) ?: continue if (rb.isChecked) { @@ -242,4 +283,4 @@ class HandlerGlassScaleQuestion( TextViewCompat.setAutoSizeTextTypeWithDefaults(view, TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE) view.setTextSize(TypedValue.COMPLEX_UNIT_SP, sp) } -} +} \ No newline at end of file