Browse Source

feat: implement proper nested scrolling in history cards using architectural redesign

- Replaced dynamic content loading with immediate population approach
- Created dedicated container-level populate methods that match ResultsBottomDrawer
- Restructured ViewHolder to recreate expanded content entirely when needed
- Fixed content lifecycle: NestedScrollView now contains content at creation time
- Added comprehensive helper methods for all UI elements (sections, rows, checkboxes)
- Enabled proper scrollable behavior within expanded Pokemon data sections

Key architectural change: Content is now populated immediately when NestedScrollView
is created, matching the working ResultsBottomDrawer pattern, instead of being
dynamically added later which prevented proper scroll recognition.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
feature/pgh-1-results-display-history
Quildra 5 months ago
parent
commit
75ae5f8e4b
  1. 829
      app/src/main/java/com/quillstudios/pokegoalshelper/ui/HistoryAdapter.kt

829
app/src/main/java/com/quillstudios/pokegoalshelper/ui/HistoryAdapter.kt

@ -178,6 +178,19 @@ class HistoryAdapter(
}
private fun createExpandedContent(context: Context): View
{
// Create placeholder that will be replaced when content is needed
return android.widget.FrameLayout(context).apply {
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
tag = "expanded_content"
visibility = View.GONE
}
}
private fun createPopulatedExpandedContent(context: Context, result: DetectionResult): View
{
return androidx.core.widget.NestedScrollView(context).apply {
// Set a reasonable fixed height for the scrollable area
@ -189,6 +202,7 @@ class HistoryAdapter(
// Configure scrolling behavior optimized for nested scrolling
isFillViewport = false
isNestedScrollingEnabled = true
tag = "expanded_content"
val contentContainer = LinearLayout(context).apply {
orientation = LinearLayout.VERTICAL
@ -200,6 +214,16 @@ class HistoryAdapter(
tag = "expanded_container"
}
// Populate content immediately - this is the key fix!
if (result.success && result.pokemonInfo != null) {
populatePokemonInfoViewsForContainer(result.pokemonInfo, contentContainer, context)
} else {
populateErrorInfoViewsForContainer(result, contentContainer, context)
}
// Technical info
populateTechnicalInfoViewsForContainer(result, contentContainer, context)
addView(contentContainer)
}
}
@ -223,12 +247,368 @@ class HistoryAdapter(
).toInt()
}
private fun populatePokemonInfoViewsForContainer(pokemonInfo: com.quillstudios.pokegoalshelper.PokemonInfo, container: LinearLayout, context: Context)
{
// Basic Pokemon Info Section
container.addView(createSectionHeaderForContainer("Pokemon Info", context))
container.addView(createTwoColumnRowForContainer(
leftLabel = "Species", leftValue = pokemonInfo.species ?: "Unknown",
rightLabel = "Dex #", rightValue = pokemonInfo.nationalDexNumber?.let { "#$it" } ?: "N/A",
context = context
))
container.addView(createTwoColumnRowForContainer(
leftLabel = "Nickname", leftValue = pokemonInfo.nickname ?: "None",
rightLabel = "Gender", rightValue = pokemonInfo.gender ?: "Unknown",
context = context
))
container.addView(createTwoColumnRowForContainer(
leftLabel = "Level", leftValue = pokemonInfo.level?.toString() ?: "N/A",
rightLabel = "Nature", rightValue = pokemonInfo.nature ?: "Unknown",
context = context
))
// Types Section
container.addView(createSectionHeaderForContainer("Types", context))
val typeDisplay = when {
pokemonInfo.primaryType != null && pokemonInfo.secondaryType != null ->
"${pokemonInfo.primaryType} / ${pokemonInfo.secondaryType}"
pokemonInfo.primaryType != null -> pokemonInfo.primaryType
else -> "Unknown"
}
container.addView(createTwoColumnRowForContainer(
leftLabel = "Type", leftValue = typeDisplay,
rightLabel = "Tera", rightValue = pokemonInfo.teraType ?: "N/A",
context = context
))
// Stats Section (if available)
pokemonInfo.stats?.let { stats ->
container.addView(createSectionHeaderForContainer("Base Stats", context))
container.addView(createThreeColumnRowForContainer(
leftLabel = "HP", leftValue = stats.hp?.toString() ?: "?",
middleLabel = "ATK", middleValue = stats.attack?.toString() ?: "?",
rightLabel = "DEF", rightValue = stats.defense?.toString() ?: "?",
context = context
))
container.addView(createThreeColumnRowForContainer(
leftLabel = "SP.ATK", leftValue = stats.spAttack?.toString() ?: "?",
middleLabel = "SP.DEF", middleValue = stats.spDefense?.toString() ?: "?",
rightLabel = "SPEED", rightValue = stats.speed?.toString() ?: "?",
context = context
))
}
// Special Properties Section
container.addView(createSectionHeaderForContainer("Properties", context))
container.addView(createCheckboxRowForContainer(
leftLabel = "Shiny", leftChecked = pokemonInfo.isShiny,
rightLabel = "Alpha", rightChecked = pokemonInfo.isAlpha,
context = context
))
container.addView(createMixedRowForContainer(
leftLabel = "Favorited", leftChecked = pokemonInfo.isFavorited,
rightLabel = "Pokeball", rightValue = pokemonInfo.pokeballType ?: "Unknown",
context = context
))
// Game Origin Section
container.addView(createSectionHeaderForContainer("Origin", context))
container.addView(createTwoColumnRowForContainer(
leftLabel = "Game", leftValue = pokemonInfo.gameSource ?: "Unknown",
rightLabel = "Language", rightValue = pokemonInfo.language ?: "Unknown",
context = context
))
pokemonInfo.originalTrainerName?.let { trainerName ->
container.addView(createTwoColumnRowForContainer(
leftLabel = "OT Name", leftValue = trainerName,
rightLabel = "OT ID", rightValue = pokemonInfo.originalTrainerId ?: "Unknown",
context = context
))
}
// Ability & Moves
pokemonInfo.ability?.let { ability ->
container.addView(createSectionHeaderForContainer("Ability & Moves", context))
container.addView(createInfoRowForContainer("Ability", ability, context))
}
if (pokemonInfo.moves.isNotEmpty()) {
if (pokemonInfo.ability == null) {
container.addView(createSectionHeaderForContainer("Moves", context))
}
container.addView(createInfoRowForContainer("Moves", pokemonInfo.moves.take(4).joinToString(", "), context))
}
// Additional Data
if (pokemonInfo.stamps.isNotEmpty() || pokemonInfo.labels.isNotEmpty() || pokemonInfo.marks.isNotEmpty()) {
container.addView(createSectionHeaderForContainer("Additional", context))
if (pokemonInfo.stamps.isNotEmpty()) {
container.addView(createInfoRowForContainer("Stamps", pokemonInfo.stamps.joinToString(", "), context))
}
if (pokemonInfo.labels.isNotEmpty()) {
container.addView(createInfoRowForContainer("Labels", pokemonInfo.labels.joinToString(", "), context))
}
if (pokemonInfo.marks.isNotEmpty()) {
container.addView(createInfoRowForContainer("Marks", pokemonInfo.marks.joinToString(", "), context))
}
}
}
private fun populateErrorInfoViewsForContainer(result: DetectionResult, container: LinearLayout, context: Context)
{
container.addView(createSectionHeaderForContainer("Error Details", context))
container.addView(createInfoRowForContainer("Status", if (result.success) "No Pokemon found" else "Detection failed", context))
result.errorMessage?.let { container.addView(createInfoRowForContainer("Error", it, context)) }
}
private fun populateTechnicalInfoViewsForContainer(result: DetectionResult, container: LinearLayout, context: Context)
{
container.addView(createSectionHeaderForContainer("Technical Info", context))
container.addView(createInfoRowForContainer("Processing Time", "${result.processingTimeMs}ms", context))
container.addView(createInfoRowForContainer("Detections Found", result.detections.size.toString(), context))
container.addView(createInfoRowForContainer("Timestamp", result.timestamp.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")), context))
}
// Helper methods for container population - using the methods from the inner class
private fun createSectionHeaderForContainer(title: String, context: Context): TextView
{
return TextView(context).apply {
text = title
setTextSize(TypedValue.COMPLEX_UNIT_SP, 14f)
setTextColor(ContextCompat.getColor(context, android.R.color.holo_blue_light))
typeface = android.graphics.Typeface.DEFAULT_BOLD
setPadding(0, dpToPx(context, 8), 0, dpToPx(context, 4))
}
}
private fun createInfoRowForContainer(label: String, value: String, context: Context): LinearLayout
{
val row = LinearLayout(context).apply {
orientation = LinearLayout.HORIZONTAL
setPadding(0, dpToPx(context, 2), 0, dpToPx(context, 2))
}
val labelView = TextView(context).apply {
text = "$label:"
setTextSize(TypedValue.COMPLEX_UNIT_SP, 12f)
setTextColor(ContextCompat.getColor(context, android.R.color.darker_gray))
layoutParams = LinearLayout.LayoutParams(
dpToPx(context, 100),
ViewGroup.LayoutParams.WRAP_CONTENT
)
}
val valueView = TextView(context).apply {
text = value
setTextSize(TypedValue.COMPLEX_UNIT_SP, 12f)
setTextColor(ContextCompat.getColor(context, android.R.color.white))
layoutParams = LinearLayout.LayoutParams(
0,
ViewGroup.LayoutParams.WRAP_CONTENT,
1f
)
}
row.addView(labelView)
row.addView(valueView)
return row
}
private fun createTwoColumnRowForContainer(leftLabel: String, leftValue: String, rightLabel: String, rightValue: String, context: Context): LinearLayout
{
val row = LinearLayout(context).apply {
orientation = LinearLayout.HORIZONTAL
setPadding(0, dpToPx(context, 2), 0, dpToPx(context, 2))
}
// Left column
val leftColumn = createColumnItemForContainer(leftLabel, leftValue, context)
leftColumn.layoutParams = LinearLayout.LayoutParams(
0,
ViewGroup.LayoutParams.WRAP_CONTENT,
1f
)
// Right column
val rightColumn = createColumnItemForContainer(rightLabel, rightValue, context)
rightColumn.layoutParams = LinearLayout.LayoutParams(
0,
ViewGroup.LayoutParams.WRAP_CONTENT,
1f
).apply {
setMargins(dpToPx(context, 8), 0, 0, 0)
}
row.addView(leftColumn)
row.addView(rightColumn)
return row
}
private fun createThreeColumnRowForContainer(leftLabel: String, leftValue: String, middleLabel: String, middleValue: String, rightLabel: String, rightValue: String, context: Context): LinearLayout
{
val row = LinearLayout(context).apply {
orientation = LinearLayout.HORIZONTAL
setPadding(0, dpToPx(context, 2), 0, dpToPx(context, 2))
}
// Left column
val leftColumn = createColumnItemForContainer(leftLabel, leftValue, context)
leftColumn.layoutParams = LinearLayout.LayoutParams(
0,
ViewGroup.LayoutParams.WRAP_CONTENT,
1f
)
// Middle column
val middleColumn = createColumnItemForContainer(middleLabel, middleValue, context)
middleColumn.layoutParams = LinearLayout.LayoutParams(
0,
ViewGroup.LayoutParams.WRAP_CONTENT,
1f
).apply {
setMargins(dpToPx(context, 4), 0, dpToPx(context, 4), 0)
}
// Right column
val rightColumn = createColumnItemForContainer(rightLabel, rightValue, context)
rightColumn.layoutParams = LinearLayout.LayoutParams(
0,
ViewGroup.LayoutParams.WRAP_CONTENT,
1f
)
row.addView(leftColumn)
row.addView(middleColumn)
row.addView(rightColumn)
return row
}
private fun createCheckboxRowForContainer(leftLabel: String, leftChecked: Boolean, rightLabel: String, rightChecked: Boolean, context: Context): LinearLayout
{
val row = LinearLayout(context).apply {
orientation = LinearLayout.HORIZONTAL
setPadding(0, dpToPx(context, 2), 0, dpToPx(context, 2))
}
// Left checkbox
val leftCheckbox = createCheckboxItemForContainer(leftLabel, leftChecked, context)
leftCheckbox.layoutParams = LinearLayout.LayoutParams(
0,
ViewGroup.LayoutParams.WRAP_CONTENT,
1f
)
// Right checkbox
val rightCheckbox = createCheckboxItemForContainer(rightLabel, rightChecked, context)
rightCheckbox.layoutParams = LinearLayout.LayoutParams(
0,
ViewGroup.LayoutParams.WRAP_CONTENT,
1f
).apply {
setMargins(dpToPx(context, 8), 0, 0, 0)
}
row.addView(leftCheckbox)
row.addView(rightCheckbox)
return row
}
private fun createMixedRowForContainer(leftLabel: String, leftChecked: Boolean, rightLabel: String, rightValue: String, context: Context): LinearLayout
{
val row = LinearLayout(context).apply {
orientation = LinearLayout.HORIZONTAL
setPadding(0, dpToPx(context, 2), 0, dpToPx(context, 2))
}
// Left checkbox
val leftCheckbox = createCheckboxItemForContainer(leftLabel, leftChecked, context)
leftCheckbox.layoutParams = LinearLayout.LayoutParams(
0,
ViewGroup.LayoutParams.WRAP_CONTENT,
1f
)
// Right text item
val rightItem = createColumnItemForContainer(rightLabel, rightValue, context)
rightItem.layoutParams = LinearLayout.LayoutParams(
0,
ViewGroup.LayoutParams.WRAP_CONTENT,
1f
).apply {
setMargins(dpToPx(context, 8), 0, 0, 0)
}
row.addView(leftCheckbox)
row.addView(rightItem)
return row
}
private fun createColumnItemForContainer(label: String, value: String, context: Context): LinearLayout
{
return LinearLayout(context).apply {
orientation = LinearLayout.VERTICAL
gravity = android.view.Gravity.START
val labelView = TextView(context).apply {
text = "$label:"
setTextSize(TypedValue.COMPLEX_UNIT_SP, 9f)
setTextColor(ContextCompat.getColor(context, android.R.color.darker_gray))
}
val valueView = TextView(context).apply {
text = value
setTextSize(TypedValue.COMPLEX_UNIT_SP, 11f)
setTextColor(ContextCompat.getColor(context, android.R.color.white))
typeface = android.graphics.Typeface.DEFAULT_BOLD
}
addView(labelView)
addView(valueView)
}
}
private fun createCheckboxItemForContainer(label: String, checked: Boolean, context: Context): LinearLayout
{
return LinearLayout(context).apply {
orientation = LinearLayout.HORIZONTAL
gravity = android.view.Gravity.CENTER_VERTICAL
// Checkbox symbol
val checkboxView = TextView(context).apply {
text = if (checked) "" else ""
setTextSize(TypedValue.COMPLEX_UNIT_SP, 14f)
setTextColor(
if (checked) ContextCompat.getColor(context, android.R.color.holo_green_light)
else ContextCompat.getColor(context, android.R.color.darker_gray)
)
layoutParams = LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
).apply {
setMargins(0, 0, dpToPx(context, 6), 0)
}
}
// Label
val labelView = TextView(context).apply {
text = label
setTextSize(TypedValue.COMPLEX_UNIT_SP, 11f)
setTextColor(ContextCompat.getColor(context, android.R.color.white))
}
addView(checkboxView)
addView(labelView)
}
}
inner class HistoryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
{
private val cardContainer = itemView as LinearLayout
private val collapsedContent = itemView.findViewWithTag<LinearLayout>("collapsed_content")
private val expandedContent = itemView.findViewWithTag<androidx.core.widget.NestedScrollView>("expanded_content")
private val expandedContainer = expandedContent.findViewWithTag<LinearLayout>("expanded_container")
private var expandedContent: View? = null // Will be created when needed
// Collapsed content views
private val statusIcon = collapsedContent.findViewWithTag<ImageView>("status_icon")
@ -244,9 +624,9 @@ class HistoryAdapter(
// Update collapsed content
updateCollapsedContent(result, context)
// Update expanded content if needed
// Handle expanded content
if (item.isExpanded) {
updateExpandedContent(result, context)
ensureExpandedContent(result, context)
showExpandedContent()
} else {
hideExpandedContent()
@ -256,11 +636,29 @@ class HistoryAdapter(
collapsedContent.setOnClickListener {
onItemClick(result, position)
}
}
private fun ensureExpandedContent(result: DetectionResult, context: Context)
{
// Remove existing expanded content if any
expandedContent?.let { existing ->
if (existing.parent == cardContainer) {
cardContainer.removeView(existing)
}
}
// Add delete button in expanded state
if (item.isExpanded) {
addDeleteButton(result, position, context)
// Create new expanded content with actual data
expandedContent = createPopulatedExpandedContent(context, result).apply {
// Add delete button to the content
val scrollView = this as androidx.core.widget.NestedScrollView
val container = scrollView.findViewWithTag<LinearLayout>("expanded_container")
container?.let {
addDeleteButton(result, adapterPosition, context, it)
}
}
// Add to card container
expandedContent?.let { cardContainer.addView(it) }
}
private fun updateCollapsedContent(result: DetectionResult, context: Context)
@ -293,399 +691,34 @@ class HistoryAdapter(
subtitleText.text = "${result.timestamp.format(formatter)}${result.processingTimeMs}ms"
// Chevron rotation based on expansion state
val rotation = if (expandedContent.visibility == View.VISIBLE) 180f else 0f
val rotation = if (expandedContent?.visibility == View.VISIBLE) 180f else 0f
chevronIcon.rotation = rotation
}
private fun updateExpandedContent(result: DetectionResult, context: Context)
{
expandedContainer.removeAllViews()
if (result.success && result.pokemonInfo != null) {
addPokemonInfoViews(result.pokemonInfo, context)
} else {
addErrorInfoViews(result, context)
}
// Technical info
addTechnicalInfoViews(result, context)
}
private fun addPokemonInfoViews(pokemonInfo: com.quillstudios.pokegoalshelper.PokemonInfo, context: Context)
{
// Basic Pokemon Info Section
addSectionHeader("Pokemon Info", context)
addTwoColumnRow(
leftLabel = "Species", leftValue = pokemonInfo.species ?: "Unknown",
rightLabel = "Dex #", rightValue = pokemonInfo.nationalDexNumber?.let { "#$it" } ?: "N/A",
context = context
)
addTwoColumnRow(
leftLabel = "Nickname", leftValue = pokemonInfo.nickname ?: "None",
rightLabel = "Gender", rightValue = pokemonInfo.gender ?: "Unknown",
context = context
)
addTwoColumnRow(
leftLabel = "Level", leftValue = pokemonInfo.level?.toString() ?: "N/A",
rightLabel = "Nature", rightValue = pokemonInfo.nature ?: "Unknown",
context = context
)
// Types Section
addSectionHeader("Types", context)
val typeDisplay = when {
pokemonInfo.primaryType != null && pokemonInfo.secondaryType != null ->
"${pokemonInfo.primaryType} / ${pokemonInfo.secondaryType}"
pokemonInfo.primaryType != null -> pokemonInfo.primaryType
else -> "Unknown"
}
addTwoColumnRow(
leftLabel = "Type", leftValue = typeDisplay,
rightLabel = "Tera", rightValue = pokemonInfo.teraType ?: "N/A",
context = context
)
// Stats Section (if available)
pokemonInfo.stats?.let { stats ->
addSectionHeader("Base Stats", context)
addThreeColumnRow(
leftLabel = "HP", leftValue = stats.hp?.toString() ?: "?",
middleLabel = "ATK", middleValue = stats.attack?.toString() ?: "?",
rightLabel = "DEF", rightValue = stats.defense?.toString() ?: "?",
context = context
)
addThreeColumnRow(
leftLabel = "SP.ATK", leftValue = stats.spAttack?.toString() ?: "?",
middleLabel = "SP.DEF", middleValue = stats.spDefense?.toString() ?: "?",
rightLabel = "SPEED", rightValue = stats.speed?.toString() ?: "?",
context = context
)
}
// Special Properties Section
addSectionHeader("Properties", context)
addCheckboxRow(
leftLabel = "Shiny", leftChecked = pokemonInfo.isShiny,
rightLabel = "Alpha", rightChecked = pokemonInfo.isAlpha,
context = context
)
addMixedRow(
leftLabel = "Favorited", leftChecked = pokemonInfo.isFavorited,
rightLabel = "Pokeball", rightValue = pokemonInfo.pokeballType ?: "Unknown",
context = context
)
// Game Origin Section
addSectionHeader("Origin", context)
addTwoColumnRow(
leftLabel = "Game", leftValue = pokemonInfo.gameSource ?: "Unknown",
rightLabel = "Language", rightValue = pokemonInfo.language ?: "Unknown",
context = context
)
pokemonInfo.originalTrainerName?.let { trainerName ->
addTwoColumnRow(
leftLabel = "OT Name", leftValue = trainerName,
rightLabel = "OT ID", rightValue = pokemonInfo.originalTrainerId ?: "Unknown",
context = context
)
}
// Ability & Moves
pokemonInfo.ability?.let { ability ->
addSectionHeader("Ability & Moves", context)
addInfoRow("Ability", ability, context)
}
if (pokemonInfo.moves.isNotEmpty()) {
if (pokemonInfo.ability == null) {
addSectionHeader("Moves", context)
}
addInfoRow("Moves", pokemonInfo.moves.take(4).joinToString(", "), context)
}
// Additional Data
if (pokemonInfo.stamps.isNotEmpty() || pokemonInfo.labels.isNotEmpty() || pokemonInfo.marks.isNotEmpty()) {
addSectionHeader("Additional", context)
if (pokemonInfo.stamps.isNotEmpty()) {
addInfoRow("Stamps", pokemonInfo.stamps.joinToString(", "), context)
}
if (pokemonInfo.labels.isNotEmpty()) {
addInfoRow("Labels", pokemonInfo.labels.joinToString(", "), context)
}
if (pokemonInfo.marks.isNotEmpty()) {
addInfoRow("Marks", pokemonInfo.marks.joinToString(", "), context)
}
}
}
private fun addErrorInfoViews(result: DetectionResult, context: Context)
{
addSectionHeader("Error Details", context)
addInfoRow("Status", if (result.success) "No Pokemon found" else "Detection failed", context)
result.errorMessage?.let { addInfoRow("Error", it, context) }
}
private fun addTechnicalInfoViews(result: DetectionResult, context: Context)
{
addSectionHeader("Technical Info", context)
addInfoRow("Processing Time", "${result.processingTimeMs}ms", context)
addInfoRow("Detections Found", result.detections.size.toString(), context)
addInfoRow("Timestamp", result.timestamp.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")), context)
}
private fun addSectionHeader(title: String, context: Context)
{
val header = TextView(context).apply {
text = title
setTextSize(TypedValue.COMPLEX_UNIT_SP, 14f)
setTextColor(ContextCompat.getColor(context, android.R.color.holo_blue_light))
typeface = android.graphics.Typeface.DEFAULT_BOLD
setPadding(0, dpToPx(context, 8), 0, dpToPx(context, 4))
}
expandedContainer.addView(header)
}
private fun addInfoRow(label: String, value: String, context: Context)
{
val row = LinearLayout(context).apply {
orientation = LinearLayout.HORIZONTAL
setPadding(0, dpToPx(context, 2), 0, dpToPx(context, 2))
}
val labelView = TextView(context).apply {
text = "$label:"
setTextSize(TypedValue.COMPLEX_UNIT_SP, 12f)
setTextColor(ContextCompat.getColor(context, android.R.color.darker_gray))
layoutParams = LinearLayout.LayoutParams(
dpToPx(context, 100),
ViewGroup.LayoutParams.WRAP_CONTENT
)
}
val valueView = TextView(context).apply {
text = value
setTextSize(TypedValue.COMPLEX_UNIT_SP, 12f)
setTextColor(ContextCompat.getColor(context, android.R.color.white))
layoutParams = LinearLayout.LayoutParams(
0,
ViewGroup.LayoutParams.WRAP_CONTENT,
1f
)
}
row.addView(labelView)
row.addView(valueView)
expandedContainer.addView(row)
}
private fun addTwoColumnRow(
leftLabel: String, leftValue: String,
rightLabel: String, rightValue: String,
context: Context
)
{
val row = LinearLayout(context).apply {
orientation = LinearLayout.HORIZONTAL
setPadding(0, dpToPx(context, 2), 0, dpToPx(context, 2))
}
// Left column
val leftColumn = createColumnItem(leftLabel, leftValue, context)
leftColumn.layoutParams = LinearLayout.LayoutParams(
0,
ViewGroup.LayoutParams.WRAP_CONTENT,
1f
)
// Right column
val rightColumn = createColumnItem(rightLabel, rightValue, context)
rightColumn.layoutParams = LinearLayout.LayoutParams(
0,
ViewGroup.LayoutParams.WRAP_CONTENT,
1f
).apply {
setMargins(dpToPx(context, 8), 0, 0, 0)
}
row.addView(leftColumn)
row.addView(rightColumn)
expandedContainer.addView(row)
}
private fun addThreeColumnRow(
leftLabel: String, leftValue: String,
middleLabel: String, middleValue: String,
rightLabel: String, rightValue: String,
context: Context
)
{
val row = LinearLayout(context).apply {
orientation = LinearLayout.HORIZONTAL
setPadding(0, dpToPx(context, 2), 0, dpToPx(context, 2))
}
// Left column
val leftColumn = createColumnItem(leftLabel, leftValue, context)
leftColumn.layoutParams = LinearLayout.LayoutParams(
0,
ViewGroup.LayoutParams.WRAP_CONTENT,
1f
)
// Middle column
val middleColumn = createColumnItem(middleLabel, middleValue, context)
middleColumn.layoutParams = LinearLayout.LayoutParams(
0,
ViewGroup.LayoutParams.WRAP_CONTENT,
1f
).apply {
setMargins(dpToPx(context, 4), 0, dpToPx(context, 4), 0)
}
// Right column
val rightColumn = createColumnItem(rightLabel, rightValue, context)
rightColumn.layoutParams = LinearLayout.LayoutParams(
0,
ViewGroup.LayoutParams.WRAP_CONTENT,
1f
)
row.addView(leftColumn)
row.addView(middleColumn)
row.addView(rightColumn)
expandedContainer.addView(row)
}
private fun createColumnItem(label: String, value: String, context: Context): LinearLayout
{
return LinearLayout(context).apply {
orientation = LinearLayout.VERTICAL
gravity = android.view.Gravity.START
val labelView = TextView(context).apply {
text = "$label:"
setTextSize(TypedValue.COMPLEX_UNIT_SP, 9f)
setTextColor(ContextCompat.getColor(context, android.R.color.darker_gray))
}
val valueView = TextView(context).apply {
text = value
setTextSize(TypedValue.COMPLEX_UNIT_SP, 11f)
setTextColor(ContextCompat.getColor(context, android.R.color.white))
typeface = android.graphics.Typeface.DEFAULT_BOLD
}
addView(labelView)
addView(valueView)
}
}
private fun addCheckboxRow(
leftLabel: String, leftChecked: Boolean,
rightLabel: String, rightChecked: Boolean,
context: Context
)
private fun showExpandedContent()
{
val row = LinearLayout(context).apply {
orientation = LinearLayout.HORIZONTAL
setPadding(0, dpToPx(context, 2), 0, dpToPx(context, 2))
}
expandedContent?.visibility = View.VISIBLE
// Left checkbox
val leftCheckbox = createCheckboxItem(leftLabel, leftChecked, context)
leftCheckbox.layoutParams = LinearLayout.LayoutParams(
0,
ViewGroup.LayoutParams.WRAP_CONTENT,
1f
)
// Right checkbox
val rightCheckbox = createCheckboxItem(rightLabel, rightChecked, context)
rightCheckbox.layoutParams = LinearLayout.LayoutParams(
0,
ViewGroup.LayoutParams.WRAP_CONTENT,
1f
).apply {
setMargins(dpToPx(context, 8), 0, 0, 0)
// Animate chevron rotation
ObjectAnimator.ofFloat(chevronIcon, "rotation", 0f, 180f).apply {
duration = 200L
start()
}
row.addView(leftCheckbox)
row.addView(rightCheckbox)
expandedContainer.addView(row)
}
private fun addMixedRow(
leftLabel: String, leftChecked: Boolean,
rightLabel: String, rightValue: String,
context: Context
)
private fun hideExpandedContent()
{
val row = LinearLayout(context).apply {
orientation = LinearLayout.HORIZONTAL
setPadding(0, dpToPx(context, 2), 0, dpToPx(context, 2))
}
expandedContent?.visibility = View.GONE
// Left checkbox
val leftCheckbox = createCheckboxItem(leftLabel, leftChecked, context)
leftCheckbox.layoutParams = LinearLayout.LayoutParams(
0,
ViewGroup.LayoutParams.WRAP_CONTENT,
1f
)
// Right text item
val rightItem = createColumnItem(rightLabel, rightValue, context)
rightItem.layoutParams = LinearLayout.LayoutParams(
0,
ViewGroup.LayoutParams.WRAP_CONTENT,
1f
).apply {
setMargins(dpToPx(context, 8), 0, 0, 0)
}
row.addView(leftCheckbox)
row.addView(rightItem)
expandedContainer.addView(row)
}
private fun createCheckboxItem(label: String, checked: Boolean, context: Context): LinearLayout
{
return LinearLayout(context).apply {
orientation = LinearLayout.HORIZONTAL
gravity = android.view.Gravity.CENTER_VERTICAL
// Checkbox symbol
val checkboxView = TextView(context).apply {
text = if (checked) "" else ""
setTextSize(TypedValue.COMPLEX_UNIT_SP, 14f)
setTextColor(
if (checked) ContextCompat.getColor(context, android.R.color.holo_green_light)
else ContextCompat.getColor(context, android.R.color.darker_gray)
)
layoutParams = LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
).apply {
setMargins(0, 0, dpToPx(context, 6), 0)
}
}
// Label
val labelView = TextView(context).apply {
text = label
setTextSize(TypedValue.COMPLEX_UNIT_SP, 11f)
setTextColor(ContextCompat.getColor(context, android.R.color.white))
}
addView(checkboxView)
addView(labelView)
// Animate chevron rotation
ObjectAnimator.ofFloat(chevronIcon, "rotation", 180f, 0f).apply {
duration = 200L
start()
}
}
private fun addDeleteButton(result: DetectionResult, position: Int, context: Context)
private fun addDeleteButton(result: DetectionResult, position: Int, context: Context, container: LinearLayout)
{
val deleteButton = Button(context).apply {
text = "Delete"
@ -708,29 +741,7 @@ class HistoryAdapter(
}
}
expandedContainer.addView(deleteButton)
}
private fun showExpandedContent()
{
expandedContent.visibility = View.VISIBLE
// Animate chevron rotation
ObjectAnimator.ofFloat(chevronIcon, "rotation", 0f, 180f).apply {
duration = 200L
start()
}
}
private fun hideExpandedContent()
{
expandedContent.visibility = View.GONE
// Animate chevron rotation
ObjectAnimator.ofFloat(chevronIcon, "rotation", 180f, 0f).apply {
duration = 200L
start()
}
container.addView(deleteButton)
}
}
}
Loading…
Cancel
Save