diff --git a/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerClientCoachCode.kt b/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerClientCoachCode.kt index b8b02c0..e4d0525 100644 --- a/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerClientCoachCode.kt +++ b/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerClientCoachCode.kt @@ -2,8 +2,6 @@ package com.dano.test1.questionnaire.handlers import android.view.View import android.widget.* -import android.util.TypedValue -import androidx.core.widget.TextViewCompat import com.dano.test1.questionnaire.GlobalValues import com.dano.test1.LanguageManager import com.dano.test1.MyApp @@ -11,6 +9,7 @@ import com.dano.test1.questionnaire.QuestionHandler import com.dano.test1.questionnaire.QuestionItem import com.dano.test1.R import com.dano.test1.network.TokenStore +import com.dano.test1.utils.ViewUtils import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -45,10 +44,10 @@ class HandlerClientCoachCode( questionTextView.text = question.question?.let { LanguageManager.getText(languageID, it) } ?: "" - setTextSizePercentOfScreenHeight(titleTextView, 0.03f) - setTextSizePercentOfScreenHeight(questionTextView, 0.03f) - setTextSizePercentOfScreenHeight(clientCodeField, 0.025f) - setTextSizePercentOfScreenHeight(coachCodeField, 0.025f) + ViewUtils.setTextSizePercentOfScreenHeight(titleTextView, 0.03f) + ViewUtils.setTextSizePercentOfScreenHeight(questionTextView, 0.03f) + ViewUtils.setTextSizePercentOfScreenHeight(clientCodeField, 0.025f) + ViewUtils.setTextSizePercentOfScreenHeight(coachCodeField, 0.025f) // Client-Code: nur verwenden, wenn bereits geladen val loadedClientCode = GlobalValues.LOADED_CLIENT_CODE @@ -64,7 +63,7 @@ class HandlerClientCoachCode( val coachFromLogin = TokenStore.getUsername(layout.context) if (!coachFromLogin.isNullOrBlank()) { coachCodeField.setText(coachFromLogin) - lockCoachField(coachCodeField) // optisch & technisch gesperrt + ViewUtils.lockEditField(coachCodeField) // optisch & technisch gesperrt } else { // Falls (theoretisch) kein Login-Username vorhanden ist, verhalten wie bisher coachCodeField.setText(answers["coach_code"] as? String ?: "") @@ -79,13 +78,6 @@ class HandlerClientCoachCode( } } - private fun setTextSizePercentOfScreenHeight(view: TextView, percentOfHeight: Float) { - val dm = layout.resources.displayMetrics - val sp = (dm.heightPixels * percentOfHeight) / dm.scaledDensity - TextViewCompat.setAutoSizeTextTypeWithDefaults(view, TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE) - view.setTextSize(TypedValue.COMPLEX_UNIT_SP, sp) - } - private fun onNextClicked(clientCodeField: EditText, coachCodeField: EditText) { val loadedClientCode = GlobalValues.LOADED_CLIENT_CODE @@ -150,19 +142,4 @@ class HandlerClientCoachCode( // Not used } - private fun lockCoachField(field: EditText) { - field.isFocusable = false - field.isFocusableInTouchMode = false - field.isCursorVisible = false - field.keyListener = null - field.isLongClickable = false - field.isClickable = false - field.setBackgroundResource(R.drawable.bg_field_locked) - field.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_lock_24, 0) - field.compoundDrawablePadding = dp(8) - field.alpha = 0.95f - } - - private fun dp(v: Int): Int = - (v * layout.resources.displayMetrics.density).toInt() } diff --git a/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerDateSpinner.kt b/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerDateSpinner.kt index 7c84380..3823a9d 100644 --- a/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerDateSpinner.kt +++ b/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerDateSpinner.kt @@ -2,15 +2,10 @@ package com.dano.test1.questionnaire.handlers import android.content.Context import android.view.View -import android.view.ViewGroup import android.widget.* import kotlinx.coroutines.* import java.text.SimpleDateFormat import java.util.* -import android.util.TypedValue -import android.view.Gravity -import androidx.core.widget.TextViewCompat -import android.widget.AbsListView import com.dano.test1.questionnaire.GlobalValues import com.dano.test1.LanguageManager import com.dano.test1.questionnaire.MAX_VALUE_YEAR @@ -20,6 +15,7 @@ import com.dano.test1.MyApp import com.dano.test1.questionnaire.QuestionHandler import com.dano.test1.questionnaire.QuestionItem import com.dano.test1.R +import com.dano.test1.utils.ViewUtils /* Zweck: @@ -62,12 +58,11 @@ class HandlerDateSpinner( textView.text = question.textKey?.let { LanguageManager.getText(languageID, it) } ?: "" // Schriftgrößen pro Bildschirmhöhe - setTextSizePercentOfScreenHeight(textView, 0.03f) // oben - setTextSizePercentOfScreenHeight(questionTextView, 0.03f) // frage - setTextSizePercentOfScreenHeight(labelDay, 0.025f) - setTextSizePercentOfScreenHeight(labelMonth, 0.025f) - setTextSizePercentOfScreenHeight(labelYear, 0.025f) - // + ViewUtils.setTextSizePercentOfScreenHeight(textView, 0.03f) + ViewUtils.setTextSizePercentOfScreenHeight(questionTextView, 0.03f) + ViewUtils.setTextSizePercentOfScreenHeight(labelDay, 0.025f) + ViewUtils.setTextSizePercentOfScreenHeight(labelMonth, 0.025f) + ViewUtils.setTextSizePercentOfScreenHeight(labelYear, 0.025f) // gespeicherte Antwort (YYYY-MM-DD) lesen val (savedYear, savedMonthIndex, savedDay) = question.question?.let { @@ -85,9 +80,9 @@ class HandlerDateSpinner( val defaultYear = savedYear ?: today.get(Calendar.YEAR) // Spinner responsiv aufsetzen (Schrift + Zeilenhöhe ohne Abschneiden) - setupSpinner(spinnerDay, days, defaultDay) - setupSpinner(spinnerMonth, months, defaultMonth) - setupSpinner(spinnerYear, years, defaultYear) + ViewUtils.setupResponsiveSpinner(context, spinnerDay, days, defaultDay) + ViewUtils.setupResponsiveSpinner(context, spinnerMonth, months, defaultMonth) + ViewUtils.setupResponsiveSpinner(context, spinnerYear, years, defaultYear) // DB-Abfrage, falls noch nicht im answers-Map val answerMapKey = question.question ?: (question.id ?: "") @@ -217,71 +212,4 @@ class HandlerDateSpinner( return sdf.parse(dateString) } - // Textgröße prozentual zur Bildschirmhöhe (in sp) - private fun setTextSizePercentOfScreenHeight(view: TextView, percentOfHeight: Float) { - val dm = (view.context ?: layout.context).resources.displayMetrics - val sp = (dm.heightPixels * percentOfHeight) / dm.scaledDensity - TextViewCompat.setAutoSizeTextTypeWithDefaults(view, TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE) - view.setTextSize(TypedValue.COMPLEX_UNIT_SP, sp) - } - - // Spinner-Adapter: Schrift & Zeilenhöhe dynamisch, kein Abschneiden - private fun setupSpinner(spinner: Spinner, items: List, defaultSelection: T?) { - val dm = context.resources.displayMetrics - - fun spFromScreenHeight(percent: Float): Float = - (dm.heightPixels * percent) / dm.scaledDensity - fun pxFromSp(sp: Float): Int = (sp * dm.scaledDensity).toInt() - - val textSp = spFromScreenHeight(0.0275f) // ~2.75% der Bildschirmhöhe - val textPx = pxFromSp(textSp) - val vPadPx = (textPx * 0.50f).toInt() // vertikales Padding - val rowHeight = (textPx * 2.20f + 2 * vPadPx).toInt() // feste Zeilenhöhe - - val adapter = object : ArrayAdapter(context, android.R.layout.simple_spinner_item, items) { - private fun styleRow(tv: TextView, forceHeight: Boolean) { - tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, textSp) - tv.includeFontPadding = true - tv.setLineSpacing(0f, 1.2f) - tv.gravity = (tv.gravity and Gravity.HORIZONTAL_GRAVITY_MASK) or Gravity.CENTER_VERTICAL - tv.setPadding(tv.paddingLeft, vPadPx, tv.paddingRight, vPadPx) - tv.minHeight = rowHeight - tv.isSingleLine = true - if (forceHeight) { - val lp = tv.layoutParams - if (lp == null || lp.height <= 0) { - tv.layoutParams = AbsListView.LayoutParams( - AbsListView.LayoutParams.MATCH_PARENT, rowHeight - ) - } else { - lp.height = rowHeight - } - } - } - - override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { - val v = super.getView(position, convertView, parent) as TextView - styleRow(v, forceHeight = false) - return v - } - - override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View { - val v = super.getDropDownView(position, convertView, parent) as TextView - styleRow(v, forceHeight = true) - return v - } - } - - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) - spinner.adapter = adapter - - spinner.setPadding(spinner.paddingLeft, vPadPx, spinner.paddingRight, vPadPx) - spinner.minimumHeight = rowHeight - spinner.requestLayout() - - defaultSelection?.let { - val index = items.indexOf(it) - if (index >= 0) spinner.setSelection(index) - } - } } diff --git a/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerGlassScaleQuestion.kt b/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerGlassScaleQuestion.kt index 4e890a5..32d9825 100644 --- a/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerGlassScaleQuestion.kt +++ b/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerGlassScaleQuestion.kt @@ -1,17 +1,16 @@ package com.dano.test1.questionnaire.handlers import android.content.Context -import android.util.TypedValue import android.view.Gravity import android.view.View import android.widget.* -import androidx.core.widget.TextViewCompat import com.dano.test1.questionnaire.GlobalValues import com.dano.test1.LanguageManager import com.dano.test1.MyApp import com.dano.test1.questionnaire.QuestionHandler import com.dano.test1.questionnaire.QuestionItem import com.dano.test1.R +import com.dano.test1.utils.ViewUtils import kotlinx.coroutines.* /* @@ -73,8 +72,8 @@ 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(questionTv, 0.03f) + ViewUtils.setTextSizePercentOfScreenHeight(titleTv, 0.03f) + ViewUtils.setTextSizePercentOfScreenHeight(questionTv, 0.03f) // Header Icons val header = layout.findViewById(R.id.glass_header) @@ -156,7 +155,7 @@ class HandlerGlassScaleQuestion( text = LanguageManager.getText(languageID, symptomKey) layoutParams = TableRow.LayoutParams(0, TableRow.LayoutParams.WRAP_CONTENT, 4f) setPadding(4, 16, 4, 16) - setTextSizePercentOfScreenHeight(this, 0.022f) + ViewUtils.setTextSizePercentOfScreenHeight(this, 0.022f) } row.addView(symptomText) @@ -283,10 +282,4 @@ class HandlerGlassScaleQuestion( else -> null } - private fun setTextSizePercentOfScreenHeight(view: TextView, percentOfHeight: Float) { - val dm = (view.context ?: layout.context).resources.displayMetrics - val sp = (dm.heightPixels * percentOfHeight) / dm.scaledDensity - TextViewCompat.setAutoSizeTextTypeWithDefaults(view, TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE) - view.setTextSize(TypedValue.COMPLEX_UNIT_SP, sp) - } } \ No newline at end of file diff --git a/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerLastPage.kt b/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerLastPage.kt index 5e3064e..a4c6e20 100644 --- a/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerLastPage.kt +++ b/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerLastPage.kt @@ -1,18 +1,18 @@ package com.dano.test1.questionnaire.handlers +import android.util.TypedValue import android.view.View import android.widget.* import android.text.Html -import kotlinx.coroutines.* -import android.util.TypedValue -import android.widget.TextView import androidx.core.widget.TextViewCompat +import kotlinx.coroutines.* import com.dano.test1.questionnaire.GlobalValues import com.dano.test1.LanguageManager import com.dano.test1.MainActivity import com.dano.test1.questionnaire.QuestionHandler import com.dano.test1.questionnaire.QuestionItem import com.dano.test1.R +import com.dano.test1.utils.ViewUtils import com.google.android.material.button.MaterialButton /* @@ -64,8 +64,8 @@ class HandlerLastPage( applyResponsiveTextSizing(finishBtn) // Überschriften responsiv skalieren (wie zuvor) - setTextSizePercentOfScreenHeight(titleTv, 0.03f) - setTextSizePercentOfScreenHeight(questionTv, 0.03f) + ViewUtils.setTextSizePercentOfScreenHeight(titleTv, 0.03f) + ViewUtils.setTextSizePercentOfScreenHeight(questionTv, 0.03f) // Buttons prevBtn.setOnClickListener { goToPreviousQuestion() } @@ -134,14 +134,6 @@ class HandlerLastPage( } // ---------------------------------------------------------------- - // Helper: Textgröße prozentual zur Bildschirmhöhe setzen (in sp) - private fun setTextSizePercentOfScreenHeight(view: TextView, percentOfHeight: Float) { - val dm = (view.context ?: layout.context).resources.displayMetrics - val sp = (dm.heightPixels * percentOfHeight) / dm.scaledDensity - TextViewCompat.setAutoSizeTextTypeWithDefaults(view, TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE) - view.setTextSize(TypedValue.COMPLEX_UNIT_SP, sp) - } - private fun sumPoints(): Int = answers.filterKeys { it.endsWith("_points") } .values.mapNotNull { it as? Int } diff --git a/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerMultiCheckboxQuestion.kt b/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerMultiCheckboxQuestion.kt index 8a97586..d4721e5 100644 --- a/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerMultiCheckboxQuestion.kt +++ b/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerMultiCheckboxQuestion.kt @@ -1,17 +1,18 @@ package com.dano.test1.questionnaire.handlers import android.content.Context +import android.util.TypedValue import android.view.View import android.widget.* -import kotlinx.coroutines.* -import android.util.TypedValue import androidx.core.widget.TextViewCompat +import kotlinx.coroutines.* import com.dano.test1.questionnaire.GlobalValues import com.dano.test1.LanguageManager import com.dano.test1.MyApp import com.dano.test1.questionnaire.QuestionHandler import com.dano.test1.questionnaire.QuestionItem import com.dano.test1.R +import com.dano.test1.utils.ViewUtils /* Zweck: @@ -44,8 +45,8 @@ class HandlerMultiCheckboxQuestion( questionTitle.text = this.question.question?.let { LanguageManager.getText(languageID, it) } ?: "" // Textgrößen pro Bildschirmhöhe (wie bei deinen anderen Handlern) - setTextSizePercentOfScreenHeight(questionTextView, 0.03f) // Überschrift - setTextSizePercentOfScreenHeight(questionTitle, 0.03f) // Frage + ViewUtils.setTextSizePercentOfScreenHeight(questionTextView, 0.03f) + ViewUtils.setTextSizePercentOfScreenHeight(questionTitle, 0.03f) container.removeAllViews() @@ -210,10 +211,4 @@ class HandlerMultiCheckboxQuestion( } } - private fun setTextSizePercentOfScreenHeight(view: TextView, percentOfHeight: Float) { - val dm = (view.context ?: layout.context).resources.displayMetrics - val sp = (dm.heightPixels * percentOfHeight) / dm.scaledDensity - TextViewCompat.setAutoSizeTextTypeWithDefaults(view, TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE) - view.setTextSize(TypedValue.COMPLEX_UNIT_SP, sp) - } } diff --git a/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerRadioQuestion.kt b/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerRadioQuestion.kt index eb74136..c6431d5 100644 --- a/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerRadioQuestion.kt +++ b/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerRadioQuestion.kt @@ -5,14 +5,13 @@ import android.view.View import android.text.Html import android.widget.* import kotlinx.coroutines.* -import android.util.TypedValue -import androidx.core.widget.TextViewCompat // <— hinzugefügt import com.dano.test1.questionnaire.GlobalValues import com.dano.test1.LanguageManager import com.dano.test1.MyApp import com.dano.test1.questionnaire.QuestionHandler import com.dano.test1.questionnaire.QuestionItem import com.dano.test1.R +import com.dano.test1.utils.ViewUtils /* Zweck: @@ -47,11 +46,8 @@ class HandlerRadioQuestion( Html.fromHtml(LanguageManager.getText(languageID, it), Html.FROM_HTML_MODE_LEGACY) } ?: "" - // - // Titel/Frage: 3% der Bildschirmhöhe - setTextSizePercentOfScreenHeight(questionTextView, 0.03f) - setTextSizePercentOfScreenHeight(questionTitle, 0.03f) - // =================================================== + ViewUtils.setTextSizePercentOfScreenHeight(questionTextView, 0.03f) + ViewUtils.setTextSizePercentOfScreenHeight(questionTitle, 0.03f) radioGroup.removeAllViews() @@ -61,7 +57,7 @@ class HandlerRadioQuestion( tag = option.key // RadioButton-Text analog zu EditTexts: 2.5% der Bildschirmhöhe - setTextSizePercentOfScreenHeight(this, 0.025f) + ViewUtils.setTextSizePercentOfScreenHeight(this, 0.025f) layoutParams = RadioGroup.LayoutParams( RadioGroup.LayoutParams.MATCH_PARENT, @@ -143,15 +139,6 @@ class HandlerRadioQuestion( } } - // setzt Textgröße prozentual zur Bildschirmhöhe (in sp) - private fun setTextSizePercentOfScreenHeight(view: TextView, percentOfHeight: Float) { - val dm = (view.context ?: layout.context).resources.displayMetrics - val sp = (dm.heightPixels * percentOfHeight) / dm.scaledDensity - TextViewCompat.setAutoSizeTextTypeWithDefaults(view, TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE) - view.setTextSize(TypedValue.COMPLEX_UNIT_SP, sp) - } - // ———————————————————————————————————————————————————————————————— - private fun restorePreviousAnswer(radioGroup: RadioGroup) { question.question?.let { questionKey -> val savedAnswer = answers[questionKey] as? String diff --git a/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerStringSpinner.kt b/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerStringSpinner.kt index 246819f..aad8f74 100644 --- a/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerStringSpinner.kt +++ b/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerStringSpinner.kt @@ -2,13 +2,8 @@ package com.dano.test1.questionnaire.handlers import android.content.Context import android.view.View -import android.view.ViewGroup import android.widget.* import kotlinx.coroutines.* -import android.util.TypedValue -import android.view.Gravity -import android.widget.TextView -import androidx.core.widget.TextViewCompat import com.dano.test1.ui.Countries import com.dano.test1.questionnaire.GlobalValues import com.dano.test1.LanguageManager @@ -16,6 +11,7 @@ import com.dano.test1.MyApp import com.dano.test1.questionnaire.QuestionHandler import com.dano.test1.questionnaire.QuestionItem import com.dano.test1.R +import com.dano.test1.utils.ViewUtils /* Zweck: @@ -50,9 +46,9 @@ class HandlerStringSpinner( questionTextView.text = question.question?.let { LanguageManager.getText(languageID, it) } ?: "" textView.text = question.textKey?.let { LanguageManager.getText(languageID, it) } ?: "" - // Textgrößen prozentual zur Bildschirmhöhe (wie im HandlerRadioQuestion) - setTextSizePercentOfScreenHeight(textView, 0.03f) - setTextSizePercentOfScreenHeight(questionTextView, 0.03f) + // Textgrößen prozentual zur Bildschirmhöhe + ViewUtils.setTextSizePercentOfScreenHeight(textView, 0.03f) + ViewUtils.setTextSizePercentOfScreenHeight(questionTextView, 0.03f) val options = buildOptionsList() @@ -60,7 +56,7 @@ class HandlerStringSpinner( val savedSelection = question.question?.let { answers[it] as? String } // Spinner aufsetzen - setupSpinner(spinner, options, savedSelection) + ViewUtils.setupResponsiveSpinner(context, spinner, options, savedSelection) // Falls noch keine Antwort im Map: aus DB laden val answerMapKey = question.question ?: (question.id ?: "") @@ -127,73 +123,4 @@ class HandlerStringSpinner( } } - // Textgröße prozentual zur Bildschirmhöhe setzen und AutoSize deaktivieren - private fun setTextSizePercentOfScreenHeight(view: TextView, percentOfHeight: Float) { - val dm = (view.context ?: layout.context).resources.displayMetrics - val sp = (dm.heightPixels * percentOfHeight) / dm.scaledDensity - TextViewCompat.setAutoSizeTextTypeWithDefaults(view, TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE) - view.setTextSize(TypedValue.COMPLEX_UNIT_SP, sp) - } - - // Spinner-Adapter mit dynamischer Schrift & stabiler Dropdown-Zeilenhöhe (kein Abschneiden) - private fun setupSpinner(spinner: Spinner, items: List, selectedItem: T?) { - val dm = context.resources.displayMetrics - - fun spFromScreenHeight(percent: Float): Float = - (dm.heightPixels * percent) / dm.scaledDensity - fun pxFromSp(sp: Float): Int = (sp * dm.scaledDensity).toInt() - - // Schrift & abgeleitete Höhen (wie beim Value-Spinner-Fix) - val textSp = spFromScreenHeight(0.0275f) // ~2.75% der Bildschirmhöhe - val textPx = pxFromSp(textSp) - val vPadPx = (textPx * 0.50f).toInt() // vertikales Padding - val rowHeight = (textPx * 2.20f + 2 * vPadPx).toInt() // feste Zeilenhöhe, verhindert Abschneiden - - val adapter = object : ArrayAdapter(context, android.R.layout.simple_spinner_item, items) { - private fun styleRow(tv: TextView, forceHeight: Boolean) { - tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, textSp) - tv.includeFontPadding = true - tv.setLineSpacing(0f, 1.2f) - tv.gravity = (tv.gravity and Gravity.HORIZONTAL_GRAVITY_MASK) or Gravity.CENTER_VERTICAL - tv.setPadding(tv.paddingLeft, vPadPx, tv.paddingRight, vPadPx) - tv.minHeight = rowHeight - tv.isSingleLine = true - if (forceHeight) { - val lp = tv.layoutParams - if (lp == null || lp.height <= 0) { - tv.layoutParams = AbsListView.LayoutParams( - AbsListView.LayoutParams.MATCH_PARENT, rowHeight - ) - } else { - lp.height = rowHeight - } - } - } - - override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { - val v = super.getView(position, convertView, parent) as TextView - styleRow(v, forceHeight = false) // ausgewählte Ansicht - return v - } - - override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View { - val v = super.getDropDownView(position, convertView, parent) as TextView - styleRow(v, forceHeight = true) // Dropdown-Zeilen: Höhe erzwingen - return v - } - } - - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) - spinner.adapter = adapter - - // Spinner selbst ausreichend hoch machen - spinner.setPadding(spinner.paddingLeft, vPadPx, spinner.paddingRight, vPadPx) - spinner.minimumHeight = rowHeight - spinner.requestLayout() - - selectedItem?.let { - val index = items.indexOf(it) - if (index >= 0) spinner.setSelection(index) - } - } } diff --git a/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerValueSpinner.kt b/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerValueSpinner.kt index 05ad7b7..98eb66e 100644 --- a/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerValueSpinner.kt +++ b/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerValueSpinner.kt @@ -2,18 +2,15 @@ package com.dano.test1.questionnaire.handlers import android.content.Context import android.view.View -import android.view.ViewGroup import android.widget.* import kotlinx.coroutines.* -import android.util.TypedValue -import android.view.Gravity -import androidx.core.widget.TextViewCompat // <- NEU import com.dano.test1.questionnaire.GlobalValues import com.dano.test1.LanguageManager import com.dano.test1.MyApp import com.dano.test1.questionnaire.QuestionHandler import com.dano.test1.questionnaire.QuestionItem import com.dano.test1.R +import com.dano.test1.utils.ViewUtils /* Zweck: @@ -50,10 +47,8 @@ class HandlerValueSpinner( questionTextView.text = question.question?.let { LanguageManager.getText(languageID, it) } ?: "" textView.text = question.textKey?.let { LanguageManager.getText(languageID, it) } ?: "" -// Schriftgrößen wie im HandlerRadioQuestion - // Titel/Frage: 3% der Bildschirmhöhe - setTextSizePercentOfScreenHeight(textView, 0.03f) - setTextSizePercentOfScreenHeight(questionTextView, 0.03f) + ViewUtils.setTextSizePercentOfScreenHeight(textView, 0.03f) + ViewUtils.setTextSizePercentOfScreenHeight(questionTextView, 0.03f) val prompt = LanguageManager.getText(languageID, "choose_answer") val spinnerItems: List = listOf(prompt) + if (question.range != null) { @@ -63,7 +58,7 @@ class HandlerValueSpinner( } val savedValue = question.question?.let { answers[it] as? String } - setupSpinner(spinner, spinnerItems, savedValue) + ViewUtils.setupResponsiveSpinner(context, spinner, spinnerItems, savedValue) //DB-Abfrage falls noch keine Antwort im Map existiert val answerMapKey = question.question ?: (question.id ?: "") @@ -134,72 +129,4 @@ class HandlerValueSpinner( } } - // setzt Textgröße prozentual zur Bildschirmhöhe (in sp) - private fun setTextSizePercentOfScreenHeight(view: TextView, percentOfHeight: Float) { - val dm = (view.context ?: layout.context).resources.displayMetrics - val sp = (dm.heightPixels * percentOfHeight) / dm.scaledDensity - TextViewCompat.setAutoSizeTextTypeWithDefaults(view, TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE) - view.setTextSize(TypedValue.COMPLEX_UNIT_SP, sp) - } - - private fun setupSpinner(spinner: Spinner, items: List, selectedItem: T?) { - val dm = context.resources.displayMetrics - - fun spFromScreenHeight(percent: Float): Float = - (dm.heightPixels * percent) / dm.scaledDensity - fun pxFromSp(sp: Float): Int = (sp * dm.scaledDensity).toInt() - - // Schrift & abgeleitete Höhen - val textSp = spFromScreenHeight(0.0275f) // ~2.75% der Bildschirmhöhe - val textPx = pxFromSp(textSp) - val vPadPx = (textPx * 0.50f).toInt() // vertikales Padding - val rowHeight = (textPx * 2.20f + 2 * vPadPx).toInt() // feste Zeilenhöhe - - val adapter = object : ArrayAdapter(context, android.R.layout.simple_spinner_item, items) { - private fun styleRow(tv: TextView, forceHeight: Boolean) { - tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, textSp) - tv.includeFontPadding = true - tv.setLineSpacing(0f, 1.2f) - tv.gravity = (tv.gravity and Gravity.HORIZONTAL_GRAVITY_MASK) or Gravity.CENTER_VERTICAL - tv.setPadding(tv.paddingLeft, vPadPx, tv.paddingRight, vPadPx) - tv.minHeight = rowHeight - tv.isSingleLine = true - if (forceHeight) { - val lp = tv.layoutParams - if (lp == null || lp.height <= 0) { - tv.layoutParams = AbsListView.LayoutParams( - AbsListView.LayoutParams.MATCH_PARENT, rowHeight - ) - } else { - lp.height = rowHeight - } - } - } - - override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { - val v = super.getView(position, convertView, parent) as TextView - styleRow(v, forceHeight = false) // ausgewählte Ansicht - return v - } - - override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View { - val v = super.getDropDownView(position, convertView, parent) as TextView - styleRow(v, forceHeight = true) // Dropdown-Zeilen: Höhe erzwingen - return v - } - } - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) - spinner.adapter = adapter - - // Spinner selbst ausreichend hoch machen - spinner.setPadding(spinner.paddingLeft, vPadPx, spinner.paddingRight, vPadPx) - spinner.minimumHeight = rowHeight - spinner.requestLayout() - - selectedItem?.let { - val index = items.indexOf(it) - if (index >= 0) spinner.setSelection(index) - } - } - } diff --git a/app/src/main/java/com/dano/test1/ui/DatabaseButtonHandler.kt b/app/src/main/java/com/dano/test1/ui/DatabaseButtonHandler.kt index f9eac4b..f4a0a74 100644 --- a/app/src/main/java/com/dano/test1/ui/DatabaseButtonHandler.kt +++ b/app/src/main/java/com/dano/test1/ui/DatabaseButtonHandler.kt @@ -6,6 +6,7 @@ import android.util.Log import android.view.View import android.widget.* import com.dano.test1.data.ExcelExportService +import com.dano.test1.utils.ViewUtils import com.dano.test1.LanguageManager import com.dano.test1.MainActivity import com.dano.test1.MyApp @@ -15,7 +16,6 @@ import com.dano.test1.data.HeaderOrderRepository import com.dano.test1.data.Question import com.dano.test1.data.Questionnaire import kotlinx.coroutines.* -import kotlin.math.roundToInt import org.json.JSONArray class DatabaseButtonHandler( @@ -420,10 +420,7 @@ class DatabaseButtonHandler( bgColor?.let { setBackgroundColor(it) } } - private fun dp(value: Int): Int { - val density = activity.resources.displayMetrics.density - return (value * density).roundToInt() - } + private fun dp(value: Int): Int = ViewUtils.dp(activity, value) private fun requireView(id: Int, name: String): T { val v = activity.findViewById(id) diff --git a/app/src/main/java/com/dano/test1/ui/HandlerOpeningScreen.kt b/app/src/main/java/com/dano/test1/ui/HandlerOpeningScreen.kt index 500be54..d6f86c3 100644 --- a/app/src/main/java/com/dano/test1/ui/HandlerOpeningScreen.kt +++ b/app/src/main/java/com/dano/test1/ui/HandlerOpeningScreen.kt @@ -27,6 +27,7 @@ import org.json.JSONObject import java.io.File import java.util.concurrent.TimeUnit import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.dano.test1.utils.ViewUtils var RHS_POINTS: Int? = null @@ -572,7 +573,7 @@ class HandlerOpeningScreen(private val activity: MainActivity) { } } - private fun dp(v: Int): Int = (v * activity.resources.displayMetrics.density).toInt() + private fun dp(v: Int): Int = ViewUtils.dp(activity, v) private fun isCompleted(button: Button): Boolean { val fileName = questionnaireFiles[button] ?: return false @@ -664,16 +665,7 @@ class HandlerOpeningScreen(private val activity: MainActivity) { } private fun lockCoachCodeField() { - coachEditText.isFocusable = false - coachEditText.isFocusableInTouchMode = false - coachEditText.isCursorVisible = false - coachEditText.keyListener = null - coachEditText.isLongClickable = false - coachEditText.isClickable = false - coachEditText.setBackgroundResource(R.drawable.bg_field_locked) - coachEditText.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_lock_24, 0) - coachEditText.compoundDrawablePadding = dp(8) - coachEditText.alpha = 0.95f + ViewUtils.lockEditField(coachEditText) } private fun applySessionAgeHighlight(ageMs: Long) { diff --git a/app/src/main/java/com/dano/test1/utils/ViewUtils.kt b/app/src/main/java/com/dano/test1/utils/ViewUtils.kt new file mode 100644 index 0000000..c551921 --- /dev/null +++ b/app/src/main/java/com/dano/test1/utils/ViewUtils.kt @@ -0,0 +1,110 @@ +package com.dano.test1.utils + +import android.content.Context +import android.util.TypedValue +import android.view.Gravity +import android.view.View +import android.view.ViewGroup +import android.widget.AbsListView +import android.widget.ArrayAdapter +import android.widget.EditText +import android.widget.Spinner +import android.widget.TextView +import androidx.core.widget.TextViewCompat +import com.dano.test1.R +import kotlin.math.roundToInt + +object ViewUtils { + + /** + * Sets the text size of a TextView to a percentage of the screen height (in sp). + * Disables auto-sizing to prevent conflicts. + */ + fun setTextSizePercentOfScreenHeight(view: TextView, percentOfHeight: Float) { + val dm = view.context.resources.displayMetrics + val sp = (dm.heightPixels * percentOfHeight) / dm.scaledDensity + TextViewCompat.setAutoSizeTextTypeWithDefaults(view, TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE) + view.setTextSize(TypedValue.COMPLEX_UNIT_SP, sp) + } + + /** + * Sets up a Spinner with a responsive, styled adapter. + * Font size and row height are derived from screen height to prevent clipping. + */ + fun setupResponsiveSpinner(context: Context, spinner: Spinner, items: List, selectedItem: T?) { + val dm = context.resources.displayMetrics + + fun spFromScreenHeight(percent: Float): Float = (dm.heightPixels * percent) / dm.scaledDensity + fun pxFromSp(sp: Float): Int = (sp * dm.scaledDensity).toInt() + + val textSp = spFromScreenHeight(0.0275f) + val textPx = pxFromSp(textSp) + val vPadPx = (textPx * 0.50f).toInt() + val rowHeight = (textPx * 2.20f + 2 * vPadPx).toInt() + + val adapter = object : ArrayAdapter(context, android.R.layout.simple_spinner_item, items) { + private fun styleRow(tv: TextView, forceHeight: Boolean) { + tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, textSp) + tv.includeFontPadding = true + tv.setLineSpacing(0f, 1.2f) + tv.gravity = (tv.gravity and Gravity.HORIZONTAL_GRAVITY_MASK) or Gravity.CENTER_VERTICAL + tv.setPadding(tv.paddingLeft, vPadPx, tv.paddingRight, vPadPx) + tv.minHeight = rowHeight + tv.isSingleLine = true + if (forceHeight) { + val lp = tv.layoutParams + if (lp == null || lp.height <= 0) { + tv.layoutParams = AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT, rowHeight) + } else { + lp.height = rowHeight + } + } + } + + override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + val v = super.getView(position, convertView, parent) as TextView + styleRow(v, forceHeight = false) + return v + } + + override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View { + val v = super.getDropDownView(position, convertView, parent) as TextView + styleRow(v, forceHeight = true) + return v + } + } + + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) + spinner.adapter = adapter + spinner.setPadding(spinner.paddingLeft, vPadPx, spinner.paddingRight, vPadPx) + spinner.minimumHeight = rowHeight + spinner.requestLayout() + + selectedItem?.let { + val index = items.indexOf(it) + if (index >= 0) spinner.setSelection(index) + } + } + + /** + * Locks an EditText field visually and functionally (e.g. for coach code fields). + */ + fun lockEditField(field: EditText, dpPadding: Int = 8) { + field.isFocusable = false + field.isFocusableInTouchMode = false + field.isCursorVisible = false + field.keyListener = null + field.isLongClickable = false + field.isClickable = false + field.setBackgroundResource(R.drawable.bg_field_locked) + field.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_lock_24, 0) + field.compoundDrawablePadding = dp(field.context, dpPadding) + field.alpha = 0.95f + } + + /** + * Converts dp to pixels using the given context. + */ + fun dp(context: Context, value: Int): Int = + (value * context.resources.displayMetrics.density).roundToInt() +}